Skip to content

Instantly share code, notes, and snippets.

@steveroush
Last active May 21, 2025 14:27
Show Gist options
  • Select an option

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

Select an option

Save steveroush/eb3743f0cc234339ea1b18f3c3de85f1 to your computer and use it in GitHub Desktop.

Revisions

  1. steveroush revised this gist May 21, 2025. 1 changed file with 5 additions and 3 deletions.
    8 changes: 5 additions & 3 deletions compressCluster.gvpr
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    /**********************************************************************************
    compressCluster.gvpr - replace a cluster (and contents) with a single node
    compressCluster.gvpr - replace one or more clusters (and contents) with single node(s)
    see help below
    **********************************************************************************/
    BEGIN{
    @@ -11,14 +11,16 @@ BEGIN{

    //////////////// help /////////////////////////////////////////////
    string help="
    compressCluster.gvpr - replace a cluster (and contents) with a simple node
    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 ...
    -aCcluster_name -aCanotherCluster ...]

    Usage examples:
    gvpr -aCclusterdogs -aCclustercats -cf compressCluster.gvpr myfile.gv
    ";
  2. steveroush created this gist May 21, 2025.
    152 changes: 152 additions & 0 deletions compressCluster.gvpr
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,152 @@
    /**********************************************************************************
    compressCluster.gvpr - replace a cluster (and contents) with a single node
    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 a cluster (and contents) with a simple node
    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 ...
    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);
    }
    }