Last active
May 21, 2025 14:27
-
-
Save steveroush/eb3743f0cc234339ea1b18f3c3de85f1 to your computer and use it in GitHub Desktop.
compressCluster.gvpr - replace a Graphviz cluster (and contents) with a single node
This file contains hidden or 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
/********************************************************************************** | |
compressCluster.gvpr - replace one or more clusters (and contents) with single node(s) | |
see help below | |
**********************************************************************************/ | |
BEGIN{ | |
graph_t aGraph, Root, Parent[], gList[]; | |
node_t aNode, newNode, saveNode[]; | |
edge_t newE; | |
string cStr, newStr, newName[]; | |
int i, OK, DeleteN[], DeleteG[], cName[]; | |
//////////////// help ///////////////////////////////////////////// | |
string help=" | |
compressCluster.gvpr - replace one or more clusters (and contents) with single node(s) | |
can be used: | |
- as a preprocessor, then feed output into dot | |
or | |
- as a postprocessor (after dot) to simplify an existing dot graph | |
Arguments: | |
-aCcluster_name -aCanotherCluster ...] | |
Usage examples: | |
gvpr -aCclusterdogs -aCclustercats -cf compressCluster.gvpr myfile.gv | |
"; | |
/////////////////////////////////////////////////////////////// | |
graph_t clusterCheck(graph_t thisG) { | |
for (aGraph = fstsubg(thisG); aGraph; aGraph = nxtsubg(aGraph)) { | |
if (match(aGraph.name,"cluster")==0 || (hasAttr(aGraph, "cluster") && aGraph.cluster=="true")) { | |
//print ("// CLUSTER ", aGraph.name); | |
gList[aGraph.name]=aGraph; | |
} | |
aGraph = clusterCheck(aGraph); | |
} | |
return thisG; | |
} // end of clusterTraverse | |
/////////////////////////////////////////////////////////////// | |
graph_t clusterDelete(graph_t thisG, string newname) { | |
for (aNode=fstnode(aGraph); aNode; aNode = nxtnode_sg(aGraph, aNode)) { | |
DeleteN[aNode]=1; | |
newName[aNode]=newname; | |
//print ("// old name: ", aNode.name," new name: ", newname); | |
} | |
for (aGraph = fstsubg(thisG); aGraph; aGraph = nxtsubg(aGraph)) { | |
for (aNode=fstnode(aGraph); aNode; aNode = nxtnode_sg(aGraph, aNode)) { | |
DeleteN[aNode]=1; | |
newName[aNode]=newname; | |
} | |
aGraph = clusterDelete(aGraph, newname); | |
} | |
return thisG; | |
} // end of clusterDelete | |
////////////////////////////////////////////////////////////// | |
i=0; | |
while (i<ARGC) { | |
if (ARGV[i]=="C") { // -a "C clusterName ..." | |
cName[ARGV[++i]]; | |
} else if (ARGV[i]=="C*") { // -a "CclusterName ..." | |
cName[substr(ARGV[i],1)]=1; | |
} else { | |
print(help); | |
exit (0); | |
} | |
i++; | |
} | |
} | |
////////////////////////////////////////////////////////////////////// | |
BEG_G { | |
Root=$G; | |
clusterCheck(Root); | |
OK=1; | |
for (cName[cStr]) { | |
aGraph=gList[cStr]; | |
if (aGraph==NULL) { | |
printf(2, "Error: there is no cluster named %s\n", cStr); | |
OK=0; | |
} else { | |
DeleteG[aGraph]=1; | |
newStr="_COLLAPSED_" + aGraph.name; | |
newNode=node(aGraph.parent, newStr); | |
saveNode[newStr]=newNode; | |
if (hasAttr(aGraph, "bgcolor")) { | |
newNode.style="filled"; | |
newNode.fillcolor=aGraph.bgcolor; | |
} else { | |
newNode.color="red"; | |
} | |
if (hasAttr(aGraph, "shape")) { | |
newNode.shape=aGraph.shape; | |
} else { | |
newNode.shape="rect"; | |
} | |
if (hasAttr(aGraph, "label") && aGraph.label!="" && aGraph.label!="\G") { | |
newNode.label=aGraph.label; | |
} else { | |
newNode.label=aGraph.name; | |
} | |
if (hasAttr(aGraph, "bb") && aGraph.bb!="") { | |
newNode.origBB=aGraph.bb; | |
float llx, lly, urx, ury; | |
sscanf (aGraph.bb, "%lf,%lf,%lf,%lf", &llx, &lly, &urx, &ury); | |
// adjust bb values to remove cluster margin | |
newNode.pos=(string)(llx+((urx-llx)/2)) + "," + (string)(lly+((ury-lly)/2)); | |
} | |
clusterDelete(aGraph, newStr); | |
} | |
} | |
if (OK!=1) | |
exit(1); | |
} | |
//////////////////////////////////////////////////////////////////////////////////// | |
E{ | |
//print("// edge: ", $.name); | |
if (DeleteN[$.tail] == 0 && DeleteN[$.head]==0) | |
continue; // no cluster involvement, skip | |
if (newName[$.tail] == newName[$.head]) { | |
//print("// edge - both ends in same cluster to be deleted: ", $.name); | |
delete(Root, $); | |
continue; // all in cluster, just delete | |
} | |
//print("// fix this edge: ", $.name); | |
if (DeleteN[$.tail] == 1) { | |
newE=edge(saveNode[newName[$.tail]], $.head, ""); | |
copyA($, newE); | |
newE.label=""; | |
newE.pos=""; | |
//print("// adding edge: ", newE.name); | |
//print("// deleting edge: ", $.name); | |
delete(Root, $); | |
} else { | |
newE=edge($.tail, saveNode[newName[$.head]], ""); | |
copyA($, newE); | |
newE.label=""; | |
newE.pos=""; | |
//print("// deleting edge: ", $.name); | |
delete(Root, $); | |
} | |
} | |
BEG_G{ | |
//reset traverse | |
} | |
N[DeleteN[$]==1] { | |
//print("// deleting node: ", $.name); | |
delete(Root, $); | |
} | |
END_G{ | |
for (DeleteG[aGraph]) { | |
//print("// deleting graph: ", aGraph.name); | |
delete(Root, aGraph); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment