diff --git a/packages/viewer/src/client/renderers/sgcr/layout.ts b/packages/viewer/src/client/renderers/sgcr/layout.ts index 98acc9a..e018715 100644 --- a/packages/viewer/src/client/renderers/sgcr/layout.ts +++ b/packages/viewer/src/client/renderers/sgcr/layout.ts @@ -523,12 +523,14 @@ function assignCoordinates( } // FORCED-EDGE STRAIGHTENING: a node face holding exactly ONE wire has no sibling port to collide - // with, so that port can snap to the neighbour's port (when it falls on this face) and run straight - // — even when the mutual-spine pass above skipped the edge because a back-edge/sibling claimed the - // node's primary spine (e.g. a fork child whose parent's centre column is taken by a reversed back - // edge: its in/out edges would otherwise jog by a few px). Pass-through nodes (1 in / 1 out) get - // both edges straightened. Deterministic (id-sorted); a single-wire face can't create a within-face - // port collision (P5), and the snapped column is one the neighbour already occupies. + // with, so that port snaps toward the neighbour's port — CLAMPED to this node's face — and runs + // straight. This catches edges the mutual-spine pass skipped (a fork child whose parent's centre is + // claimed by a sibling/back-edge), AND the boundary case where the neighbour's port lands a hair + // outside this (narrower / ½-grid-parity-shifted) face: clamping snaps to the nearest on-face point + // (≤ the parity offset away) instead of leaving the full diagonal jog. Pass-through nodes (1 in / + // 1 out) get both edges straightened. Deterministic (id-sorted); a single-wire face can't create a + // within-face port collision (P5), so the snap is always safe. + const PARITY_SLACK = 1; // the ½-grid parity (0.5) + rounding can push a reachable neighbour port a hair past a narrower face for (const it of [...items.values()].sort((p, q) => (p.id < q.id ? -1 : p.id > q.id ? 1 : 0))) { if (it.isDummy || selfLoopCount.get(it.id)) continue; const lo = it.cc - it.crossSize / 2 + o.portInset, hi = it.cc + it.crossSize / 2 - o.portInset; @@ -539,8 +541,8 @@ function assignCoordinates( const otherId = w.aItem === it.id ? w.bItem : w.aItem; if (items.get(otherId)!.isDummy) continue; // keep long-edge chains on their routed columns const np = portOf(w, otherId); // neighbour's port on its own face - if (np < lo - 1e-6 || np > hi + 1e-6) continue; // not reachable on this face → genuinely off to the side - setPort(w, it.id, Math.round(np)); + if (np < lo - PARITY_SLACK || np > hi + PARITY_SLACK) continue; // genuinely off to the side → it must turn + setPort(w, it.id, Math.round(Math.max(lo, Math.min(hi, np)))); // reachable (or a parity-hair outside) → snap, clamped to face } }