Last active
June 19, 2024 05:42
-
-
Save steveroush/21f1a85ea662c6bf880db26c93c1afcd to your computer and use it in GitHub Desktop.
Graphviz: fixOrtho - a work-around to allow ortho edges to connect to ports
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
neato complains about overlapping nodes, kicks out warning message (see below), | |
and uses splines=false. | |
"Warning: the bounding boxes of some nodes touch - falling back to straight | |
line edges" | |
dot bug work-around: | |
- check each edge | |
- do "pos" arrowheads match "dir"? | |
- in number (0, 1, or 2) | |
- in end (tail or head) | |
- note that "dir" is always correct while "pos" may be correct or incorrect | |
how to use: | |
dot -Gsplines=true myFile.gv | | |
gvpr -cf fixOrtho.gvpr | | |
neato -n1 -Tpng -Gsplines=ortho >myFile.png | |
OR | |
dot -Gnodesep=.8 -Gsplines=true myFile.gv |gvpr -cf fixOrtho.gvpr | neato -Tpng -n1 -Gsplines=ortho myFile.png | |
options: -a S [color] << will show new node(s) and edges in red or optional color | |
*/ | |
BEGIN{ | |
graph_t Root; | |
node_t newNode[], tmpNode, aNode; | |
int nodeNo=0, show, debug, newId=0; | |
int HeadsTails[], changeTail[], changeHead[], ignoreEdge[]; | |
float M, B; // slope | |
float arrowSize; | |
string tailPt[], headPt[], nextToTail[], nextToHead[], whichPts[], posStr, showColor; | |
////////////////////////////////////////////////////////////// | |
void doErr(string errString) { | |
print("// Error: ", errString); | |
printf(2,"Error: %s\n", errString); | |
} | |
////////////////////////////////////////////////////////////// | |
void doMsg(string errString) { | |
print("// Note: ", errString); | |
//printf(2,"Note: %s\n", errString); | |
} | |
///////////////////////////////////////////////////////////// | |
float Max(float f1, float f2) { | |
float fx; | |
if (f1>f2) | |
fx=f1; | |
else | |
fx=f2; | |
return fx; | |
} | |
///////////////////////////////////////////////////////////// | |
float Min(float f1, float f2) { | |
float fx; | |
if (f1>f2) | |
fx=f2; | |
else | |
fx=f1; | |
return fx; | |
} | |
///////////////////////////////////////////////////////////// | |
float abs(float f1) { | |
float fx; | |
if (f1<0) | |
fx=-f1; | |
else | |
fx=f1; | |
return fx; | |
} | |
///////////////////////////////////////////////////////////// | |
// greater than, with some fudge for floating point values | |
int greater(float f1, float f2) { | |
int rc; | |
if (abs(f1)-abs(f2)>.09) | |
rc=1; | |
else | |
rc=0; | |
print("// greater : ", f1, " ", f2, " ",rc); | |
return rc; | |
} | |
///////////////////////////////////////////////////////////// | |
// less than, with some fudge for floating point values | |
int less(float f1, float f2) { | |
int rc; | |
if (abs(f1)-abs(f2)<-.09) | |
rc=1; | |
else | |
rc=0; | |
print("// less : ", f1, " ", f2, " ",rc); | |
return rc; | |
} | |
///////////////////////////////////////////////////////////// | |
// equal to, with some fudge for floating point values | |
int equal(float f1, float f2) { | |
int rc; | |
if (!(less(f1, f2)) && !(greater(f1,f2))) | |
rc=1; | |
else | |
rc=0; | |
print("// equal : ", f1, " ", f2, " ",rc); | |
return rc; | |
} | |
////////////////////////////////////////////////////////////// | |
// compute distance in points - always positive value | |
float distance(float x1,float y1,float x2,float y2) { | |
float di; | |
di=sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); | |
return di; | |
} | |
////////////////////////////////////////////////////////////// | |
// compute distance in points from one X,Y point to another - always positive value | |
float distancePtPt(string Pt1, string Pt2) { | |
float dist; | |
dist=distance((float)xOf(Pt1), (float)yOf(Pt1), (float)xOf(Pt2), (float)yOf(Pt2)); | |
return dist; | |
} | |
//////////////////////////////////////////////////////////////// | |
float XdistancePtPt(string Pt1, string Pt2) { | |
float dist; | |
dist=(float)xOf(Pt1) - (float)xOf(Pt2); | |
//dist=abs(dist); | |
return dist; | |
} | |
//////////////////////////////////////////////////////////////// | |
float YdistancePtPt(string Pt1, string Pt2) { | |
float dist; | |
dist=(float)yOf(Pt1) - (float)yOf(Pt2); | |
//dist=abs(dist); | |
return dist; | |
} | |
////////////////////////////////////////////////////////////// | |
// compute new pos, distance D (in points) from one X,Y point to another | |
string computePos(float D, string Pt1, string Pt2) { | |
float fullDist, frac, newX, newY; | |
string rslt; | |
fullDist=distance((float)xOf(Pt1), (float)yOf(Pt1), (float)xOf(Pt2), (float)yOf(Pt2)); | |
if (fullDist==0) | |
frac=1; // ??? | |
else | |
frac=D/fullDist; | |
newX=(float)xOf(Pt1) + (frac* ((float)xOf(Pt2)-(float)xOf(Pt1))); | |
newY=(float)yOf(Pt1) + (frac* ((float)yOf(Pt2)-(float)yOf(Pt1))); | |
rslt=(string)newX + "," + (string)newY; | |
return rslt; | |
} | |
node_t createNode() { | |
tmpNode=node(Root, "__tmpNode_" + (string)++newId); | |
return tmpNode; | |
} | |
//////////////////////////////////////////////////////////////// | |
// chooses a point on the same side of a line as another point | |
string whichSide(string lnPt1, string lnPt2, string refPt, string optPt1, string optPt2) { | |
float whichPtX[], whichPtY[], whichDX, whichDY, tmpX, tmpY; | |
int fi; | |
string rstr; | |
// lnPt1 & lnPt2 two points on a line | |
// refPt a point on one side or the other of the above line | |
// optPt1 & optPt2 are two alternative points, one on each side | |
// return optPt1 or optPt2, whichever is on same side as whichPts[3] | |
whichPts[1]=lnPt1; | |
whichPts[2]=lnPt2; | |
whichPts[3]=refPt; | |
whichPts[4]=optPt1; | |
whichPts[5]=optPt2; | |
for (whichPts[fi]) { | |
sscanf (whichPts[fi], "%lf,%lf", &tmpX, &tmpY); | |
whichPtX[fi]=tmpX; | |
whichPtY[fi]=tmpY; | |
print("// whichPts : ", fi, " ",whichPts[fi], " ",whichPtX[fi]," --- ",whichPtY[fi]); | |
} | |
whichDX=whichPtX[1]-whichPtX[2]; | |
whichDY=whichPtY[1]-whichPtY[2]; | |
// calculat slope (M) | |
if (whichDX==0) | |
M=9999999; // fudge - no divide by 0 | |
else | |
M=whichDY/whichDX; | |
B=whichPtY[1]-(M*whichPtX[1]); | |
print("// equation M: ", M," B: ",B, " whichDX: ", whichDY); | |
if ((whichPtY[3] > ((M*whichPtX[3])+B) && whichPtY[4] > ((M*whichPtX[4])+B)) || | |
(whichPtY[3] < ((M*whichPtX[3])+B) && whichPtY[4] < ((M*whichPtX[4])+B)) || | |
(whichPtY[3] == ((M*whichPtX[3])+B) && whichPtY[4] == ((M*whichPtX[4])+B))) | |
rstr=optPt1; | |
else | |
rstr=optPt2; | |
unset (whichPtX); | |
print("// returning : ",rstr); | |
return rstr; | |
} | |
//////////////////////////////////////////////////////////////////////// | |
string pickOrtho(string lnPt1, string lnPt2, string refPt, string ret) { | |
string optPt1, optPt2; | |
optPt1=xOf(lnPt1) + "," + yOf(lnPt2); | |
optPt2=xOf(lnPt2) + "," + yOf(lnPt1); | |
ret=whichSide(lnPt1,lnPt2,refPt,optPt1,optPt2); | |
print("// pick: ", ret); | |
return ret; | |
} | |
//////////////////////////////////////////////////////////////////////// | |
edge_t addEdge(node_t tailN, node_t headN, edge_t oldE, string newPos, string newDir, | |
string newLabel, string newTailport, string newHeadport) { | |
edge_t newE; | |
newE=edge(tailN, headN, ""); | |
ignoreEdge[newE]=1; | |
copyA(oldE, newE); | |
if (newTailport != ">^leave^<") | |
newE.tailport=newTailport; | |
if (newHeadport != ">^leave^<") | |
newE.headport=newHeadport; | |
newE.pos=newPos; | |
newE.dir=newDir; | |
if (newLabel != ">^leave^<") | |
newE.label=newLabel; | |
//newE.color="purple"; // TEMPORARY !!!! temporary !! remove this | |
return newE; | |
} | |
//////////////////////////////////////////////////////////////////////// | |
node_t createSimpleNode(string newPos) { | |
int ONindx; | |
node_t TN; | |
//string | |
//newName="__OrthoNode__" + ++ONindx; | |
TN=node(Root, "__OrthoNode__" + (string)++ONindx); | |
TN.pos=newPos; | |
TN.shape="point"; | |
TN.width=".02"; | |
TN.label=""; | |
TN.style="filled"; | |
TN.color="green"; | |
print("// new node: ", TN.name," ", TN.pos); | |
return TN; | |
} | |
//////////////////////////////////////////////////////////////////////// | |
// create a new node, near the head/tail landing place | |
////////////////////////////////////////////////////////////////////////// | |
node_t createEdgeNode(edge_t thisEdge, string HT, string portPt, string nextToPortPt, | |
int hasArrowhead, string otherEdgePt, string portStr) { | |
node_t TN, thisNode, otherNode, thisTail, thisHead; // are all these used?????? | |
float portPtX, portPtY, otherEndPtX, otherEndPtY, dx, dy; | |
float arrowX1, arrowX2, arrowY1, arrowY2; | |
float _left, _right, _top, _bottom, dAX, dAY, toGoX, toGoY; | |
float _centerX, _centerY, tmpPtX, tmpPtY, marg; | |
string P, center, newName; | |
string linePt1,linePt2,referencePt,choicePt1,choicePt2; | |
/////// marg, seemingly minimum of 4, look at pmargin in neatosplines.c | |
marg=6; | |
if (HT=="T") { | |
thisNode=thisEdge.tail; | |
otherNode=thisEdge.head; | |
} else { | |
thisNode=thisEdge.head; | |
otherNode=thisEdge.tail; | |
} | |
center=thisNode.pos; | |
print("// START Createnode: ", thisNode, " : ", otherNode, " : ", portPt, " : ", nextToPortPt, " : ",otherEdgePt, " : ", portStr); | |
//////////////////////////////////////////////// | |
// | |
// should we drop this test ??? (newNode[portPt]!=NULL) | |
// | |
//////////////////////////////////////////////// | |
if (newNode[portPt]!=NULL) { | |
TN=newNode[portPt]; | |
} else { | |
arrowX1=(float)xOf(portPt); | |
arrowY1=(float)yOf(portPt); | |
arrowX2=(float)xOf(nextToPortPt); | |
arrowY2=(float)yOf(nextToPortPt); | |
if (hasArrowhead) | |
//arrowSize=distance(arrowX1, arrowY1, arrowX2, arrowY2); | |
arrowSize=distancePtPt(portPt,nextToPortPt); | |
else | |
arrowSize=0; | |
print("// arrowsize: ", portPt, " -- ", nextToPortPt, " -- ", arrowSize); | |
_centerX=(float)xOf(thisNode.pos); | |
_centerY=(float)yOf(thisNode.pos); | |
_left=_centerX-((float)thisNode.width*72./2.); | |
_right=_centerX+((float)thisNode.width*72./2.); | |
_bottom=_centerY-((float)thisNode.height*72./2.); | |
_top=_centerY+((float)thisNode.height*72./2.); | |
marg+=arrowSize; // ???????????? | |
if (arrowSize==0) | |
arrowSize=4; | |
portPtX=(float)xOf(portPt); | |
portPtY=(float)yOf(portPt); | |
otherEndPtX=(float)xOf(otherEdgePt); | |
otherEndPtY=(float)yOf(otherEdgePt); | |
if (HT=="T") { | |
dx=otherEndPtX-portPtX; | |
dy=otherEndPtY-portPtY; | |
} else { | |
dx=portPtX-otherEndPtX; | |
dy=portPtY-otherEndPtY; | |
} | |
P=sub(portStr,"*:",""); | |
print("// createnode (switch): ", portStr," ",P, " dx: ", dx," dy: ",dy); | |
linePt1=portPt; | |
referencePt=otherEdgePt; | |
switch(portStr) { | |
case "n": | |
posStr=(string)(portPtX) + "," + (string)(portPtY+marg); | |
break; | |
case "s": | |
posStr=(string)(portPtX) + "," + (string)(portPtY-marg); | |
break; | |
case "e": | |
posStr=(string)(portPtX+marg) + "," + (string)(portPtY); | |
break; | |
case "w": | |
posStr=(string)(portPtX-marg) + "," + (string)(portPtY); | |
break; | |
case "sw": | |
linePt2=(string)_right + "," + (string)_top; // opposite corner | |
choicePt1=(string)(portPtX -marg) + "," + (string)(portPtY + 0 ); | |
choicePt2=(string)(portPtX +0 ) + "," + (string)(portPtY - marg); | |
posStr=whichSide(linePt1,linePt2,referencePt,choicePt1,choicePt2); | |
break; | |
case "se": | |
linePt2=(string)_left + "," + (string)_top; // opposite corner | |
choicePt1=(string)(portPtX +marg) + "," + (string)(portPtY + 0 ); | |
choicePt2=(string)(portPtX +0 ) + "," + (string)(portPtY - marg); | |
posStr=whichSide(linePt1,linePt2,referencePt,choicePt1,choicePt2); | |
break; | |
case "nw": | |
linePt2=(string)_right + "," + (string)_bottom; // opposite corner | |
choicePt1=(string)(portPtX -marg) + "," + (string)(portPtY + 0 ); | |
choicePt2=(string)(portPtX +0 ) + "," + (string)(portPtY + marg); | |
posStr=whichSide(linePt1,linePt2,referencePt,choicePt1,choicePt2); | |
break; | |
case "ne": | |
linePt2=(string)_left + "," + (string)_bottom; // opposite corner | |
choicePt1=(string)(portPtX +marg) + "," + (string)(portPtY + 0 ); | |
choicePt2=(string)(portPtX +0 ) + "," + (string)(portPtY + marg); | |
posStr=whichSide(linePt1,linePt2,referencePt,choicePt1,choicePt2); | |
break; | |
default: { // c (center) or record/html port name | |
print("// corner compass point OR INSIDE node or RECORD port"); | |
print("// _left, _right, _top, _bottom : ", _left, " : ", _right, " : ", _top, " : ", _bottom); | |
print("// portPtX, portPtY : ", portPtX, " : ", portPtY) ; | |
tmpPtX=portPtX; | |
tmpPtY=portPtY; | |
if (P=="@(ne|nw|se|sw)" || less(_left,tmpPtX) && greater(_right,tmpPtX) && less(_bottom,tmpPtY) && greater(_top,tmpPtY)) { | |
// inside the node | |
print("// INSIDE a node OR compass corner"); | |
print("// arrowX1, arrowX2, arrowY1, arrowY2: ", arrowX1, " - ", arrowX2, " - ", arrowY1," - ", arrowY2); | |
dAX=abs(arrowX1-arrowX2); | |
dAY=abs(arrowY1-arrowY2); | |
dx=0; | |
dy=0; | |
if (dAY>dAX) { // go vertical | |
print("// vertical: ", dAX," ",dAY); | |
if (tmpPtY>_top || tmpPtY<_bottom) | |
toGoY=0; | |
else if (tmpPtY>_centerY) | |
toGoY=_top-tmpPtY; | |
else | |
toGoY=_bottom-tmpPtY; | |
dy=marg+Max(arrowSize, abs(toGoY)); | |
print("// toGoY : ",toGoY, " dy : ",dy); | |
if (tmpPtY<_centerY) | |
dy=-dy; | |
tmpPtY+=dy; | |
} else { | |
print("// horizontal: ", dAX," ",dAY); | |
if (tmpPtX>_right || tmpPtX<_left) | |
toGoX=0; | |
else if (tmpPtX>_centerX) | |
toGoX=_right-tmpPtX; | |
else | |
toGoX=_left-tmpPtX; | |
dx=marg+Max(arrowSize, abs(toGoX)); | |
print("// toGoX : ",toGoX, " dx : ",dx); | |
if (tmpPtX<_centerX) | |
dx=-dx; | |
tmpPtX+=dx; | |
} | |
} else { | |
// not compass based, but touching a side of the node | |
// (maybe two sides, but we will ignore that for now) | |
print("// Touching a Side"); | |
if (equal(_left,tmpPtX)) { | |
tmpPtX-=marg; | |
} else if (equal(_right,tmpPtX)) { | |
tmpPtX+=marg; | |
} else if (equal(_bottom,tmpPtY)) { | |
tmpPtY-=marg; | |
} else if (equal(_top,tmpPtY)) { | |
tmpPtY+=marg; | |
} | |
} | |
posStr=(string)(tmpPtX) + "," + (string)(tmpPtY); | |
break; | |
} | |
} | |
// TN=node(Root, "__portNode__" + thisNode.name + "_"+ (string)++nodeNo); | |
// new naming scheme, if node exists, we will use it | |
newName="__" + thisNode.name + "_" + portStr; | |
TN=isNode(Root, newName); | |
if (TN==NULL) { | |
TN=node(Root, newName); | |
TN.shape="point"; | |
TN.width=".00"; | |
TN.label=""; | |
TN.savePos=portPt; | |
TN.pos=posStr; | |
print("// createnode portPtX & portPtY: ",portPtX," ",portPtY," pos: ", TN.pos," marg: ",marg); | |
if (show) { | |
TN.color=showColor; | |
} else { | |
if (hasAttr(thisEdge, "color")) | |
TN.color=thisEdge.color; | |
TN.width=0; | |
TN.height=0; | |
} | |
newNode[portPt]=TN; | |
} | |
} | |
print("// returning: ", TN.name); | |
return TN; | |
} | |
//////////////////////////////////////////////////// | |
} | |
BEG_G{ | |
int i, j, cnt, firstPt, lastPt, HeadArrow, TailArrow, oneCurve, new=0; | |
int hasTailArrow[], hasHeadArrow[], deleteIt[]; | |
float extension; | |
edge_t oldE, newE, workE; | |
node_t tmpN; | |
node_t aN, tN, hN; | |
string eString, tmpS, point[int]; | |
string DirTail[], DirHead[], help="future"; | |
Root=$G; | |
if ($G.directed==1) | |
eString="->"; | |
else | |
eString="--"; | |
show=0; | |
showColor="green"; | |
debug=0; | |
i=0; | |
while (i<ARGC) { | |
if (show==1) { // kludge | |
showColor=ARGV[i]; | |
} else if (ARGV[i]=="[H?]") { // help | |
printf(2,help); | |
exit(0); | |
} else if (ARGV[i]=="[sS]") { | |
show=1; | |
} else if (ARGV[i]=="[sS]*") { | |
show=1; | |
showColor=substr(ARGV[i],1); | |
} else if (ARGV[i]=="D") { | |
debug=1; | |
show=1; | |
} else { | |
printf(2,"ERROR"); | |
} | |
print("// show: ",show," showColor: ", showColor); | |
i++; | |
} | |
DirTail[""]="none"; | |
DirHead[""]="forward"; | |
DirTail["forward"]="none"; | |
DirHead["forward"]="forward"; | |
DirTail["back"]="back"; | |
DirHead["back"]="none"; | |
DirTail["none"]="none"; | |
DirHead["none"]="none"; | |
DirTail["both"]="back"; | |
DirHead["both"]="forward"; | |
} | |
/////////////////////////////////////////////////////////////////////////////////////////// | |
// | |
// does not handle multiedges(?) (pos=spline ; spline ..." | |
// | |
E{ | |
int needNewEdges=0, dirType; | |
string s, dirStr; | |
unset(point); | |
print("//////////////////////////////////////////"); | |
print("// EDGE : ", $.name); | |
if (ignoreEdge[$]==1) { | |
print("// ignoreEdge (continue)"); | |
continue; | |
} | |
print("// pos: ", $.pos); | |
// $.color="orange"; //// TEMPORARY temporary | |
// there is a bug in dot (dot output format only), sometimes it messes up the pos value | |
// https://gitlab.com/graphviz/graphviz/-/issues/2439 | |
// it produces pos="s,... instead of pos="e,... | |
// OR | |
// it produces pos="e,... instead of pos="s,... | |
if (!hasAttr($,"dir") || $.dir=="" || $.dir=="forward") | |
dirType=1; | |
else | |
switch($.dir) { | |
case "forward": | |
dirType=1; | |
break; | |
case "back": | |
dirType=2; | |
break; | |
case "both": | |
dirType=3; | |
break; | |
case "none": | |
dirType=4; | |
break; | |
default: | |
MSG="Edge " + $.name + ", bad value for dir (" + $.dir + ")"; | |
doErr(MSG); | |
break; | |
} | |
print("// dirType: ",dirType); | |
cnt=tokens($.pos, point); | |
if (cnt<7) | |
oneCurve=1; | |
else | |
oneCurve=0; | |
/**************************************************** | |
now we try to "fix" the dot pos bug (see above) | |
do we need to check ""both"" ????????? | |
copied from alterSimpleEdge.gvpr !!!!!!!!!!!!! | |
*****************************************************/ | |
if ((point[0]=="s,*" && dirType==1) || (point[0]=="e,*" && dirType==2)) { | |
/********************* Hmmm, why do we need to skip this code?? ********************/ | |
string ts[]; | |
int ti; | |
for (ti=1; ti<cnt; ti++) { | |
ts[ti]=point[cnt-ti]; | |
} | |
for (ti=1; ti<cnt; ti++) { | |
point[ti]=ts[ti]; | |
print("// ti: ", ti," point: ", point[ti]); | |
} | |
/*******************************************************************************/ | |
print("// before: >",point[0]); | |
if (point[0]=="s,*" && dirType==1) | |
point[0]="e," + substr(point[0],2); | |
else | |
point[0]="s," + substr(point[0],2); | |
print("// after: >",point[0]); | |
print("// FIXED backwards arrowhead"); | |
} | |
if (!((hasAttr($, "tailport") && $.tailport!="") || (hasAttr($, "headport") && $.headport!=""))) { | |
print("// No ports (continue)"); | |
continue; | |
} | |
TailArrow=0; // this seems very wrong | |
HeadArrow=cnt-1; // this seems very wrong | |
firstPt=-1; | |
lastPt=-1; | |
for (i=0; i<cnt; i++) { | |
print("// point: ",i," ",point[i]); | |
s=point[i]; | |
// arrowheads? | |
if (point[i]=="[se]*") { | |
print("// arrowhead : ",point[i]); | |
s=substr(point[i],2); | |
if (point[i]=="s*") { | |
TailArrow=i; | |
hasTailArrow[$]=1; | |
print("// TailArrow: ",TailArrow," HeadArrow: ", HeadArrow); | |
print("// tail POINT: ", point[i]); | |
point[i]=s; | |
print("// tail POINT: ", point[i]); | |
} else if (point[i]=="e*") { | |
HeadArrow=i; | |
hasHeadArrow[$]=1; | |
// if no tail (but head), set to 1 | |
if (TailArrow==HeadArrow) { | |
TailArrow++; | |
} | |
print("// HeadArrow: ", HeadArrow, " TailArrow: ",TailArrow); | |
print("// head POINT: ", point[i]); | |
point[i]=s; | |
print("// head POINT: ", point[i]); | |
} | |
} else { | |
if (firstPt<0) | |
firstPt=i; | |
lastPt=i; | |
} | |
} | |
if (oneCurve) { | |
float DX, DY, totX, totY; | |
string pt; | |
print ("// One curve!!"); | |
/**************************************************************** | |
pick/find the one ortho point that we will use | |
we will create the complete pseudo-ortho edge, notjust 2 partials | |
NOTE, oneCurve is sometimes 2 half-curves (S-shape) | |
we need to handle that (I assume) | |
***************************************************************/ | |
pt=pickOrtho(point[firstPt], point[lastPt], point[firstPt+1]); | |
// createSimpleNode(pt); | |
/********************************************************** | |
for (j=firstPt; j<lastPt; j++) { | |
DX= XdistancePtPt(point[j], point[j+1]); | |
DY=YdistancePtPt(point[j], point[j+1]); | |
totX+=DX; | |
totY+=DY; | |
//print ("// onecurve - ", point[j], " : ", point[j+1], " ", XdistancePtPt(point[j], point[j+1]), " : ", YdistancePtPt(point[j], point[j+1])); | |
print ("// onecurve - ", point[j], " : ", point[j+1], " ", DX, " : ", DY); | |
} | |
print("// total: ", totX, " : ", totY); | |
**************************************************************/ | |
// now create the new node at pt | |
// then add the two straight line segments | |
// do not forget any/all arrowheads | |
} | |
tailPt[$]=point[TailArrow]; | |
headPt[$]=point[HeadArrow]; | |
if (hasTailArrow[$]==1) { | |
if (hasHeadArrow[$]==1) { // both | |
nextToTail[$]=point[HeadArrow+1]; | |
nextToHead[$]=point[cnt-1]; | |
} else { // back | |
nextToTail[$]=point[TailArrow+1]; | |
nextToHead[$]=point[HeadArrow-1]; | |
} | |
} else { | |
if (hasHeadArrow[$]==1) { // forward | |
nextToTail[$]=point[TailArrow+1]; | |
nextToHead[$]=point[cnt-1]; | |
} else { // none | |
nextToTail[$]=point[TailArrow+1]; | |
nextToHead[$]=point[cnt-2]; | |
} | |
} | |
tN=$.tail; | |
hN=$.head; | |
print("// edge: ", $.name); | |
if (hasAttr($, "tailport") && $.tailport!="") { | |
print("// BINGO tailport: ", $.tailport, ", ", point[TailArrow], " next: ", nextToTail[$]); | |
changeTail[$]=1; | |
HeadsTails[$]++; | |
needNewEdges=1; | |
} | |
if (hasAttr($, "headport") && $.headport!="") { | |
print("// BINGO headport: ", $.headport, ", ", point[HeadArrow], " next: ", nextToHead[$]); | |
changeHead[$]=1; | |
HeadsTails[$]++; | |
needNewEdges=1; | |
} | |
// remove non-ortho pos strings | |
if(debug==0) | |
$.pos=""; | |
workE=$; | |
oldE=$; | |
if (hasAttr($, "dir")) | |
dirStr=$.dir; | |
else | |
dirStr=""; | |
if (changeTail[oldE]) { | |
print("// changeTAIL - ",oldE.name); | |
tmpN=createEdgeNode(workE, "T", tailPt[oldE], nextToTail[oldE], hasTailArrow[oldE], headPt[oldE], workE.tailport); | |
tmpS="s," + tailPt[oldE] + " " + tmpN.pos + " " + tmpN.pos + " " + tmpN.pos + " " + computePos(arrowSize,tailPt[oldE], tmpN.pos); | |
/************************************************************************************************** | |
edge_t addEdge(node_t tailN, node_t headN, edge_t oldE, string newPos, string newDir, | |
string newLabel, string newTailport, string newHeadport)[ | |
**************************************************************************************************/ | |
newE=addEdge(workE.tail, tmpN, workE, tmpS, DirTail[dirStr], "", ">^leave^<", ""); | |
if (show)newE.color=showColor; | |
////// edge #2 (reduced version of workE) | |
newE=addEdge(tmpN, workE.head, workE, "", DirHead[dirStr], "", "", ">^leave^<"); | |
deleteIt[workE]=1; | |
workE=newE; // if changeHead also, use this (new) edge | |
} | |
/******** | |
if headport, we are creating a newnode->newnode loop edge, in addition to correct edges | |
*******/ | |
if (changeHead[oldE]) { | |
print("// changeHEAD - ",oldE.name); | |
tmpN=createEdgeNode(workE, "H", headPt[oldE], nextToHead[oldE], hasHeadArrow[oldE], tailPt[oldE], workE.headport); | |
newE=addEdge(tmpN, workE.head, workE, "", DirHead[dirStr], "", "", ">^leave^<"); | |
if (show)newE.color=showColor; | |
////// edge #2 (reduced version of workE) | |
newE=addEdge(workE.tail, tmpN, workE, "", DirTail[dirStr], "", ">^leave^<", ""); | |
deleteIt[workE]=1; | |
print("////////////////////// end of edge ", $.name, " ///////////////////////////////////"); | |
} | |
} | |
END_G{ | |
print("////////////////////////// END_G"); | |
if (debug==0){ | |
for (deleteIt[oldE]) { | |
delete(Root, oldE); | |
} | |
} | |
$G.splines="ortho"; | |
} |
I hope the just installed (6/10/24) (m/d/y) version is better.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks, everything works now. Looking forward to updating this fix. Will an implementation be added so that the ends of the graphs, if they arrive at the same port, will be located at the same point(if the nodes have corresponding head and tail)?