Skip to content

Instantly share code, notes, and snippets.

@steveroush
Last active June 19, 2024 05:42
Show Gist options
  • Save steveroush/21f1a85ea662c6bf880db26c93c1afcd to your computer and use it in GitHub Desktop.
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
/*
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";
}
@maris-volk
Copy link

maris-volk commented Jun 7, 2024

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

@steveroush
Copy link
Author

Does using the -o option on the neato command line fix the problem?
neato -n -Tpng myFileFixed.dot -o myfileFixed.png

@maris-volk
Copy link

maris-volk commented Jun 7, 2024

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.

@steveroush
Copy link
Author

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.

@maris-volk
Copy link

maris-volk commented Jun 7, 2024

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)?

@steveroush
Copy link
Author

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