1

I want to create a graph with both splines=line and splines=splines, similar to the image below.

However, to achieve this, I had to create two separate graphs via the R package DiagrammeR and combine them using the figpatch and patchwork packages.

Is this possible just with graphviz? I'm not 100% certain, but I don't think it is… But just want to cover all bases to make sure.

Can someone please confirm / deny my hunch. If the latter, some examples or resources on how to do so would be greatly appreciated.

Please let me know if you need more info about my questions.

enter image description here

Graphs

Below is the code used to generate the graphs.

Graph 1: The one on the left.

digraph dot {

    // splines = curved;
    node[fontname = arial, width=1.5, height=1.5, shape=circle, fontsize=50, style = filled, penwidth = 3];
    edge[fontname = arial, penwidth = 3.6, fontsize = 50]

    RItb0  [pos = "000,0", style = invis, fillcolor = palevioletred];
    RIpb0  [pos = "100,0", style = invis, fillcolor = darkseagreen3];
    RIsi0  [pos = "200,0", style = invis, fillcolor = white];
    RIbhs0 [pos = "300,0", style = invis, fillcolor = mediumorchid3];
    RIdep0 [pos = "400,0", style = invis, fillcolor = cadetblue3];
    RItb   [pos = "000,100", width = 3, height = 3, fillcolor = palevioletred];
    RIpb   [pos = "100,100", width = 3, height = 3, fillcolor = darkseagreen3];
    RIsi   [pos = "200,100", width = 3, height = 3, fillcolor = white];
    RIbhs  [pos = "300,100", width = 3, height = 3, fillcolor = mediumorchid3];
    RIdep  [pos = "400,100", width = 3, height = 3, fillcolor = cadetblue3];
    tb1    [pos = "000,200", style = invis, fillcolor = palevioletred];
    pb1    [pos = "100,200", style = invis, fillcolor = darkseagreen3];
    si1    [pos = "200,200", style = invis, fillcolor = white];
    bhs1   [pos = "300,200", style = invis, fillcolor = mediumorchid3];
    dep1   [pos = "400,200", style = invis, fillcolor = cadetblue3];

    {   edge [weight = 10, minlen = 4.0; style = invis, penwidth = 3];
        // Random Intercepts
        RItb0  -> RIpb0  ; // [minlen = 4.0];
        RIpb0  -> RIsi0  ; // [minlen = 4.0];
        RIsi0  -> RIbhs0 ; // [minlen = 4.0];
        RIbhs0 -> RIdep0 ; // [minlen = 4.0];
        RItb   -> RIpb   ; // [minlen = 4.0];
        RIpb   -> RIsi   ; // [minlen = 4.0];
        RIsi   -> RIbhs  ; // [minlen = 4.0];
        RIbhs  -> RIdep  ; // [minlen = 4.0];
        tb1    -> pb1  ;
        pb1    -> si1  ;
        si1    -> bhs1 ;
        bhs1   -> dep1 ;
    }

    // autogregrssive paths
    {   rank = same; RItb0  -> RItb  [style = invis, labeldistance = 4.3, minlen = 6, color = palevioletred, style = invis]; }
    {   rank = same; RItb   -> tb1   [style = invis, labeldistance = 4.3, minlen = 6, color = palevioletred, style = invis]; }
    {   rank = same; RIpb0  -> RIpb  [style = invis, labeldistance = 4.3, minlen = 6, color = darkseagreen3, headlabel = ".28"]; }
    {   rank = same; RIpb   -> pb1   [style = invis, labeldistance = 4.3, minlen = 6, color = darkseagreen3, headlabel = ".28"]; }
    {   rank = same; RIsi0  -> RIsi  [style = invis, labelangle = 29.0, labeldistance = 4.3, minlen = 6, headlabel = ".35"]; }
    {   rank = same; RIsi   -> si1   [style = invis, labelangle = 29.0, labeldistance = 4.3, minlen = 6, headlabel = ".35"]; }
    {   rank = same; RIbhs0 -> RIbhs [style = invis, labelangle = 29.0, labeldistance = 4.3, minlen = 6, color = mediumorchid3, headlabel = ".43"];}
    {   rank = same; RIbhs  -> bhs1  [style = invis, labelangle = 29.0, labeldistance = 4.3, minlen = 6, color = mediumorchid3, headlabel = ".43"];}
    {   rank = same; RIdep0 -> RIdep [style = invis, labelangle = 29.0, labeldistance = 4.3, minlen = 6, color = cadetblue3; style = invis]; }
    {   rank = same; RIdep  -> dep1  [style = invis, labelangle = 29.0, labeldistance = 4.3, minlen = 6, color = cadetblue3; style = invis]; }

    {
    // cross-lagged paths
        edge[style = dashed, labeldistance = 4.3, labelfloat = false, constraint = true, minlen = 5]
        RIpb  -> RItb  [penwidth = 5, dir = both, label = ".47"];
        RIpb  -> RIbhs [penwidth = 5, dir = both, label = ".55"];
        RIpb  -> RIdep [penwidth = 5, dir = both, label = ".67"];
        RItb  -> RIbhs [penwidth = 5, dir = both, label = ".60"];
        RItb  -> RIdep [penwidth = 5, dir = both, label = ".58"];
        RItb  -> RIsi  [penwidth = 9.5, dir = both, style = dashed, label = ".27"];
        RIbhs -> RIsi  [penwidth = 9.5, dir = both, style = dashed, label = ".31"];
        RIdep -> RIsi  [penwidth = 9.5, dir = both, style = dashed, label = ".37"];
        RIbhs -> RIdep [penwidth = 5, dir = both, label = ".62"];
    }
}

