graphvizdotunreal-blueprint

Graphviz DOT syntax... is there a way to change the anchor of nodes?


I'm trying to replicate hundreds of directed graphs from another software for documentation purposes.

I have the written a plugin so the software creates output in dot syntax, and we can import/publish that output in doxygen or wherever.

The issue is that my software considers the upper left of a node to be the 0,0 position, but the dot and fdp renderer/layout systems seem to use the center of an object as 0,0.

Unfortunately the width/height is unknown until fonts (and their sizings) are rendered to screen. I'm trying to process libraries headlessly, so nothing goes to screen, and the file format doesn't record width/height.

Is there a way for me to have dot syntax use upper left as an anchor for objects? I can't seem to find it anywhere in the documentation.


Solution

  • Short answer: no. There is no way to get any of the Graphviz engines to use UL of a node as the pos value.

    However, it is possible to establish the UL coordinate of every node in an automated way.
    Here is a pipeline that does a standard layout, but produces "dot" output format (https://graphviz.org/docs/outputs/canon/). That is then fed to a gvpr program (https://www.graphviz.org/pdf/gvpr.1.pdf) that will compute the UL coordinate, based on the pos value and the values of height & width for each node. (changing inches to points as needed). The Graphviz engines all allow the user to include undefined attributes - here we add the UL attribute. (If desired, you could use two attributes, say Upper and left).
    My assumption is that you are making other changes to the dot output, so those changes would be made after the gvpr step and before actual output generation.
    The example below uses dot as the layout engine, but it works for all the engines.
    dot -Tdot myinput.gv | gvpr -cf setUL.gvpr | neato -n2 -Tpng >mine.png

    Here is setUL.gvpr:

    BEGIN{
      float ht, wid, defH, defW;
    }
    N{
      // get defaults, if needed
      // i wonder if they can change as  the input continues!!
      // let us assume so, get defaults for each node
      if (isAttr($G, "N", "height"))
        defH=getDflt($G, "N", "height");
      else
        defH=-1;
      if (isAttr($G, "N", "width"))
        defW=getDflt($G, "N", "width");
      else
        defW=-1;
      //print("// defaults: ", defH, " -- ", defW);
    
      if (hasAttr($, "height") &&  $.height!=""){
        ht=$.height;
      }else{
        if (defH>=0)
          ht=defH;
      }
      if (hasAttr($, "width") &&  $.width!=""){
        wid=$.width;
      }else{
        if (defW>=0)
          wid=defW;
      }
      //inches to points!
      ht*=72;
      wid*=72;
      $.UL=(string)((float)$.X-(wid/2.)) + "," + (string)((float)$.Y+(ht/2.));
    }
    

    Here is example output from gvpr step:

    digraph G {
            graph [bb="0,0,86,140"];
            node [label="\N"];
            subgraph cluster_2 {
                    graph [bb="8,8,78,132",
                            color=red
                    ];
                    c       [UL="16,124",
                            height=0.5,
                            pos="43,106",
                            shape=rect,
                            width=0.75];
                    d       [UL="16,52",
                            height=0.5,
                            pos="43,34",
                            shape=rect,
                            width=0.75];
                    c -> d  [color=red,
                            pos="e,43,52.104 43,87.697 43,80.407 43,71.726 43,63.536"];
            }
    }