Last active
August 18, 2024 20:30
-
-
Save steveroush/325b594e0547e2abbc79c5106d8e8db3 to your computer and use it in GitHub Desktop.
add a grid to any Graphviz graph
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/************************************************************************** | |
add a grid to a graph in .dot format | |
**************************************************************************/ | |
BEGIN{ | |
int i, Indx, bcnt, LR; | |
string Type, Val, Gcolor[], Gstyle[], Gsize[], tmpstr; | |
string Hcolor, Vcolor, Hstyle, Vstyle, gridType, gridAlign; | |
float Hsize, Vsize, deltaX, deltaY, minX, minY, maxX, maxY; | |
string help="TBD"; | |
float rankPos[]; | |
node_t LL, UR, aNode; | |
edge_t E; | |
graph_t Root; | |
help=" | |
adddGrid.gvpr - add a grid to any Graphviz graph | |
options: | |
grid=horizontal|vertical|both - set direction(s) of grid lines | |
size=rank - grid lines run through center of ranks | |
[GHV]size= - set gridlines to numeric value (123in for inches or 123pt for points), | |
fraction (4 for 1/4th of graph size) or % (10% for 10% of graph size) | |
[GHV]style= - set grid style to valid Graphviz style string | |
[GHV]color= - set grid color to rotating value from provided list | |
note: G|H|V prefix indicates set attribute for horizontal, vertical or both(G) | |
usage examples: | |
dot myFile.gv | gvpr -cf addGrid.gvpr |neato -n2 ... | |
dot myFile.gv | gvpr -a 'size=rank Gcolor=green,purple,red grid=horizontal' -cf addGrid.gvpr |neato -n2 ... | |
"; | |
//////////////////////////////////////////////////////////////////////////// | |
// do NOT call with direct output from sprintf - there is a bug - string will be empty | |
void doErrs(string eString) | |
{ | |
printf(2, "Error:: %s\n", eString); | |
} | |
//////////////////////////////////////////////////////////////////////////// | |
// float inch2pt(float i){ | |
// return 72.*i; | |
// } | |
//////////////////////////////////////////////////////////////////////////// | |
float strInch2pt(string inches) { | |
float f; | |
sscanf(inches, "%lf", &f); | |
//print("// strInch2pt: >", inches, "< ", f, " ", f*72.); | |
return f*72.; | |
} | |
//////////////////////////////////////////////////////////////////////////// | |
string next(string list) { | |
int Cnt, nxt, save[]; | |
string retS, nxtTok[int]; | |
print("// NEXT: ", save[list]); | |
Cnt=tokens(list,nxtTok,",;"); | |
nxt=save[list]; | |
if(nxt>=(Cnt)) { | |
save[list]=0; | |
nxt=0; | |
} | |
retS=nxtTok[nxt]; //save[list]]; | |
save[list]++; | |
return(retS); | |
} | |
//////////////////////////////////////////////////////////////////////////// | |
float gridSizeChk(string sizeStr, float delta) { | |
float size; | |
size=72.; | |
print("// SIZE start: ", sizeStr, " delta: ", delta); | |
if (sizeStr=="(+([0-9])pt)") { // points | |
size=(float)sub(sizeStr,"pt"); | |
} else if (sizeStr=="(+([0-9])in)") { // inches | |
size=72.*(float)sub(sizeStr,"in"); | |
} else if (sizeStr=="(+([0-9])%)") { // percent 20% means 5 intervals | |
size=delta * .01 * (float)sub(sizeStr,"%"); | |
} else if (sizeStr=="(+([0-9]))") { // if bare number, divide by that number | |
size=delta / (float)sizeStr; | |
} else if (sizeStr=="rank") { // align on rank | |
size=-1; // flag? | |
} else { | |
doErrs("bad gridsize attribute value \"" + sizeStr + "\""); | |
} | |
print("// SIZE end: ", sizeStr, " size: ", size, " delta: ", delta); | |
return size; | |
} | |
//////////////////////////////////////////////////////////////////////////// | |
void straightLine(float x1, float y1, float x2, float y2, string C, string S, string lbl) { | |
node_t n1, n2; | |
string tmpStr; | |
tmpstr="__grdNodeA__" + (string) x1 + "_" + (string) y1 +"_" + (string) x2 +"_" + (string) y2; | |
n1=node(Root,tmpstr); | |
if (lbl!="") { | |
n1.label=lbl; | |
n1.shape="plaintext"; | |
} else { | |
n1.label=""; | |
n1.shape="point"; | |
n1.style="invis"; | |
} | |
n1.pos=(string)x1 + "," + (string)y1; | |
tmpstr="__grdNodeB__" + (string) x1 + "_" + (string) y1 +"_" + (string) x2 +"_" + (string) y2; | |
n2=node(Root,tmpstr); | |
n2.label=""; | |
n2.shape="point"; | |
n2.style="invis"; | |
n2.pos=(string)x2 + "," + (string)y2; | |
E=edge(n1,n2,""); // new edge, | |
E.dir="none"; | |
E.color=C; | |
E.style=S; | |
E.penwidth=1.; | |
tmpstr=sprintf("%.2f,%.2f %.2f,%.2f %.2f,%.2f %.2f,%.2f", | |
x1, y1, x1, y1, x2, y2, x2, y2); | |
E.pos=tmpstr; | |
//print("// straight: " + tmpstr); | |
} | |
//////////////////////////////////////////////////////////////////////////// | |
void doGrid() { | |
float n; | |
int II; | |
print("// check grid1 - ", gridType," LR: ",LR," gridAlign: ", gridAlign); | |
II=0; | |
if (gridType=="both" || gridType=="vertical" || (LR==1 && gridAlign=="rank")) { | |
if (LR==1 && gridAlign=="rank") { | |
for (rankPos[n]) { | |
print("// check g1a - ", n); | |
straightLine(n,minY,n,maxY, next(Vcolor), Vstyle, (string)++II); | |
} | |
} else { | |
for (n=minX; n<=maxX; n+=Vsize) { | |
print("// check g1b - ", n," - ",Vsize); | |
straightLine(n,minY,n,maxY, next(Vcolor), Vstyle, (string)++II); | |
} | |
} | |
} | |
print("// check grid2"); | |
II=0; | |
if (gridType=="both" || gridType=="horizontal" || (LR==0 && gridAlign=="rank")) { | |
if (LR==0 && gridAlign=="rank") { | |
forr (rankPos[n]) { | |
straightLine(minX,n,maxX,n, next(Hcolor), Hstyle, (string)++II); | |
print("// n: ",n," ",II); | |
} | |
} else { | |
for (n=maxY; n>=minY; n-=Hsize) { | |
straightLine(minX,n,maxX,n, next(Hcolor), Hstyle, (string)++II); | |
print("// n: ",n," ",II); | |
} | |
} | |
} | |
print("// check g last"); | |
} | |
} | |
///////////////////////////////////////////////////////////////////////////////// | |
BEG_G{ | |
Root=$G; | |
if (hasAttr(Root, "layout")) | |
Root.layout="neato"; | |
Root.phase=""; | |
maxX=(float)xOf(urOf(Root.bb)); | |
minX=(float)xOf(llOf(Root.bb)); | |
deltaX=maxX-minX; | |
maxY=(float)yOf(urOf(Root.bb)); | |
minY=(float)yOf(llOf(Root.bb)); | |
deltaY=maxY-minY; | |
Hcolor="red"; | |
Vcolor="red"; | |
Hstyle="dashed"; | |
Vstyle="dashed"; | |
Hsize=gridSizeChk("72pt", 0.); | |
Vsize=Hsize; | |
gridType="both"; | |
Gcolor["both"]=Val; | |
Gstyle["both"]=Val; | |
Gsize["both"] =Val; | |
i=0; | |
while (i<ARGC) { | |
print("// ARG >", ARGV[i],"<"); | |
Indx=1+index(ARGV[i],"="); | |
Type=substr(ARGV[i],0,1); | |
print("// type: ", Type); | |
Val=substr(ARGV[i],Indx); | |
if (ARGV[i]=="[Gg]rid=*") { | |
gridType=Val; | |
print("// ",ARGV[i], " ",Val," << gridType"); | |
} else if (ARGV[i]=="[GHV]color=*") { | |
if (Type=="H") { | |
Hcolor=Val; | |
} else if (Type=="V") { | |
Vcolor=Val; | |
} else { | |
Hcolor=Val; | |
Vcolor=Val; | |
} | |
print("// ",ARGV[i], " << color"); | |
} else if (ARGV[i]=="[GHV]style=*") { | |
if (Type=="H") { | |
Hstyle=Val; | |
} else if (Type=="V") { | |
Vstyle=Val; | |
} else { | |
Hstyle=Val; | |
Vstyle=Val; | |
} | |
print("// ",ARGV[i], " << style"); | |
} else if (ARGV[i]=="size=rank|Gsize=rank") { | |
gridAlign="rank"; | |
} else if (ARGV[i]=="[GHV]size=*") { | |
if (Type=="H") { | |
Hsize=gridSizeChk(Val, deltaY); | |
} else if (Type=="V") { | |
Vsize=gridSizeChk(Val, deltaX); | |
} else { | |
Hsize=gridSizeChk(Val, deltaY); | |
Vsize=gridSizeChk(Val, deltaX); | |
} | |
print("// ",ARGV[i], " << size"); | |
} else { | |
printf(2, "Bad argument: \"%s\"\n", ARGV[i]); | |
printf(2, "%s\n", help); | |
exit (1); | |
} | |
i++; | |
} | |
//print("// Vsize: ",Vsize); | |
//print("// bb: ",$G.bb, " ", deltaX, " ",deltaY); | |
if (hasAttr(Root, "grid") && Root.grid!="") { | |
tmpstr=tolower(Root.grid); | |
if (tmpstr!="both" && tmpstr!="horizontal" && tmpstr!="vertical") { | |
doErrs("grid attribute value \"" + Root.grid + "\" is illegal (horizontal, vertical, or both)"); | |
exit(8); | |
} | |
gridType=tmpstr; | |
} | |
// set other grid values | |
// - interval (pts or inches) or count | |
// - horiz/vert color | |
// - horiz/vert line style | |
print("// check 1"); | |
if (hasAttr(Root, "gridHcolor") && Root.gridHcolor!="") { | |
Hcolor=Root.gridHcolor; | |
} | |
if (hasAttr(Root, "gridVcolor") && Root.gridVcolor!="") { | |
Vcolor=Root.gridVcolor; | |
} | |
if (hasAttr(Root, "gridcolor") && Root.gridcolor!="") { | |
Hcolor=Root.gridcolor; | |
Vcolor=Root.gridcolor; | |
} | |
if (hasAttr(Root, "gridHstyle") && Root.gridHstyle!="") { | |
Hstyle=Root.gridHstyle; | |
} | |
if (hasAttr(Root, "gridVstyle") && Root.gridVstyle!="") { | |
Vstyle=Root.gridVstyle; | |
} | |
if (hasAttr(Root, "gridstyle") && Root.gridstyle!="") { | |
Hstyle=Root.gridstyle; | |
Vstyle=Root.gridstyle; | |
} | |
if (hasAttr(Root, "gridHsize") && Root.gridHsize!="") { | |
Hsize=gridSizeChk(Root.gridHsize, deltaY); | |
} | |
if (hasAttr(Root, "gridVsize") && Root.gridVsize!="") { | |
Vsize=gridSizeChk(Root.gridVsize, deltaX); | |
} | |
if (hasAttr(Root, "gridsize") && Root.gridsize!="") { | |
Hsize=gridSizeChk(Root.gridsize, deltaY); | |
Vsize=gridSizeChk(Root.gridsize, deltaX); | |
} | |
//print("// check 2 -- Vsize: ",Vsize); | |
LR=0; | |
if (hasAttr(Root,"rankdir")) { | |
if (toupper(Root.rankdir)=="@(LR|RL)") { | |
LR = 1; | |
} else if (toupper(Root.rankdir)=="@(TB|BT)") { | |
LR=0; | |
} | |
} | |
//print("// check 3"); | |
} | |
N{ | |
if (hasAttr($, "pos") && $.pos!="") { | |
if (LR==1) { | |
rankPos[$.X]=1; | |
} else { | |
rankPos[$.Y]=1; | |
} | |
} | |
} | |
END_G{ | |
//print("// check 4"); | |
doGrid(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment