Last active
July 19, 2023 19:59
-
-
Save steveroush/aca4cb434042bb67c0eba394c3456d24 to your computer and use it in GitHub Desktop.
A Graphviz gvpr program to add concentric circles to a twopi 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
/************************************************************************* | |
twopiCircles: add circles to twopi graph | |
similar to the "addrings" gvpr program that comes with Graphviz source | |
New Attributes, only used by twopiCircles.gvpr: | |
- maxRank - set a maximum number of concentric circles (ranks). | |
Ranks counted from the center, Root is not counted as a rank | |
- noOffset - if true, draw circles through the center of nodes | |
otherwise, draw circles between the ranks | |
(i.e half-way from one rank to the next) | |
- ringColors - allows the rings/circles to be colorized | |
format: 'color1:color2:...' | |
example: ringColors="/greys9/2:/greys9/4:green:/X11/beige" | |
(see https://graphviz.org/docs/attrs/colorscheme/) | |
Command Line Options (to gvpr): | |
to identify the root node | |
-a"rootname" | |
or | |
-a"root = rootname" (grepped from twopi -v output to stderr) | |
Optional (preferred) method of identifying the root node is `graph [root=mynode]`, within input file | |
USAGE: | |
twopi myfile.gv | gvpr -cf twopiCircles.gvpr >myFileNowWithConcentrigRings.gv | |
then: | |
neato -n -Tpng myFileNowWithConcentrigRings.gv >myFileNowWithConcentrigRings.png | |
*********************************************************************/ | |
BEGIN{ | |
node_t center; | |
float x[], y[]; | |
int i, j, l, max, rsCnt, maxRankSepPt, maxRSPindx, doOffset, maxRanks; | |
string rankSep[int]; | |
int RS, rankSepPt[int]; | |
string POS, st, tok[int]; | |
graph_t Root, tempG, tempGx, circleG ; | |
void dumpG(graph_t aGraph, string txt) { | |
print("// DUMP ", txt); | |
write(aGraph); | |
print("//////////////////////"); | |
} | |
// bug fixed 6/30/23 | |
int istrue(string checkme) { | |
int rc; | |
if (tolower(checkme) == "@(1|yes|true)") // ksh syntax | |
rc=1; | |
else | |
rc=0; | |
return(rc); | |
} | |
int idistance(float x1,float y1,float x2,float y2) { | |
int d; | |
d=(int)sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); | |
return d; // points | |
} | |
} | |
BEG_G{ | |
string centerName; | |
Root=$G; | |
// fix 6/17/23 | |
if (hasAttr($G, "layout") && $G.layout!="") { | |
$G.layout="neato"; | |
} | |
// fix wierd graph label bug?? | |
if (hasAttr($G, "lp") && $G.lp!="") | |
$G.lp=""; | |
// maxRank added 6/17/23 | |
maxRanks=99999; // cheesy | |
if (hasAttr($G, "maxRank") && $G.maxRank!="") { | |
maxRanks=$G.maxRank; | |
} | |
// noOffset option added 6/17/23 | |
doOffset=1; | |
if (hasAttr($G, "noOffset") && $G.noOffset!="" && istrue($G.noOffset)) { | |
doOffset=0; | |
} | |
max=0; | |
// use aget because "root" is a reserved word | |
centerName=aget($G,"root"); | |
if (centerName=="") { | |
switch (ARGC) { | |
case 1: | |
case 3: | |
centerName=ARGV[ARGC-1]; | |
break; | |
default: | |
printf(2, "Error: missing \"root\" value on command line, exiting\n"); | |
exit (1); | |
break; | |
} | |
} | |
// yikes, this code got duped, try to clean up | |
//print("// CENTER: ", centerName); | |
center = node($, centerName); // 1st or 3rd argument | |
if (center==NULL) { | |
printf(2, "Error: unknown node (%s), exiting\n", centerName); | |
exit(9); | |
} | |
if (hasAttr(center, "pos") && center.pos!="") { | |
// all is ok | |
}else{ | |
printf(2, "Error: \"root\" (%s) node has no \"pos\" value, exiting\n", center.name); | |
exit (1); | |
} | |
rankSep[0]=1; // 1 inch | |
if (hasAttr($G, "ranksep") && $G.ranksep!="") { | |
rsCnt=tokens($.ranksep, rankSep, ":"); | |
} | |
// convert array to points (int) | |
for (rankSep[i]) { | |
if (rankSep[i] == 0.) | |
rankSepPt[i]=72; | |
else | |
rankSepPt[i]=(int)(72*rankSep[i]); | |
maxRankSepPt=rankSepPt[i]; | |
maxRSPindx=i; | |
} | |
//dumpG(Root,"Root first"); | |
} | |
N{ | |
if ($==center) { | |
x[0]=$.X; | |
y[0]=$.Y; | |
//print("// center/root: ", $.name, " ", $.pos); | |
} else { | |
++max; | |
x[max]=$.X; | |
y[max]=$.Y; | |
} | |
} | |
//END_G{ | |
// use beg_g to help place rings at the beginning of the output graph | |
BEG_G{ | |
int circ=0, dist[], iD, maxD, done, totalD[]; | |
float D; | |
node_t newN; | |
string colorScheme="", ringColor[int], gtype; | |
int ringC=0, colorCnt; | |
graph_t aGraph, first; | |
// create subgraph for circles, needed if we colorize | |
first=fstsubg(Root); | |
if (first==NULL) | |
first=Root; | |
circleG=subg(first, "__COLORED_CIRCLES__"); | |
string Nprefix="____Circle____"; | |
for (i=1; i<=max; i++) { | |
iD=idistance(x[0], y[0], x[i], y[i]); | |
dist[iD]++; | |
} | |
// find max distance in points | |
for (dist[iD]) { | |
if (iD>maxD) | |
maxD=iD; | |
} | |
if (doOffset) { | |
//print("// offset adjust "); | |
forr (rankSepPt[i]) { // note forr | |
rankSepPt[i+1]=rankSepPt[i]; | |
} | |
rankSepPt[0]=rankSepPt[1]/2; | |
maxRSPindx++; | |
} | |
// add to or remove from rankSepPt, if necessary | |
totalD[0]=0; | |
done=0; | |
totalD[-1]=0; // yuck | |
for (i=0; i<=maxRSPindx; i++) { // skip 1st? | |
//print("// i: ", i, " rankSepPt: ",rankSepPt[i], " max: ", maxRSPindx, " done: ",done); | |
if (done) { // delete these ring entries | |
unset(rankSepPt,i); | |
} else { | |
totalD[i]=totalD[i-1]+rankSepPt[i]; // now have total distance | |
if ((totalD[i]+5)>maxD || i>=maxRanks) { // delete the rest | |
done=1; | |
maxRSPindx=i; | |
//print("// DONE"); | |
} else if (i==maxRSPindx) { | |
// this is cheesy, but keep adding entries, | |
// we will strip unneeded when we are done | |
rankSepPt[++maxRSPindx]=rankSepPt[i]; | |
//print ("// maxRSPindx: ",maxRSPindx," ",rankSepPt[maxRSPindx]," ",rankSepPt[i]); | |
} | |
} | |
} | |
unset(totalD,-1); // unyuck | |
/***************************************************************************** | |
here is code to colorize the concentric rings | |
HOWEVER, they are written LAST - on top of all the nodes and edges | |
Whoops. | |
To make this work, | |
- create a new subgraph, at the start of the processing | |
- add all the rings, largest 1st | |
********************************************************************/ | |
// added color option 6/22/23 | |
if (hasAttr($G, "ringColors") && $G.ringColors!="") { | |
colorScheme=$G.ringColors; | |
colorCnt=tokens(colorScheme,ringColor,":"); | |
} | |
// from the outside in | |
forr (totalD[i]) { | |
//print("// distance count: ", totalD[i]); | |
circ++; | |
newN=node(circleG, Nprefix + (string)(circ -1)); | |
newN.shape="circle"; | |
newN.style="dashed"; | |
newN.label=""; | |
// added next two to help external numbering | |
newN.rank=(circ-1); | |
newN.ringNo=i; | |
// fix 6/17/23 | |
newN.penwidth="1"; | |
newN.pos=(string)x[0] + "," + (string)y[0]; | |
newN.width=totalD[i]/36; | |
if (colorScheme!="") { | |
newN.style="dashed,filled"; | |
if (ringC>=colorCnt) | |
ringC=0; | |
newN.fillcolor=ringColor[ringC++]; | |
} | |
} // end of forr, rings are created | |
} |
code cleanup & minor bug fixes
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
added :