GraphViz DOT output: What are the 4-6 "pos" coordinates for an "edge" - graphviz

I just started to experiment with GraphViz's "neato" command for
Kamada-Kawai graph visualization (solved using stress majorization).
I am particularly interested in extracting the laid out node
coordinates for external processing.
I was looking at this
question
to figure out the "pos" attribute for graph edges in the output DOT
file. The attribute consists of 4 coordinate points. One helpful
answer says that the
series of coordinats (not necessarily 4) are the control points of a
spline, likely a Bezier
spline.
The answerer is "compound eye", who I'll refer to as "CE". Here is
the example given:
// digrfG.dot
//-----------
digraph G {
A [label = "A" xlabel="a" pos="0,100" ]
B [label = "B" xlabel="b" pos="100,0"]
A -> B [label = "A->B www" xlabel="a->b"]
[pos="0,100 0,0 0,0 100,0"]
}
In Bash, it is processed by GraphViz using:
# Make image:
#------------
dot -Kneato -n2 -Tpng digrfG.dot >| digrfG.png
# Make output DOT file:
#----------------------
dot -Kneato -n2 digrfG.dot >| digrfG_out.dot
The output is:
# digrfG_out.dot
#---------------
digraph G {
graph [bb="-7,0,154,151"];
node [label="\N"];
A [height=0.5, label=A, pos="27,118",
width=0.75, xlabel=a, xlp="-3.5,143.5"];
B [height=0.5, label=B, pos="127,18",
width=0.75, xlabel=b, xlp="96,43.5"];
A -> B [label="A->B www", lp="42.5,75.5",
pos="27,118 27,18 27,18 127,18",
xlabel="a->b", xlp="63.5,60.5"];
}
My edge coordinates do not match
CE's, neither in the
numerical quantities nor in the number of coordinates. While I have 4
coordinates, CE has 6, including 2 coordinates at the start of the
series prefixed by "s," and "e,". The Bezier curve
page
cited by CE gives the impression that typical Bezier splines have 4
control points, including start and end points, though the math
allows for more. This Micrsoft
page
reinforces this impression of a 4-point default.
This GraphViz
page shows that
an edge's "pos" attribute contains a spline, starting with start and
end points, prefixed with "s," and "e," as per CE's output, but the
syntax is puzzling. If it is regular expression syntax, then there
are 4 or more coordinates following optional start and end points.
That might explain why I have no coordinates prefixed with "s," or
"e,", but I have 4 coordinates.
In digrfG_out.dot above, if I compare the 4 coordinates with the
node coordinates, it is clear that the first and last of the 4
coordinates match the node coordinates. It seems that CE's GraphViz
defaults to 6 control points and explicitly designates start and end
points at the head of the list of coordinates, whereas my GraphViz
defaults to 4, without special treatment of the start and end
points. Since GraphViz's spline
page is so
ambiguous, I was wondering if this interpretation can be confirmed.
Thanks.

I would suggest skipping CE's explanation (not necessarily wrong, but maybe ambiguous) and go to the sources on the Graphviz website.
Yes, Bezier curves require 4 points per "segment" (my term) but the last point in a segment is also used as the first in the next segment. Graphviz's arrowheads use separate points - for one end of the arrowhead.
The optional s (start) and e (end) points are for optional arrowheads. Then 4 required points and then optional sets of 3 points.
See p.36 https://www.graphviz.org/pdf/dotguide.pdf, https://forum.graphviz.org/t/how-to-control-the-points-of-splines/1087, and https://forum.graphviz.org/t/fun-with-edges/888 - in addition to the (yes, regular expression) http://www.graphviz.org/docs/attr-types/splineType/
I hope this helps.
Any edge can have arrowheads, even in a non-directed graph. Digraph just sets the default (arrowhead or no arrowhead). The dir attribute (https://graphviz.org/docs/attrs/dir/) explicitly sets arrowheads.
Finally, arrowhead shape (https://graphviz.org/doc/info/arrows.html) can be "none".
graph {
A--B [dir=forward]
C--D [dir=back]
E--F [dir=both]
G--H [dir=none]
}
Dot produces this:

Related

Increase curvature of connecting arcs

Consider:
digraph D {
0[pos="0,0!"]
1[pos="0,5!"]
0 -> 1[label=0.25]
1 -> 0[label=0.50]
}
Rendered under neato engine, this gives:
Is there a way to increase/control the curvature of the connecting arcs so that the rendering is something like the hand-drawn annotations below:
Edited to add: While I don't have to stick with neato, it is important in my application that the node pos coordinate values be upheld.
[there may be better ways, but edges are pretty hard to wrangle]
This solution requires neato -n (see https://graphviz.org/faq/#FaqDotWithNodeCoords) and (https://graphviz.org/docs/attrs/pos/)
Different versions of neato seem to produce different results when previous answer is input. This input file should work with older & newer versions of neato.
digraph D {
// next lines from the Graphviz FAQ
overlap=false
splines=true
0[pos="0,0!"]
1[pos="0,5!"]
// stick an invisible node in the middle, to force the edges out
half [pos="0,2.5!" width=1.1 height=1 style=invis]
0:nw -> 1:sw [label=0.25]
1:se -> 0:ne [label=0.50]
}
Giving:

How to draw a spline curve between 2 points with a control points with graphviz?

I would like to create a spline curve between the blue point and the green point with the red point as a control point.
Graphviz documentation says :
splines attribute, of type bool or string, is valid on Graphs
splineType with the pattern : spline ( ';' spline )* is a valid type for pos attribute
pos attribute is valid on Edges and Nodes
I Tried this graph
graph G{
layout ="neato" outputorder="edgesfirst" splines="true"
a[shape="point" pos="0,0!" color="blue"]
b[shape="point" pos="1,0!" color="green"]
c[shape="point" pos=" 0.5,0.5!" color="red"]
a -- b [pos="e,1,0 s,0,0 0.5,0.5!"]
}
then on Windows 10 PowerShell :
neato.exe -Tsvg .\spline.dot > .\spline.svg
with
neato - graphviz version 2.49.3 (20211023.0002)
result
What is the proper way of achieving this?
Thanks.
Close, but ...
! notation seems to only apply to nodes (not edges) (poorly documented, but see: https://graphviz.org/docs/attr-types/point/ and https://graphviz.org/docs/attrs/pin/)
by default, pos value units are points (inch/72). Your values are ~ too small
neato -n2 will use node & edge values you provide (https://graphviz.org/faq/#FaqDotWithCoords)
all edges (not just curves) are defined as cubic B-splines and are defined by 1 + n*3 points (n is integer >=1) (https://graphviz.org/docs/attr-types/splineType/) (i.e. 4 or 7 or 11 or ...)
So:
graph G{
// default units for pos values are in points (72/inch)
// multiplied by 100 out of laziness
// ! only applies to nodes, not edges
layout ="neato"
outputorder="edgesfirst" // why???
splines="true"
a[shape="point" pos="0,0!" color="blue"]
b[shape="point" pos="100,0!" color="green"]
c[shape="point" pos="50,50!" color="red"]
// "s" arrowhead must preceed "e" arrowhead, so swaped them
// BUT, "--" says non-directed graph, NO arrowheads, so they are deleted
// also need 4, 7, 11, ... points NOT including arrowhead points
// so added points (just guesses)
a -- b [pos="0,0 30,66 70,66 100,0"]
}
Gives:
Whew

How to force neato engine to reverse node order?

I'm trying to draw a graph where the shorter word "Myth" is in the top right, however the neato engine is recalcitrant and refuses to accept pos inputs (with or without !) and rank inputs. When appending ! to my pos inputs, it will just stretch the graph out really far, and keep "Hominids" in the top row, while putting "Myth" in the bottom.
digraph G {
layout="neato";
center="true";
fontpath="C:\Windows\Fonts";
node [fontsize="14"];
node [fontname="Abode Jenson Pro"];
graph [size="10,10!"];
rankdir=LR;
meme [shape=ellipse,label="Myth",rank="min",pos="3,0!"];
gene [shape=ellipse,label="Hominids",rank="max",pos="7,10!"];
meme -> gene [arrowsize="0.5",label="increases \n survival of",fontsize="8",constraint="true"];
gene -> meme [arrowsize="0.5",label=" generate \n venerate",fontsize="8",constraint="false"];
}
Graphviz counts coordinates as on the orthogonal graph with 0 being at the bottom left corner. So in pos attribute first coordinate is distance from left side to right →, and the second one — from bottom to top ↑.
Look at you current coordinate values:
So it's logical that your nodes appear as on the picture: you've forced their positions to be so! If you inverse the pos values you will get the result you need:
digraph G {
layout="neato";
center="true";
fontpath="C:\Windows\Fonts";
node [fontsize="14"];
node [fontname="Abode Jenson Pro"];
graph [size="10,10!"];
meme [shape=ellipse,label="Myth",rank="min",pos="7,10"];
gene [shape=ellipse,label="Hominids",rank="max",pos="3,0"];
meme -> gene [arrowsize="0.5",label="increases \n survival of",fontsize="8",constraint="true"];
gene -> meme [arrowsize="0.5",label=" generate \n venerate",fontsize="8",constraint="false"];
}
Also note that rankdir attribute you used, won't have any effect in neato. As stated in the documentation it only works with dot layout.

How to draw a graphviz spline in d3

Background
graphviz is great for laying out graphs. When I layout my graph with graphviz "dot" it produces a file with nodes and edges tagged with positions. E.g. an edge:
"a" -> "b" [pos="e,152.13,411.67 91.566,463.4 108.12,449.26 127.94,432.34 144.37,418.3"];
The Graphviz spline language is the following:
spline = (endp)? (startp)? point (triple)+
and triple = point point point
and endp = "e,%f,%f"
and startp = "s,%f,%f"
If a spline has points p1 p2 p3 ... pn, (n = 1 (mod 3)), the points correspond to the control points of a cubic B-spline from p1 to pn. If startp is given, it touches one node of the edge, and the arrowhead goes from p1 to startp. If startp is not given, p1 touches a node. Similarly for pn and endp.
So decomposing the string above,
e,152.13,411.67 // This is an endpoint.
91.566,463.4 // p1
108.12,449.26 // the following three points are control points for a cubic b-spline
127.94,432.34
144.37,418.3
Question
I want to draw this spline via d3. d3 seems to have cubic b-splines with the following signature:
d3.curveBasis(context)
Produces a cubic basis spline using the specified control points. The first and last points are triplicated such that the spline starts at the first point and ends at the last point, and is tangent to the line between the first and second points, and to the line between the penultimate and last points.
So how do I take the string
[pos="e,152.13,411.67 91.566,463.4 108.12,449.26 127.94,432.34 144.37,418.3"];
And draw the spline that graphviz would draw in d3?
Turns out I was overthinking this.
If you move the e (endpoint) to the end of the array of points, d3.curveBasis() renders this perfectly.
# G is a networkx graph and A is a pygraphviz graph
for e in G.edges():
points = A.get_edge(e[0], e[1]).attr["pos"].lstrip('e,').split(' ')
points.append(points.pop(0))
G.edges[e[0], e[1]].update({'points': [{'x':float(p.split(',')[0]), 'y':float(p.split(',')[1])} for p in points]})
And later:
var lineGenerator = d3.line().y(d => d.y).x(d => d.x).curve(d3.curveBasis);
link.enter()
.append('path')
.attr('d', l => lineGenerator(l.points))

edge-layout in graphviz for fixed node positions

I was trying to write my own little algorithm for graph layout that only creates a node layout but does not define edge routes. When I use Graphviz to turn the resulting dot file into a graph, the edges are straight lines that cross the nodes and even overlap each other. Is there a way to use Graphviz to layout the edges as nicely as the dot algorithm does, but have the nodes in predetermined fixed positions?
You can see the effect for instance on the following graph:
digraph test {
"a" [pos="0.0,0.0"];
"b" [pos="50.0,50.0"];
"c" [pos="100.0,100.0"];
"a" -> "b";
"a" -> "c";
"b" -> "c";
}
When drawn with dot -Knop -Tpng -otest.png test.dotty the line between a and c crosses b. What I want is that all nodes keep their positions, but the line between a and c goes around b.
Just add:
splines=true;
to your graph - result is:

Resources