Skip to content

Instantly share code, notes, and snippets.

@steveroush
Last active August 18, 2024 20:30
Show Gist options
  • Save steveroush/325b594e0547e2abbc79c5106d8e8db3 to your computer and use it in GitHub Desktop.
Save steveroush/325b594e0547e2abbc79c5106d8e8db3 to your computer and use it in GitHub Desktop.
add a grid to any Graphviz graph
/**************************************************************************
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