Graph 2: The one on the right.

digraph dot {
    splines = line;
    node[fontname = arial, width=1.5, height=1.5, shape=circle, fontsize=36, style = filled, penwidth = 3];
    edge[fontname = arial, penwidth = 3.6, fontsize = 28]

    tb1  [pos = "000,200", fillcolor = palevioletred];
    pb1  [pos = "100,200", fillcolor = darkseagreen3];
    si1  [pos = "200,200", fillcolor = white];
    bhs1 [pos = "300,200", fillcolor = mediumorchid3];
    dep1 [pos = "400,200", fillcolor = cadetblue3];
    tb2  [pos = "000,300", fillcolor = palevioletred];
    pb2  [pos = "100,300", fillcolor = darkseagreen3];
    si2  [pos = "200,300", fillcolor = white];
    bhs2 [pos = "300,300", fillcolor = mediumorchid3];
    dep2 [pos = "400,300", fillcolor = cadetblue3];
    tb3  [pos = "000,400", fillcolor = palevioletred];
    pb3  [pos = "100,400", fillcolor = darkseagreen3];
    si3  [pos = "200,400", fillcolor = white];
    bhs3 [pos = "300,400", fillcolor = mediumorchid3];
    dep3 [pos = "400,400", fillcolor = cadetblue3];
    tb4  [pos = "000,500", fillcolor = palevioletred];
    pb4  [pos = "100,500", fillcolor = darkseagreen3];
    si4  [pos = "200,500", fillcolor = white];
    bhs4 [pos = "300,500", fillcolor = mediumorchid3];
    dep4 [pos = "400,500", fillcolor = cadetblue3];
    tb5  [pos = "000,600", fillcolor = palevioletred];
    pb5  [pos = "100,600", fillcolor = darkseagreen3];
    si5  [pos = "200,600", fillcolor = white];
    bhs5 [pos = "300,600", fillcolor = mediumorchid3];
    dep5 [pos = "400,600", fillcolor = cadetblue3];

    {   edge [weight = 10, minlen = 4.0; style = invis, penwidth = 3];
        tb1   -> pb1  ;
        pb1   -> si1  ;
        si1   -> bhs1 ;
        bhs1  -> dep1 ;
        tb2   -> pb2  ;
        pb2   -> si2  ;
        si2   -> bhs2 ;
        bhs2  -> dep2 ;
        tb3   -> pb3  ;
        pb3   -> si3  ;
        si3   -> bhs3 ;
        bhs3  -> dep3 ;
        tb4   -> pb4  ;
        pb4   -> si4  ;
        si4   -> bhs4 ;
        bhs4  -> dep4 ;
        tb5   -> pb5  ;
        pb5   -> si5  ;
        si5   -> bhs5 ;
        bhs5  -> dep5 ;
    }

    // autogregrssive paths
    {   rank = same; tb1   -> tb2  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = palevioletred, style = invis]; }
    {   rank = same; tb2   -> tb3  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = palevioletred, headlabel = ".37"]; }
    {   rank = same; tb3   -> tb4  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = palevioletred, headlabel = ".28"]; }
    {   rank = same; tb4   -> tb5  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = palevioletred, headlabel = ".30"]; }
    {   rank = same; pb1   -> pb2  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = darkseagreen3, headlabel = ".28"]; }
    {   rank = same; pb2   -> pb3  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = darkseagreen3; style = invis]; }
    {   rank = same; pb3   -> pb4  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = darkseagreen3, headlabel = ".30"]; }
    {   rank = same; pb4   -> pb5  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = darkseagreen3, headlabel = ".45"]; }
    {   rank = same; si1   -> si2  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, style = bold, headlabel = ".35"]; }
    {   rank = same; si2   -> si3  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, style = bold, headlabel = ".25"]; }
    {   rank = same; si3   -> si4  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, style = bold; style = invis]; }
    {   rank = same; si4   -> si5  [labelangle = -39.5, labeldistance = 3.6, minlen = 16, style = bold, headlabel = ".27"]; }
    {   rank = same; bhs1  -> bhs2 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = mediumorchid3, headlabel = ".43"];}
    {   rank = same; bhs2  -> bhs3 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = mediumorchid3, headlabel = ".49"]; }
    {   rank = same; bhs3  -> bhs4 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = mediumorchid3, headlabel = ".50"]; }
    {   rank = same; bhs4  -> bhs5 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = mediumorchid3, headlabel = ".48"]; }
    {   rank = same; dep1  -> dep2 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = cadetblue3; style = invis]; }
    {   rank = same; dep2  -> dep3 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = cadetblue3, headlabel = ".25"]; }
    {   rank = same; dep3  -> dep4 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = cadetblue3, headlabel = ".27"]; }
    {   rank = same; dep4  -> dep5 [labelangle = -39.5, labeldistance = 3.6, minlen = 16, color = cadetblue3, headlabel = ".37"]; }

    {
    // cross-lagged paths
        edge[style = dashed, labelangle = -39.5, labeldistance = 3.6, labelfloat = true]
        ## [headlabel=<<table border="0" cellborder="0"><tr><td bgcolor="white">Head Label</td></tr></table>>,taillabel="Tail Label"]
        bhs1 -> pb2 [labelangle = 39.5, color = mediumorchid3, headlabel = ".39"];
        bhs2 -> pb3 [labelangle = 39.5, color = mediumorchid3, headlabel = ".25"];
        bhs3 -> pb4 [labelangle = 39.5, color = mediumorchid3, headlabel = ".31"];
        bhs3 -> si4 [color = mediumorchid3, headlabel = ".30"];
        bhs4 -> tb5 [color = mediumorchid3, headlabel = ".25"];
        dep4 -> pb5 [labelangle = 39.5, color = cadetblue3,    headlabel = ".26"];
        pb2 -> tb3  [color = darkseagreen3, headlabel = ".25"];
        pb3 -> si4  [color = darkseagreen3, headlabel = ".30"];
        pb3 -> dep4 [color = darkseagreen3, headlabel = ".35"];
        pb3 -> bhs4 [color = darkseagreen3, headlabel = ".28"];
        pb4 -> dep5 [color = darkseagreen3, headlabel = ".30"];
        pb4 -> bhs5 [color = darkseagreen3, headlabel = ".27"];
        si1 -> bhs2 [headlabel = ".19"];
        si1 -> dep2 [headlabel = ".17"];
        si1 -> pb2  [headlabel = ".14"];
        si2 -> pb3  [headlabel = ".16"];
        tb2 -> si3  [color = palevioletred, headlabel = ".15"];
    }
}

