rendering arc: the imp node becomes the argument's geometry, not a fake proposition
Owner-approved brief (orchestrator): the canvas stops drawing implication nodes as first-class nodes and starts drawing them as what they are — the point where co-premise lines converge. - deriveView (core/graph.ts): single-premise chains collapse to ONE direct premise->conclusion arrow — no imp node minted (a chain is always N-in/ 1-out; one premise is the degenerate case). Multi-premise chains keep the imp node + fan. Dangling guard unchanged. - physics (core/layout.ts createGraphSimulation): per-link rest length — a link touching an arg_-prefixed endpoint gets HALF the base link distance, default or user-tuned alike; everything else about imp-node physics citizenship unchanged. - canvas (components/graph-canvas.ts): imp nodes are PURE GEOMETRY — invisible (no disc, no label) and unpickable (excluded from pickAt's hit-test projection). Edges INTO an imp node: headless, shaft runs to its CENTER (toRadius 0, no head triangle). Edges OUT: shaft starts at the center (fromRadius 0), arrowhead lands on the conclusion's disc edge. Single-premise direct arrows render as today's plain arrows. - comment repairs (same commit): the two stale double-shaft claims (CanvasEdge doc + the draw-pass block) rewritten to the truth — plain single shafts; bundles are headless converging lines + one arrowed out-edge; no double-shaft glyph exists anywhere. - tests (+9): deriveView pins (single-premise collapse, multi-premise fan, shared-conclusion distinctness, dangling drop); imp-link half- distance settle pins; arrowGeometry zero-radius laws + isImpNodeId pin; graph.test.ts's snapshot invariant now counts only MULTI-premise live chains (its fixture's single-premise chains mint zero imp nodes); layout settle fixture updated for the collapse.
This commit is contained in:
@@ -14,6 +14,7 @@ import { describe, it, it as test } from "node:test";
|
||||
import {
|
||||
applyClusterLabels,
|
||||
arrowGeometry,
|
||||
isImpNodeId,
|
||||
boxBlurRound,
|
||||
fitTransform,
|
||||
labelAlpha,
|
||||
@@ -148,6 +149,26 @@ describe("arrowGeometry", () => {
|
||||
test("null for coincident endpoints (never a NaN arrow)", () => {
|
||||
assert.equal(arrowGeometry({ x: 5, y: 5 }, { x: 5, y: 5 }, 6, 6, 5), null);
|
||||
});
|
||||
|
||||
// The rendering arc's geometry laws (owner, 2026-09-30): an edge INTO
|
||||
// an imp point is drawn with toRadius 0 (headless, terminates at the
|
||||
// center); an edge OUT of one with fromRadius 0 (starts at the center).
|
||||
test("toRadius 0 — the tip lands exactly at the target CENTER (headless convergence)", () => {
|
||||
const g = arrowGeometry({ x: 0, y: 0 }, { x: 100, y: 0 }, 6, 0, 5)!;
|
||||
assert.ok(Math.abs(g.tip.x - 100) < 1e-9 && Math.abs(g.tip.y) < 1e-9, "tip = the center, past which no shaft draws");
|
||||
});
|
||||
|
||||
test("fromRadius 0 — the shaft starts exactly at the source CENTER", () => {
|
||||
const g = arrowGeometry({ x: 0, y: 0 }, { x: 100, y: 0 }, 0, 6, 5)!;
|
||||
assert.ok(Math.abs(g.shaftFrom.x) < 1e-9 && Math.abs(g.shaftFrom.y) < 1e-9, "shaftFrom = the center");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isImpNodeId (the pure-geometry law, rendering arc 2026-09-30)", () => {
|
||||
test("arg_-prefixed ids are geometry; proposition ids are citizens", () => {
|
||||
assert.equal(isImpNodeId("arg_123af5a046ba"), true);
|
||||
assert.equal(isImpNodeId("n_abc123"), false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user