Plan-graph round 13 (owner): arrowheads point again — no zero-length final segment

Round-12 regression: manually repeating the mouth point as a control
point made d3 emit a degenerate final path command (a zero-length L).
orient="auto" derives the marker angle from the direction of the LAST
path segment; a zero-length segment has none, so every head fell back
to 0° — pointing right. Fix: drop the manual repeat — curveBasis already
repeats the endpoint internally, so controls ending [Q, M] keep the
exact end tangent (the approach direction) AND a real final segment
(the straight Q→M stub). Left a comment in edgePathD so this never
regresses again.
This commit is contained in:
amosblomqvist
2026-09-14 07:56:01 +03:00
parent 847caf48d3
commit d7790f895e
+8 -5
View File
@@ -248,13 +248,16 @@ export function edgePathD(points: Array<{ x: number; y: number }>): string {
// The mouth: tip overhang + the docking gap back from the border.
const back = HEAD_TIP_OVERHANG + DOCK_GAP;
const M = { x: last.x - ux * back, y: last.y - uy * back };
// From Q every control point lies on the approach line — the basis
// From Q the control points lie on the approach line — the basis
// spline renders Q→M perfectly straight (a B-spline span with
// collinear control points IS the line), and the repeated M makes
// the end tangent exactly the approach direction, so the head
// squares onto its shaft.
// collinear control points IS the line), and curveBasis's internal
// endpoint repetition makes the end tangent exactly the approach
// direction. NOTE: do NOT repeat M manually — a duplicated final
// control point renders a zero-length last segment, and
// orient="auto" on a zero-length segment falls back to 0° (every
// head pointing right — the round-12 regression, fixed round 13).
const Q = { x: M.x - ux * STRAIGHT_RUN, y: M.y - uy * STRAIGHT_RUN };
pts.splice(pts.length - 1, 1, Q, M, M);
pts.splice(pts.length - 1, 1, Q, M);
}
return edgeLine(pts) ?? "";
}