Skip to content

Instantly share code, notes, and snippets.

@steveroush
Created March 10, 2026 20:30
Show Gist options
  • Select an option

  • Save steveroush/5ff332e297bf6b98a89dbc02447925f4 to your computer and use it in GitHub Desktop.

Select an option

Save steveroush/5ff332e297bf6b98a89dbc02447925f4 to your computer and use it in GitHub Desktop.
illustrateDot - showing how dot (a Graphviz program) works

illustrateDot - showing how dot (a Graphviz program) works

This shell script and gvpr program show the phases (steps) that dot performs,

The dot document https://www.graphviz.org/documentation/TSE93.pdf) (refers to passes. The dot program refers to Phases. (e.g. dot -Gphase=1 myfile.gv will terminate after completing phase 1)

  • Phase 0 - parse and set up data structures (phase 0 is my label)
  • Phase 1 - assign a rank value to each of the nodes
  • Phase 2 - assign an order value to each of the nodes, relative only to nodes with the same rank
  • Phase 3 - position the nodes
  • Phase 4 - position the edges
BEGIN{
int i, cnt, maxR, Phase, Rank, Order,RorL, Reverse, nIndx, Virt, hasNode[];
float newX, newY, tmpXY, marg, rankCoord[], currXorY[], maxHtWid[];
graph_t newG, Root;
node_t aNode, nID[];
string RankDir, tmpStr, tok[int], RankOrder[int];
int maxInt(int int1, int int2) {
int int3;
int3=int1;
if (int2>int1)
int3=int2;
return int3;
}
void insert(int nPtr, int Rank, int Order) {
int all;
//print("// nPtr: ", nPtr, " rank: ", Rank, " Order: ", Order);
if (Order>=0) {
unset(tok);
cnt=split(RankOrder[Rank], tok, "|");
print("// cnt: ", cnt);
tok[Order]=(string)nPtr;
RankOrder[Rank]="";
all=maxInt(cnt-1,Order);
RankOrder[Rank]=tok[0];
for (i=1; i<=all; i++)
RankOrder[Rank]=RankOrder[Rank] + "|" + tok[i];
} else {
if (RankOrder[Rank]=="")
RankOrder[Rank]= (string)nPtr;
else
RankOrder[Rank]=RankOrder[Rank] + "|" + (string)nPtr;
}
print("// nPtr: ", nPtr, " rank: ", Rank, " Order: ", Order, " >", RankOrder[Rank], "<");
}
node_t addVirt() {
aNode=node(Root,"virtual"+(string)++Virt);
aNode.label="Virtual";
aNode.shape="rect";
//aNode.style="dashed";
aNode.color="red";
aNode.fontcolor="red";
//aNode.fontsize="9";
aNode.height=".5";
aNode.width=".75";
return aNode;
}
}
BEG_G{
Root=$G;
Phase=(int)$G.phase;
print("// Phase: ", Phase);
$G.phase="";
if (Phase==0)
$G.packmode="array_";
if (isAttr($G, "N", "label")) {
tmpStr=getDflt($G, "N", "label");
setDflt($G, "N", "label", tmpStr);
}
newG=graph($G.name + "_phase" + (string)Phase, "");
copyA($G, newG);
if (hasAttr($G,"rankdir") && $G.rankdir!="")
RankDir=toupper($G.rankdir);
else
RankDir="TB";
RorL=1;
switch(RankDir) {
case "TB": // high coordinates == low ranks
RorL=0;
case "RL":
Reverse=1;
break;
case "BT": // high coordinates == high ranks
RorL=0;
case "LR":
Reverse=-1; // note -1 is easy to switch to 1 & back (Reverse=-Reverse)
break;
default:
// error message goes here
break;
}
marg=40; // points
if (RankDir=="TB|BT")
print("// RankDir == TB|BT - ", RankDir);
else
print("// RankDir <> TB|BT - ", RankDir);
}
N{
fixedsize="true"; // neato sometimes changes?
nID[++nIndx]=$;
ID=nIndx;
Rank=(int)rank;
$.oldPos=pos;
if (maxR<Rank)
maxR=Rank;
hasNode[Rank]=1;
if (RankDir=="TB|BT")
tmpXY=72*(float)height;
else
tmpXY=72*(float)width;
if (maxHtWid[Rank]<tmpXY)
maxHtWid[Rank]=tmpXY;
}
BEG_G{
// reset traverse & calc rank distances & dimension
if (Reverse == -1) {
print("// NOT REVERSE");
rankCoord[0]=33;
for (i=1; i<=maxR; i++) {
if (hasNode[i]) {
rankCoord[i]=rankCoord[i+Reverse] + (maxHtWid[i+Reverse] + maxHtWid[i] + marg)/2.;
} else {
maxHtWid[i]=60;
rankCoord[i]=rankCoord[i+Reverse] + (maxHtWid[i+Reverse] + 60 + marg)/2;
}
}
} else {
print("// REVERSE");
rankCoord[maxR]=33;
for (i=maxR-1; i>=0; i--) {
if (hasNode[i]) {
rankCoord[i]=rankCoord[i+Reverse] + (maxHtWid[i+Reverse] + maxHtWid[i] + marg)/2;
} else {
maxHtWid[i]=60;
rankCoord[i]=rankCoord[i+Reverse] + (maxHtWid[i+Reverse] + 60 + marg)/2;
}
}
}
}
N{
if (hasAttr($,"order") && $.order!="")
Order=(int)order;
else
Order=-1;
Rank=(int)rank;
insert((int)ID, Rank, Order);
}
END_G{
for (Rank=0; Rank<=maxR; Rank++) {
print("// END_G Rank: ", Rank);
if (hasNode[Rank]==0) {
aNode=addVirt();
nID[++nIndx]=aNode;
RankOrder[Rank]=(string)nIndx;
}
unset(tok);
cnt=split(RankOrder[Rank], tok, "|");
print("// cnt: ", cnt);
for (Order=0; Order<cnt; Order++) {
aNode=nID[(int)tok[Order]];
if(aNode==NULL) {
print("// NULL node: ", RankOrder[Rank], " ", Rank," ",Order);
aNode=addVirt();
maxHtWid[Rank]=MAX((float)aNode.height, (float)aNode.width);
}
if (RorL) {
print("// RorL");
newX=rankCoord[Rank];
tmpXY=((float)aNode.height*36);
//newY=currXorY[Rank]+Reverse*(marg+tmpXY);
newY=currXorY[Rank]+(marg+tmpXY);
currXorY[Rank]=newY+tmpXY;
} else {
print("// TB");
newY=rankCoord[Rank];
tmpXY=((float)aNode.width*36);
//newX=currXorY[Rank]+Reverse*(marg+tmpXY);
newX=currXorY[Rank]+(marg+tmpXY);
currXorY[Rank]=newX+tmpXY;
}
//if (Phase>0)
aNode.pos=(string)newX + "," + (string)newY;
//else
//aNode.pos="";
copy(newG, aNode);
}
}
write (newG);
}
#############################################################################
#############################################################################
info='
An illustrated guide to dot - the phases
The dot document https://www.graphviz.org/documentation/TSE93.pdf) (refers to passes.
The dot program refers to Phases.
(e.g. dot -Gphase=1 myfile.gv will terminate after )
######
Phase 0 - parse and set up data structures (phase 0 is my label)
Phase 1 - assign a rank value to each of the nodes
Phase 2 - assign an order value to each of the nodes, relative only to nodes with the same rank
Phase 3 - position the nodes
Phase 4 - position the edges
'
#############################################################################
#set -x
f=$1
F=${f%.*}
T=svg
# phase 0
# let neato position the nodes
P=0
gvpr -c '
BEG_G{
graph_t aGraph;
int DEL[];
phase="";
for (aGraph = fstsubg($G); aGraph; aGraph = nxtsubg(aGraph))
DEL[aGraph]=1;
}
E{
delete($G,$);
}
END_G{
for(DEL[aGraph])
delete($G,aGraph);
}' $f |
neato -Glabel="Phase=$P\nNodes in no paticular order" -T$T -o$F.$P.$T;
# phase 1 and 2
for P in 1 2;do
if [[ "$P" == 1 ]];then
LBL="Phase=$P\nRanked, unordered"
else
LBL="Phase=$P\nRanked, ordered"
fi
dot -Gphase=$P $f |
gvpr -f illustrateDot.gvpr |
neato -n2 -T$T -o$F.$P.$T -Glabel="$LBL"
done
# phase 3
P=3
dot -Gphase=$P $f |
gvpr -c '
BEG_G{
graph_t aGraph;
int DEL[];
phase="";
for (aGraph = fstsubg($G); aGraph; aGraph = nxtsubg(aGraph))
DEL[aGraph]=1;
}
E{
delete($G,$);
}
END_G{
for(DEL[aGraph])
delete($G,aGraph);
}' |
neato -n -T$T -o$F.$P.$T -Glabel="Phase=$P\nNodes fully positioned"
# phase 4
P=4
dot $f -Glabel="Final with edges & labels" -T$T -o$F.$P.$T;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment