Skip to content

neato splines=polyline: route to a compass port on the node boundary ends 0.1pt off from C's for some node widths

Kind
Port bug
Severity
low
Engine
neato
Category
splines
Cases
0
Seeds
·
Tracking
GitHub issue
Labels
bugfidelityfuzzengine:neato

For an edge routed with splines=polyline (or spline) to a compass port on the head node's boundary ("10":n), the port and C produce different polylines for some node widths, even with identical node positions. In C the path from the tail is slightly bent (x drifts from 29.241 to 29.265) and in the port it is perfectly vertical; with other widths the head end point itself differs by 0.1pt in x (C at the node centre, the port 0.1pt right of it, or the reverse). splines=line agrees. The node positions agree exactly, so the difference is in routing.

Reproducer

digraph "fuzz 12614" {
layout=neato;
splines="polyline";
mode="hier";
"10" [label="Start"];
"9" -> "10":"n" [xlabel="\T", color="white"];
}
GVBINDIR=<pango-free plugins> dot -Kneato -Tsvg repro.gv   # C Graphviz 16.1.0
graphviz-pure -Kneato -Tsvg repro.gv                             # port

C output vs port

--- c
+++ port
@@ -25,5 +25,5 @@
 <title>9&#45;&gt;10:n</title>
-<path fill="none" stroke="white" d="M29.24,-71.76C29.25,-63.74 29.26,-54.56 29.26,-47.55"/>
-<polygon fill="white" stroke="white" points="32.76,-47.81 29.27,-37.81 25.76,-47.81 32.76,-47.81"/>
-<text xml:space="preserve" text-anchor="middle" x="25.75" y="-47.06" font-family="Times,serif" font-size="14.00">9</text>
+<path fill="none" stroke="white" d="M29.28,-71.52C29.28,-63.63 29.28,-54.67 29.28,-47.76"/>
+<polygon fill="white" stroke="white" points="32.78,-47.81 29.28,-37.81 25.78,-47.81 32.78,-47.81"/>
+<text xml:space="preserve" text-anchor="middle" x="25.78" y="-47.04" font-family="Times,serif" font-size="14.00">9</text>
 </g>

Root cause

Narrowed down with -n (fixed positions): head width=0.81 differs, while width=0.75, 0.8, 0.82, 0.83, 0.85, 0.9, 1.0 agree (at 0.83 C itself ends 0.1pt off-centre and the port agrees). Confirmed with lldb on Homebrew's dot (dumping the router_t triangle graph at the first Pshortestpath). The head port lies on the node boundary, so the edge goes through the GTS router (makeMultiSpline, lib/neatogen/multispline.c), not getPath/makePolyline. The two stacked octagon obstacles are mirror images, so the triangle shortest path (triPath) from the head can pass either side of the 9-bottom/10-top diagonal at the same length. C and the port build the same triangles, but GTS returns each face's vertices in a different rotation (gts_triangle_vertices, fixed by GTS's incremental construction behind tri/mkSurface, lib/neatogen/delaunay.c), and triCenter (multispline.c:391) sums them in that order. In C both head edges come out at exactly 20.005238055389295, the first inserted (left) one wins and the guiding polygon is the left sleeve. In the port the right head edge is 1 ulp shorter, so it takes the right sleeve, whose last bend is the obstacle's top corner, and tweakEnd (multispline.c:658) moves the head 0.1pt right. Matching C needs GTS's per-face vertex order (and, for other ties, its pointer-hash face order), which the port's own constrained Delaunay triangulation (mk_surface in crates/graphviz-pure-layout/src/neatogen/multispline.rs) does not reproduce. Regression test (ignored): neato_polyline_compass_port_routes_like_c in crates/graphviz-pure-cli/tests/cli.rs.

Minimal fixed-position reproducer (run with -Kneato -n -Tdot):

digraph G { splines="polyline"; "10" [width=0.81, pos="100,18"]; "9" [pos="100,90"]; "9" -> "10":"n"; }

C: pos="e,29.16,36.482 29.126,71.526 ...", port: pos="e,29.26,36.483 29.026,71.859 ...".

Cases

No fuzz case of its own. This difference appeared while minimising seed 12614. The full seed-12614 input matches C once the record-obstacle fix (neato-router-record-obstacle-height) is applied, but this minimised form still differs with that fix, so it is a separate bug.

Files

tests/fuzz-open/019-neato-polyline-compass-port-endpoint

C and the port

Comparing…
C Graphviz 16.1.0 output, cyan plategraphviz-pure output, magenta plate

C onlyport onlyboth: in register

Reproduce

repro.gv
digraph "fuzz 12614" {
layout=neato;
splines="polyline";
mode="hier";
"10" [label="Start"];
"9" -> "10":"n" [xlabel="\T", color="white"];
}
cmd.txt
C: GVBINDIR=<pango-free plugins> dot -Kneato -Tsvg repro.gv
port: graphviz-pure -Kneato -Tsvg repro.gv
cases.txt
(no fuzz case of its own: found while minimising r2/diff/s12614.gv seed=12614; the full case was fixed by neato-router-record-obstacle-height, #13)

Files: c.svg, cases.txt, cmd.txt, repro.gv, rust.svg. Source: tests/fuzz-open/019-neato-polyline-compass-port-endpoint.

All open issues