Atanas Janackovski
  • 348
  • 1
  • 2
  • 12

1 Answers1

2

No - the splines attribute applies to the entire graph, so it can't be done with a single invocation / pass of a Graphviz tool

Yes - Chop your graph into multiple graphs, each cluster into its own graph. Each (new) graph can have a different splines value. Run each (new) graph through dot -Tdot to determine layouts of each separately. Run those results through gvpack (https://www.graphviz.org/pdf/gvpack.1.pdf 2) to combine them into a single graph. And finally hand that to neato -n2 (https://graphviz.org/faq/#FaqDotWithNodeCoords) to produce an output file.
Like so:

S="";
for f in sub[12].gv;  # sub1.gv and sub2.gv are the two created graphs
do 
  T=dot; 
  F=`basename $f .gv`;
  dot -T$T $f >$F.$T; 
  S="$S  $F.$T"; 
done; 
gvpack -array_i3  $S |
  neato -n2 -Tpng >O.png

Note - if necessary, it is possible to apply different splines values to individual edges by running a dot-formatted graph through neato -n2 multiple times, each time changing the splines value and removing individual edge pos values. Possible, but messy.

sroush
  • 5,375
  • 2
  • 5
  • 11
  • 1
    Here is another take on the problem: https://stackoverflow.com/questions/62687030/is-there-a-way-to-have-different-edge-splines-between-nodes-in-the-same-cluster – sroush Sep 27 '21 at 21:09
  • Thanks @sroush. I'm not a programmer, so apologies for any ignorance, but how would I run your above script? Is it `bash` or some other language? – Atanas Janackovski Sep 28 '21 at 00:17
  • Okay, so I got a script up and running in `sh`, however I am getting the error: `Error: node RItb0 in graph dot has no position; Error loading layout info from graph dot`. I see in the post above that this could be something to do with `pin` or `keep`... What am I doing wrong? – Atanas Janackovski Sep 28 '21 at 00:32
  • please post your graphs – sroush Sep 28 '21 at 01:00
  • I've just posted the code. I added the `pos` argument to each node at the top of the graph, so this error is gone now. However, I think I may have an issue with the script calling the relative dir i.e., `Can't open graph_0.dot graph_2.dot`. I have different files names. – Atanas Janackovski Sep 28 '21 at 01:05
  • Okay, so running manually: `gvpack -array_i3 ./graph_0.gv && gvpack -array_i3 ./graph_2.gv | neato -s -n2 -Tpng >0.png` I get an error re: nodes have last their edge. What do I do with this? – Atanas Janackovski Sep 28 '21 at 02:06
  • Try this: **gvpack -array_i3 ./graph_0.gv ./graph_2.gv | neato -s -n2 -Tpng >0.png** – sroush Sep 28 '21 at 03:38
  • By the way, manually adding the **pos** values was not strictly needed (it did not hurt) **dot -Tdot** should have added **pos** for you – sroush Sep 28 '21 at 03:42
  • Thanks. I'm getting a number of errors e.g., `❯ gvpack -array_i3 ./graph_0.gv ./graph_2.gv | neato -s -n2 -Tpng >0.png Warning: node tb1 in graph[1] dot already defined Some nodes will be renamed. Error: lost RItb0 RIpb0 edge `. The last error seems to be occurring for every edge and node that has been defined. Any idea what might be causing this? – Atanas Janackovski Sep 28 '21 at 03:43
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/237589/discussion-between-atanas-janackovski-and-sroush). – Atanas Janackovski Sep 28 '21 at 11:34
  • Okay, so I finally got it working, but not with the script. I still get the Can't open error… Any ideas why this is happening? – Atanas Janackovski Sep 28 '21 at 12:46