Skip to content

Instantly share code, notes, and snippets.

@steveroush
Last active October 22, 2025 19:27
Show Gist options
  • Select an option

  • Save steveroush/21f1a85ea662c6bf880db26c93c1afcd to your computer and use it in GitHub Desktop.

Select an option

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
/*****************************************************
Name: fixOrtho.gvpr
As of Date: 2024-08-25 20:42:37
Description: a gvpr program that attempts to allow ortho edges connectiongports on nodes
(currently the splines=ortho code does NOT correctlt draw edges to/from ports)
Usage: dot -Gsplines=true myFile.gv |gvpr -cf fixOrtho.gvpr | neato -n2 -Gsplines=ortho -T...
Arguments: -a S [color] << will show new node(s) and edges in green or optional color
*****************************************************/
/***************************************************************************************
how to use:
dot -Gsplines=true myFile.gv |
gvpr -cf fixOrtho.gvpr |
neato -n2 -Tpng -Gsplines=ortho >myFile.png
OR
dot -Gnodesep=.8 -Gsplines=true myFile.gv |gvpr -cf fixOrtho.gvpr | neato -Tpng -n2 -Gsplines=ortho myFile.png
options: -a S [color] << will show new node(s) and edges in green or optional color
**************************************************************************************/
BEGIN{
graph_t Root;
node_t newNode[];
int show, debug;
int HeadsTails[], changeTail[], changeHead[], ignoreEdge[];
float M, B;
float arrowSize;
string tailPt[], headPt[], nextToTail[], nextToHead[], whichPts[], posStr, showColor;
string help ;
help="
how to use:
dot -Gsplines=true myFile.gv |
gvpr -cf fixOrtho.gvpr |
neato -n2 -Tpng -Gsplines=ortho >myFile.png
OR
dot -Gnodesep=.8 -Gsplines=true myFile.gv |gvpr -cf fixOrtho.gvpr | neato -Tpng -n2 -Gsplines=ortho myFile.png
options:
-a S [color] << will show new node(s) and edges in green or optional color
";
//////////////////////////////////////////////////////////////
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;
}
//////////////////////////////////////////////////////////////
int isHorizontalOrVertical(string Pt1, string Pt2) {
int rcode;
rcode=0;
if (xOf(Pt1)==xOf(Pt2) || yOf(Pt1)==yOf(Pt2))
rcode=1;
return(rcode);
}
//////////////////////////////////////////////////////////////
// compute new pos, distance D (in points) from one X,Y point to another
string computePos(string Pt1, string Pt2, float D) {
float fullDist, frac, newX, newY;
string rslt;
print("// computePos: ",Pt1," ", Pt2, " ", D);
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;
print("// computePos: ",Pt1," ", Pt2, " ", D," rslt:", rslt);
return rslt;
}
////////////////////////////////////////////////////////////////
// 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) {
// if newPos=="" then neato/ortho will determine the pos
edge_t newE;
newE=isEdge(tailN, headN, "");
if (newE==NULL){
newE=edge(tailN, headN, "");
print("// EDGE CREATED: ", newE.name);
copyA(oldE, newE);
newE.pos=newPos;
newE.dir=newDir;
if (newLabel != ">^leave^<")
newE.label=newLabel;
}else{
print("// EDGE EXISTS: ", newE.name);
}
return newE;
}
////////////////////////////////////////////////////////////////////////
node_t createSimpleNode(string newPos) {
int ONindx;
node_t TN;
TN=node(Root, "__OrthoNode__" + (string)++ONindx);
TN.pos=newPos;
TN.shape="point";
TN.width=".02";
TN.height=".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; // 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 ",HT, " createEdgeNode: ", thisNode, " : ", otherNode, " : ", portPt, " : ", nextToPortPt, " : ",otherEdgePt, " : ", hasArrowhead, " : ", portStr);
portPt=sub(portPt,"[es],");
nextToPortPt=sub(nextToPortPt,"[es],");
print("// START ",HT, " createEdgeNode: ", thisNode, " : ", otherNode, " : ", portPt, " : ", nextToPortPt, " : ",otherEdgePt, " : ", hasArrowhead, " : ", portStr);
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=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("// createEdgeNode (before 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;
}
}
// 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=".02";
TN.height=".02";
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, cnt, firstPt, lastPt, HeadArrow, TailArrow, oneCurve;
int hasTailArrow[], hasHeadArrow[], deleteIt[];
edge_t oldE, newE, workE;
node_t tmpN;
string eString, tmpStr, tmpStr2, point[int];
string DirTail[], DirHead[];
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, help);
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";
}
///////////////////////////////////////////////////////////////////////////////////////////
E{
int needNewEdges=0, dirType;
string s, dirStr, saveHeadLabel, saveTailLabel;
unset(point);
print("//////////////////////////////////////////");
print("// EDGE : ", $.name);
if (ignoreEdge[$]==1) {
print("// ignoreEdge (continue)");
continue;
}
ignoreEdge[$]=1;
print("// EDGE : ", $.name);
print("// pos: ", $.pos);
// 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)) {
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=-1;
HeadArrow=-1;
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]);
print("// tail POINT: ", point[i]);
} else if (point[i]=="e*") {
HeadArrow=i;
hasHeadArrow[$]=1;
print("// HeadArrow: ", HeadArrow, " TailArrow: ",TailArrow);
print("// head POINT: ", point[i]);
print("// head POINT: ", point[i]);
}
} else {
if (firstPt<0)
firstPt=i;
lastPt=i;
}
}
/////////////////////////////////////////////////////////////////////////
// this seems to work nicely
if (isHorizontalOrVertical(point[firstPt], point[lastPt])) {
string TS;
$.oldPos=$.pos;
TS="";
if (TailArrow>=0)
TS=TS + " " + point[TailArrow];
if (HeadArrow>=0)
TS=TS + " " + point[HeadArrow];
TS=TS + " " + point[firstPt] + " " + point[firstPt] + " " + point[lastPt] + " " + point[lastPt];
$.pos=TS;
if (hasAttr($, "tailport") && $.tailport!="") {
$.oldTailport=$.tailport;
$.tailport="";
}
if (hasAttr($, "headport") && $.headport!="") {
$.oldHeadport=$.headport;
$.headport="";
}
print("// ", $.name, " is already horizontal or vertical, go with it");
continue;
}
/////////////////////////////////////////////////////////////////////////
// temporary - not using onecurve
if (oneCurve ) {
string pt;
print ("// One curve!!");
/****************************************************************
pick/find the one ortho point that we will use
we will create the complete pseudo-ortho edge, not just 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]);
// now create the new node at pt
// then add the two straight line segments
// do not forget any/all arrowheads
}
if (TailArrow>=0)
tailPt[$]=point[TailArrow];
else
tailPt[$]=point[firstPt];
if (HeadArrow>=0)
headPt[$]=point[HeadArrow];
else
headPt[$]=point[lastPt];
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];
}
}
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
$.splinedPos=$.pos;
if(debug==0)
$.pos="";
//////////////////////////////////////////////
if (hasAttr($, "tailport") && $.tailport!="") {
$.oldTailport=$.tailport;
$.tailport="";
}
if (hasAttr($, "headport") && $.headport!="") {
$.oldHeadport=$.headport;
$.headport="";
}
// fiddle w/ headlabel, taillabel, and label
if (hasAttr($, "taillabel")) {
saveTailLabel=$.taillabel;
$.taillabel="";
}
if (hasAttr($, "headlabel")) {
saveHeadLabel=$.headlabel;
$.headlabel="";
}
$.label="";
$.lp="";
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.oldTailport);
tmpStr="";
if (TailArrow>=0) {
tmpStr=point[TailArrow] + " ";
tmpStr2=computePos(substr(point[TailArrow],2), tmpN.pos, arrowSize);
} else {
tmpStr2=point[firstPt];
}
////// do not use firstPt/lastPt as the other end of the arrowhead/arrowtail
tmpStr=tmpStr + tmpStr2 + " " + tmpStr2 + " " + tmpN.pos + " " + tmpN.pos;
newE=addEdge(workE.tail, tmpN, workE, tmpStr, DirTail[dirStr], "", ">^leave^<", "");
if (show)
newE.color=showColor;
////// edge #2 (reduced version of workE)
newE=addEdge(tmpN, workE.head, workE, "", DirHead[dirStr], "", "", ">^leave^<");
if (saveTailLabel!="")
newE.taillabel=saveTailLabel;
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.oldHeadport);
if (HeadArrow>=0) {
tmpStr=point[HeadArrow] + " ";
tmpStr2=computePos(substr(point[HeadArrow],2), tmpN.pos, arrowSize);
} else {
tmpStr2=point[lastPt];
}
////// do not use firstPt/lastPt as the other end of the arrowhead/arrowtail
tmpStr=tmpStr + tmpStr2 + " " + tmpStr2 + " " + tmpN.pos + " " + tmpN.pos;
newE=addEdge(tmpN, workE.head, workE, tmpStr, DirHead[dirStr], "", "", ">^leave^<");
newE.taillabel="";
if (show)
newE.color=showColor;
////// edge #2 (reduced version of workE)
newE=addEdge(workE.tail, tmpN, workE, "", DirTail[dirStr], "", ">^leave^<", "");
if (saveHeadLabel!="")
newE.headlabel=saveHeadLabel;
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";
}
@steveroush
Copy link
Author

fix(?) taillabel bug

@steveroush
Copy link
Author

Code clean-up & bug fixed (prevent duplicate edges)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment