Skip to content

Instantly share code, notes, and snippets.

@maris-volk
Forked from steveroush/fixOrtho.gvpr
Created June 19, 2024 05:42
Show Gist options
  • Save maris-volk/d9633dc857157ce4b044159cdb5109e3 to your computer and use it in GitHub Desktop.
Save maris-volk/d9633dc857157ce4b044159cdb5109e3 to your computer and use it in GitHub Desktop.
Graphviz: fixOrtho - a work-around to allow ortho edges to connect to ports
/*
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";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment