VC3D line annotation: adjacent link candidates - #1829
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
12cf3c0 to
2ba0cf5
Compare
A second kind of link: an ADJACENT link ties a V fiber's control point to an H fiber's control point one winding outside it (horizontals lie on the front of the sheet, verticals on the back, so a V fiber showing through to the next wrap out is one sheet thickness from it): W_V = W_H - 1. - "Designate as adjacent link candidate" on a control point; the candidate draws as a green triangle. On another fiber the menu reads "Link with adjacent candidate (...)", and a new line annotation can start linked to it. Any pair may be linked: a fiber's type can change and a new fiber has none yet, so the tags are not checked here. - FiberBranchRef::adjacent, written as `"adjacent": true` on both reciprocal entries (key held in one constant), absent otherwise; the loader reads it, the controller's dedup keeps it, and the cross-file validation does not compare it (a kind mismatch is the sync merge's business). - Adjacent-linked control points draw as triangles in the same link-state colours (blue pending, purple approved) in the cut views, generated views, overview strip and the fiber map; the cut view's X glyphs and connectors are unchanged. - The fiber map hands the winding solver a one-turn offset (LinkInput::windingOffset) instead of an equality and measures the link's turn error against it. An adjacent link whose fibers are not one H and one V has no inside: it is flagged as an error (PlacedLink:: adjacentUnpaired, always suspect, named in the errors list) and carries no constraint. Adjacent links are never kollesis seam evidence. The per-network unroll has no winding dimension and treats every link as an angular tie, as documented. - Tests: the V fiber lands one winding inside; an unpaired adjacent link is an error that constrains nothing; an adjacent link cannot certify a kollesis; the output digest is sensitive to both new flags. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YKBWyFShke85mC653ujChD
`adjacent` is a link's kind, never changed on an existing link, so the refresh mirrors A's value onto B's reciprocal (like the review state) and restores it on a rebuilt reciprocal; a changed kind counts as a modification (a pair re-linked as adjacent survives the other side's deletion of the original); and a three-way merge that meets the same anchor pair as an adjacent link on one side and an ordinary one on the other reports a conflict instead of picking. Five tests. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YKBWyFShke85mC653ujChD
Designating a control point as a link candidate (ordinary or adjacent) after it was a split candidate, or the reverse, left both designations in place; the split marker takes precedence, so the point stayed red however it was re-designated. Each designation now clears the other kind on the same point. Candidates on different points still coexist, as "split from candidate and link" requires. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YKBWyFShke85mC653ujChD
2ba0cf5 to
c3ee637
Compare
|
|
|
The shortcut and refresh paths already preserve absence; the full three-way merge still wrote both arrays unconditionally, so a merge whose saved sides are legacy inserted `adjacent_branches: []`. That turns "a legacy writer saved this, the kind is unknown" into "deliberately no adjacent links", which hides a peer's missing reciprocal from the sync check and stops VC3D's loader restoring it. The array is now left out when the merge produced no entries for it and neither content side carried the key. The base is not consulted: it only arbitrates deletions and contributes no entries, so its own array says nothing about what the two saved files know, and a base with real adjacent entries facing a stripped side is already a hard conflict. An input that knows the kind still yields an explicit empty array. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YKBWyFShke85mC653ujChD
|
Fixed in 8b89042, minimally: the final for kind in BRANCH_ARRAYS:
entries = merged_branches[kind] + opaque_branches[kind]
if not entries and all(kind not in doc for doc in (local, remote)):
continue
merged[kind] = entriesYour case is now One thing while checking it: the condition on all three inputs was not quite enough. If the base carries an empty Also verified on the real code paths:
Worth stating plainly, since it is a user-visible consequence rather than a pure bug fix: auto-merge output can now genuinely lack |
Motivation and behavior
Adjacent fiber links connect a vertical fiber to the horizontal fiber one winding outside it (W_V = W_H − 1). Keeping them in
brancheswould let existing same-winding consumers fuse the two windings. This PR stores them exclusively in a separate top-leveladjacent_branchesarray, using exactly the existing branch-entry schema. There is no per-entryadjacentkey.Current VC3D always writes
adjacent_branches, even when empty. A missing array identifies a legacy writer; a present empty array expresses a deliberate absence of adjacent links. Both reciprocal refs must have the same kind. Before cross-file validation, VC3D restores missing adjacent reciprocals into peers whose array was absent, leaving peers with a present array untouched. The existing concurrent-save guard remains in place.fiber_mergemerges, reanchors and refreshes both arrays through the same branch machinery. A kind change moves an entry between arrays and retains the new link's review state. A base with adjacent entries and a side lacking the array is a conflict, including through whole-document shortcuts and plain vc_sync uploads.The input verification digest now includes
adjacentExplicit(Hendrik’s P2). Changing whether a reciprocal states its kind can change disagreement handling and solver output; those inputs now have different digests, preventing a false cache-mismatch report.The GUI designates adjacent candidates and draws adjacent links as triangles. The fiber map applies the H/V-derived one-turn constraint, excludes adjacent links from kollesis seam evidence, and flags invalid H/V pairs and kind disagreements. The existing candidate re-designation fix is also retained.
The format proposal includes the schema, compatibility rules and verification commands.
Compatibility
branchesand never see adjacent links.adjacent_brancheson load and drops it on save. A surviving reciprocal or synced base lets current software restore or detect that loss.Verification
line_annotation_adjacent_links,fiber_network_layout,fiber_winding_solverandfiber_global_layoutpass.adjacentExplicitproduces different solver outputs and now different input digests. The test failed before the hash fix and passes afterward; the full global-layout suite passes. A digest check using the real PHercParis4 pairdj_20260717T165249423_000001.json/lt_20260715T081231472_000515.json(36 control points, 4,791 line points) likewise failed before and passed after, using in-memory copies.Commands (using the existing Python environment with pytest):
AGENTS_AGENT_MODE=1 python -m pytest -q volume-cartographer/scripts/tests AGENTS_AGENT_MODE=1 cmake --build volume-cartographer/build --target VC3D test_line_annotation_adjacent_links test_fiber_global_layout test_fiber_network_layout test_fiber_winding_solver --parallel 2 AGENTS_AGENT_MODE=1 ctest --test-dir volume-cartographer/build -R '^(line_annotation_adjacent_links|fiber_global_layout|fiber_network_layout|fiber_winding_solver)$' --output-on-failuremacOS and arm64 were not run in this environment. No map, solver or GUI behavior changed in the separate-array revision.