Horizontally partitioning nodes in a graph - graphviz

I'm using the dot command to generate a graph of, in this case, a series of software releases, showing the relationship among the releases.
Assumptions:
Each release has a number, with higher numbers denoting later releases.
Each release must appear above earlier releases in the graph.
Each release is based on a specific earlier release, not necessarily the most recent one.
Each "based-on" relationship is shown as an arrow from one node to another.
Some subset of the releases are separate, for reasons that aren't important to the question. I'll call them "alt" releases. "Alt" releases can be based on non-"alt" releases, and vice versa. (It happens that I'm more interested in the non-"alt" releases, but I want all releases in the graph.)
I can generate a graph that clearly shows the relationships among the releases, but I also want to put all the "alt" releases on the right side of the vertical graph, and all the non-"alt" releases on the left side -- while maintaining all the relationship information (which means arrows going from the left side of the graph to the right, and vice versa).
I've tried putting the "alt" nodes into a subgraph/cluster, but that loses the dependencies (arrows) between nodes in the cluster and nodes outside it.
I've also tried creating an invisible node in the "middle" and defining dependencies from every node to the "middle" node while specifying the direction, but I was unable to mix vertical and horizontal dependencies in the same graph.
Here's the demo.dot file I have so far, which does not do the partitioning I want:
digraph releases {
// Use a box shape for all nodes
node [shape=box];
// Define labels for nodes
v106 [style=bold];
v105 [label="v105 alt" style=dashed];
v104;
v103;
v102 [label="v102 alt" style=dashed];
v101 [style=bold];
// Define dependencies
v106 -> v104;
v106 -> v105 [style=dashed];
v105 -> v102;
v104 -> v101;
v103 -> v101;
v102 -> v101;
// Use [style=invisible dir=none] to ensure that a later node appears above
// an earlier one even if there's no dependency.
v106 -> v105 [style=invisible dir=none];
v105 -> v104 [style=invisible dir=none];
v104 -> v103 [style=invisible dir=none];
v103 -> v102 [style=invisible dir=none];
v102 -> v101 [style=invisible dir=none];
}
And here's the image file, generated with dot -Tpng demo.dot -o demo.png
What I want is to have all the "alt" nodes to the right of an invisible vertical line and all the other nodes to the left of it, with all the arrows still in place (distorted as necessary).
Is there a way to do this with dot? Or is there another tool (usable on Linux, specifically Debian) that can do the same thing?

This is a very late answer but may help others.
Graphviz tries to keep edges straight if both nodes belog to the same group.
digraph releases { nodesep=0.5
// Use a box shape for all nodes
node [shape=box];
// Define regular nodes
{
node [group=regular];
v106 [style=bold];
v104;
v103;
v101 [style=bold];
}
// Define alt nodes
{
node [group=alt style=dashed];
v102 [label="v102 alt"];
v105 [label="v105 alt"];
}
// Define dependencies
v106 -> v104;
v106 -> v105 [style=dashed];
v105 -> v102;
v104 -> v101;
v103 -> v101;
v102 -> v101;
// Use [style=invisible dir=none] to ensure that a later node appears above
// an earlier one even if there's no dependency.
edge [style=invisible dir=none];
v106 -> v105;
v105 -> v104;
v104 -> v103;
v103 -> v102;
v102 -> v101;
}
gives

Trying to do something similar. Adding this to your code gets close:
subgraph cluster_alt {
graph [style= invisible];
edge [style = invisible, arrowhead=none];
weight = 10;
v102 -> v105;
}
subgraph cluster_main {
graph [style= invisible];
edge [style = invisible, arrowhead=none];
weight = 10;
v106 -> v105-> v104->v103->v102->v101;
}
Gives me:
The "weight" attribute pushes the edges in these invisible subgraphs towards being more vertical than the other edges (the visible ones you've already defined).

Related

Graphviz drawing - make a box that starts at one node and goes to the end of another one

I have a timeline and would like a box to be drawn next to a particular number of years
digraph timeline {
node [fontsize=24, shape = plaintext];
1940 -> 1950;
1950 -> 1955;
1955 -> 1960;
node [fontsize=20, shape = box];
{ rank=same; 1940 test; }
}
This places a timeline on the left hand side going from 1940 to 1950 and so forth. I would like draw a box next to the numbers that starts at 1940 - which is what I do now with { rank=same; 1940 test; } and that ends with 1955.
Here is an example of the box drawn at 1940
Here is an example of the box drawn at 1955 with code { rank=same; 1955 test; }
I would like to have the box drawn from the start of 1940 position to the end of 1955 position, so encompassing these two boxes right now.
Tricky solution is to draw a cluster and place invisible nodes inside it using style=invis. Then align the cluster with the timeline using newrank.
Script:
digraph timeline {
newrank=true;
node [fontsize=24, shape = plaintext];
1940 -> 1950 -> 1955 -> 1960;
subgraph cluster_1 {
test [fontsize=20]
cl_start [shape=none style=invis]
cl_end [shape=none style=invis]
cl_start -> test -> cl_end [style=invis]
}
{rank=same;1940;cl_start}
{rank=same;1955;cl_end}
}
Result:
No easy way to do this with Graphviz.
dot does not allow nodes to span ranks. It centers all nodes in the same rank on the same line (horizontal, with rankdir=TB).
if you set rankdir=LR, you can get nodes to span years, but positioning & node sizing become difficult
you could put a node on every year you want the final node to span and the post-process the result to overwrite the several nodes with a single large node.
you can us the neato -n (https://graphviz.org/faq/#FaqDotWithNodeCoords) capability and position & size all the nodes yourself
or you could try another language entirely (maybe pikchr https://pikchr.org/home/doc/trunk/homepage.md)

Bisecting dashed curved lines ala Threat Model

Is there a way to add a curved dash line similar to the one present here for Privilege Boundaries? I'd like to have a threat model checked into GIT that is procedurally generated as part of my CI/CD builds as apposed to drawing it online or in some tool like OmniGraffle and exporting image only to loose the original docs.
You can get fairly close by adding some extra edges and invisible nodes and using attribute for extra edge arrowhead=icurve, but it will not be a dashed line. Graphviz allows you to draw dashed cluster boundaries, or dashed edges from node to node, but the perpendicular line to the one or two edges will be impossible to draw without manual positioning, as you drawing it online, and as I assume you need to draw without human involvement.
Also with some extra effort with PostScript you can create half-drawn nodes with dashed border like A -> Internet Boundary [shape=yourshape color=red] -> B, but this is also not actually a line crossing.
Image:
and script for arrowhead=icurve variant:
digraph threat_model {
graph [
splines=false
ranksep=0
nodesep=2]
User [shape=box xlabel=<<FONT COLOR="ORANGE"><B>Actor</B></FONT>>]
a [shape=circle label=<<FONT><B>a. Static<BR/>front-end<BR/>files</B></FONT>> xlabel=<<FONT COLOR="ORANGE"><B>Process</B></FONT>>]
b [shape=circle label=<<FONT><B>b. App<BR/>back-end</B></FONT>> xlabel=<<FONT COLOR="ORANGE"><B>Data<BR/>Flow</B></FONT>>]
// Two extra nodes:
boundary_User_b_1 [shape=point height=.01]
boundary_User_b_2 [shape=point height=.01]
User -> a [minlen=3 dir=both xlabel="1. Retrieve static\nfront-end files"]
// Three extra edges:
User -> boundary_User_b_1 [minlen=3 dir=back xlabel="2. Modify report"]
boundary_User_b_1 -> boundary_User_b_2 [arrowhead=icurve arrowsize=6 color=red headlabel=<<FONT COLOR="RED">Internet<BR/>Boundary</FONT>>]
boundary_User_b_2 -> b [minlen=3]
}
Another fairly close variant using cluster, note that the arrow can rest against boundary, with the compound=true attribute for graph and lhead attribute for arrow:
digraph {
graph[
ranksep=1
compound=true
]
A
B
subgraph cluster_IB {
graph [
label="Internet Boundary"
fontcolor=red
margin=20
style="dashed, rounded"
color=red
]
C
}
A -> C
B -> C [lhead="cluster_IB"]
}

How do I force nodes to be drawn next to each other with Graphviz dot?

I'm looking to have a series of nodes in a row, joined by an edge. This works fine when the graph's rankdir is set to TB or BT, but it rearranges the nodes when I set it to LR or RL so they're no longer next to each other. Example images are included.
I've taken my code and stripped it down to it's minimum point for demonstration. The code is the same for both of the following graphs, aside from line 2 (which specifies rankdir):
digraph{
rankdir=LR;
node[shape=box,fontcolor=white,color=black,fillcolor=black,style=filled];
edge[dir=none,color=black];
Josh -> JoshParent;
JoshParent -> Hero;
JoshParent[shape=circle,label="",height=0.0001,width=0.0001];
{
rank=same;
Kae[label="Kae"];
Hero[label="Hero"];
Kae -> Hero;
}
Kae -> KaeParent;
Hero -> HeroParent;
KaeParent -> Liz;
KaeParent[shape=circle,label="",height=0.0001,width=0.0001];
HeroParent -> George;
HeroParent[shape=circle,label="",height=0.0001,width=0.0001];
{
rank=same;
George[label="George"];
Liz[label="Liz"];
Ocean[label="Ocean"];
Egg[label="Egg"];
Liz -> Ocean -> Egg;
}
}
This is what's shown with rankdir=TB:
This is what's shown with rankdir=LR:
As you can see, from the LR image, the nodes have been drawn in the order "Ocean, George, Egg", rather than "Ocean, Egg, George" as it is with the TB image.
You can force the order by adding an explicit but invisible edge from Egg to George:
Liz -> Ocean -> Egg; // last line of your code
Egg -> George[ style = invis ]; // additional edge
This produces
I don't have an explanation for the different behaviour between TB and LR, though.

Graphviz (dot) control edge routing

In this graph the bottom edge is not drawn symmetrical to the top edge:
digraph G {
A:ne -> A:nw;
A:sw -> A:se;
}
I want it to look more like a "fat snowman" with the edge A:sw -> A:se; looping below the node. Is there a way?
Short answer no - or not easily.
Loops seem to be placed from the rankdir direction. If rankdir is TB (down), loops seem to be placed "up".
It you're willing to work at it, you can run your graph twice, once with rankdir=TB, once with rankdir=BT - both times with -Tdot. Then you'd have to replace the offending edge with the equivalent edge from the other graph. [I hope this makes some sense]
Here is a tweaked version of your graph run with different values of rankdir:
digraph G {
A:ne -> A:nw;
A:sw -> A:se;
dummy [style=invis]
dummy -> A [style=invis]
}

Add extra edges to a digraph in Graphviz

I have a .dot digraph which shows a graph as I want (depicting relationship between some tables). I want to add redundant edges to the graph (to represent redundant relationships in the database which exist only to write queries less effortly). These redundat edges, which will be written in a "dotted" style, must not change the deployment of nodes in the graph.
In other words, there's edges which must affect the node positions to print the graph pretty, and other edges which must to be added after the node positions are already computed (which will be styled differently --light gray, dotted, etc; to show that they're not main edges).
Is there options in Graphviz to specify "extra" edges?
Use constraint=false and color=gray on those additional edges.
digraph G {
a -> b -> c -> d;
d -> a [constraint=false,color=gray]
a -> z -> x -> d;
}
Play with that on http://graphviz.it/#/rhlWBZsz

Resources