rgraphvizdiagrammer

How to center text in custom html-style GraphViz/DiagrammeR node?


Following the helpful answer of a previous question, there is still an unresolved issue, that the hourglass symbol (i.e. the text in custom node) is not centered, so that vertical edges are not in the middle. I tried also different html-style centering options, but with no success. Any method or a completely different approach would be welcome.

library(DiagrammeR)

graph <- "
digraph logistic_growth {

  graph [rankdir = 'LR', bgcolor='none', fontsize=10]

  node [shape = circle, fontsize=8, penwidth=0.7, fontname='Open Sans']
   r [xlabel=' intrinsic growth rate']
   K [xlabel=' carrying capacity']

  node [shape = box, penwidth=2]
    N [label='Population N']

  node [shape = octagon, width='', penwidth=0.5, style='rounded', fixedsize=25]
    Source Sink

  node [shape=plaintext, width=0.2, height=0.2]
    growth [label=<<table cellpadding='0' border='0' cellspacing='0'><tr><td height='10'></td></tr><tr><td><FONT POINT-SIZE='20'>&#10710;</FONT></td></tr><tr><td>growth</td></tr></table>>]

  edge [penwidth=1.5]
    Source -> growth [dir = none, color = 'blue' headclip=false]
    growth -> N      [dir = left, color = 'blue' tailclip=false]
    N -> Sink

  edge [penwidth=0.7, tailport = 'n', headport = 'n', constraint = false, color=tomato]
    N -> growth

  edge [penwidth=0.7, tailport = 'e', headport = 'n', constraint = false, color=tomato]
    r -> growth
    K -> growth
}
"

grViz(graph)

digraph of a logistic model


Solution

  • Here is a convoluted but solid solution.

    dot input:

    digraph logistic_growth {
    
      graph [rankdir = "LR", XXbgcolor="none", fontsize=10]
    
      node [shape = circle, fontsize=8, penwidth=0.7, fontname="Open Sans"]
       r [xlabel=" intrinsic growth rate"]
       K [xlabel=" carrying capacity"]
    
      node [shape = box, penwidth=2]
        N [label="Population N"]
    
      node [shape = octagon, width="", penwidth=0.5, style="rounded", fixedsize=25]
        Source Sink
    
      node [shape=plaintext, width=0.2, height=0.2]
        growth [label="" xlabel="\N" hourglass=1]  // any undefined attribute name
    
      edge [penwidth=1.5]
        Source -> growth [dir = none, color = "blue" headclip=false]
        growth -> N      [dir = left, color = "blue" tailclip=false]
        N -> Sink
    
      edge [penwidth=0.7, tailport = "n", headport = "n", constraint = false, color=tomato]
        N -> growth:n
    
      edge [penwidth=0.7, tailport = "e", headport = "n", constraint = false, color=tomato]
        r -> growth:n
        K -> growth:n
    }
    

    gvpr program (save as hourglass.gvpr):

    BEGIN{
      int i;
      edge_t hGlass;
      string x1, y1, x2, y2, pp1, p2, p3, p4, segment1, segment2, segment3, segment4;
    }
    
    N[hourglass=="1"]{
      // first, move xlabel
      oldXLP=xlp;
      xlp=(string)$.X + ","+ (string) yOf($.xlp);
      // create a very bogus edge that will become the hourglass figure
      // need to convert inches to points then divide by 2 == *72/2 == *36
      x1=(string)($.X-(((float)$.width/2)*72));
      x2=(string)($.X+(((float)$.width/2)*72));  
      y1=(string)($.Y-(((float)$.height/2)*72));
      y2=(string)($.Y+(((float)$.height/2)*72));
      p1=x1 + "," + y1 + " ";
      p2=x2 + "," + y1 + " ";
      p3=x1 + "," + y2 + " ";
      p4=x2 + "," + y2 + " ";
      segment1=p1 + p1 + p1 + p2;
      segment2=p2 + p2 + p3;
      segment3=p3 + p3 + p4;
      segment4=p4 + p4 + p1;
      hGlass=edge($, $, "");           //$.name + "->" + $.name);
      hGlass.pos=segment1 + segment2 + segment3 + segment4;
      hGlass.color="black";
    }
    

    Finally, the command line (Linux) (Windows can be broken into separate steps)

    dot myFile.gv |gvpr -cfhourglass.gvpr |neato -n2 -Tsvg >myFile.svg