-
-
Save steveroush/21f1a85ea662c6bf880db26c93c1afcd to your computer and use it in GitHub Desktop.
/* | |
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"; | |
} |
Lately I've been having a problem with encoding after typing
dot -Gsplines=true myFile.gv > myFile.dot
gvpr -cf fixOrtho.gvpr myFile.dot >myfileFixed.dot
As a result after entering
neato -n -Tpng myFileFixed.dot >myfileFixed.png
png does not open even if UTF-8 encoding without BOM is applied to each file in the process
(I tried to enter the above commands both in the PyCharm terminal and in the cmd, I also tried changing the encoding both through PyCharm and through Notepad++)
although I haven't encountered this before
Does using the -o option on the neato command line fix the problem?
neato -n -Tpng myFileFixed.dot -o myfileFixed.png
Now the image opens, but the graphs look broken... maybe it’s a problem with the encoding. If don’t convert the files to UTF-8 without BOM, an error appears
Error: myFile.dot: syntax error in line 1 near '��g'
This problem appeared relatively recently and did not exist before.
with conversion to UTF-8 after each step and using the command with the -o option (as per your recommendation), a warning appears
Warning: the bounding boxes of some nodes touch - falling back to straight line edges
And as mentioned above, the graphs look "broken" in the picture but myFileFixed.dot itself looks not bad in Graphviz Online.
Try using the -o option on the dot and gvpr commands also.
dot -Gsplines=true myFile.gv -o myFile.dot
gvpr -cf fixOrtho.gvpr myFile.dot -o myfileFixed.dot
neato -n -Tpng myFileFixed.dot -o myfileFixed.png
p.s. the fixOrtho.gvpr program was not sufficiently tested & has known bugs. I have a partial improvement and will post it, in the next few days, but it will still not be a great solution.
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)?
I hope the just installed (6/10/24) (m/d/y) version is better.
The ortho edge implementation does not handle ports (see https://forum.graphviz.org/t/regarding-graphvizs-orthogonal-edge-routing/1889/2, https://gitlab.com/graphviz/graphviz/-/issues/352, or https://gitlab.com/graphviz/graphviz/-/issues/1415)
Here is a link (https://gist.github.com/steveroush/21f1a85ea662c6bf880db26c93c1afcd) to a gvpr program that will often allow ortho edges to connect to ports.
It is easy, but not trivial to use:
dot -Gsplines=true
myFile.gv > myFile.dot` (This will position all the nodes and ports)gvpr -cf fixOrtho.gvpr myFile.dot >myfileFixed.dot
(This adds nodes and edges and alters existing edges)neato -n -Tpng myFileFixed.dot >myfileFixed.png
(Creates viewable image)Note: the commands above can also be run as a single pipeline.
This is modestly tested software (surprisingly complex). Please try it and comment here. If/when it settles down, it can become part of the Graphviz package it that is desired, though I consider this a temporary "fix".
Here are three versions of the ports.gv file that is included in the Graphviz source. (The red edge components are just to show the changes)
![ports montage](https://private-user-images.githubusercontent.com/65417364/291722687-95584319-5dce-42ef-a8cd-555fc1234b2a.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjIxMDgzNjAsIm5iZiI6MTcyMjEwODA2MCwicGF0aCI6Ii82NTQxNzM2NC8yOTE3MjI2ODctOTU1ODQzMTktNWRjZS00MmVmLWE4Y2QtNTU1ZmMxMjM0YjJhLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA3MjclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNzI3VDE5MjEwMFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTVjNTdhYzU2YWUxYmU5ZDM2M2JjMzc1NTU3MTdmYTZkODFiNDc1ZGFiZGVkMmZhNDlkMDI1M2U4NTQwZTI3NGYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.ue5wJcZUCOCwk9zkCtZLi7bA_m_NR8YR7F36QZmNmGo)