Fix render_braiding.rs: transitive for wire endpoints, direct for surface boundaries

Wire endpoints and surface boundaries require different computation methods:

- Wire endpoints: TRANSITIVE reachability to vertices
  A strand spans between vertices even if poset path has intermediate points.
  Most wires connect both v₀ and v₁; only wire 14 is a self-loop.

- Surface boundary_wires: DIRECT covering relations only
  The immediate boundary of a surface is its direct predecessor/successor wires.
  Surfaces have 1-3 boundary wires (was 1-7 with transitive).

Updated:
- examples/render_braiding.rs: restored reachable_from for wire endpoints
- fixtures/half_braid_geometry.json: correct wire endpoints + direct surface boundaries
- web/zigzag-renderer.jsx: updated embedded geometry data

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Maximus Gorog 2026-04-08 01:21:45 -06:00
parent d679eeba6b
commit 2f33791809
3 changed files with 21 additions and 47 deletions

View file

@ -126,7 +126,7 @@ fn main() {
predecessors[upper].push(lower); predecessors[upper].push(lower);
} }
// Helper: find all reachable points // Helper: find all transitively reachable points (for wire→vertex connections)
let reachable_from = |start: usize, adj: &[Vec<usize>]| -> HashSet<usize> { let reachable_from = |start: usize, adj: &[Vec<usize>]| -> HashSet<usize> {
let mut visited = HashSet::new(); let mut visited = HashSet::new();
let mut queue = VecDeque::new(); let mut queue = VecDeque::new();
@ -166,7 +166,8 @@ fn main() {
for (i, &idx) in wire_indices.iter().enumerate() { for (i, &idx) in wire_indices.iter().enumerate() {
let point = &pts.elements()[idx]; let point = &pts.elements()[idx];
// Find connected vertices // Find connected vertices via TRANSITIVE reachability
// A wire (strand) spans between vertices even if the poset path has intermediate points
let reachable_up = reachable_from(idx, &successors); let reachable_up = reachable_from(idx, &successors);
let reachable_down = reachable_from(idx, &predecessors); let reachable_down = reachable_from(idx, &predecessors);
@ -213,12 +214,11 @@ fn main() {
for (i, &idx) in surface_indices.iter().enumerate() { for (i, &idx) in surface_indices.iter().enumerate() {
let point = &pts.elements()[idx]; let point = &pts.elements()[idx];
// Find connected wires (boundary) // Find boundary wires via DIRECT covering relations only
let reachable_up = reachable_from(idx, &successors); // Surface (geom_dim=2) connects to wires (geom_dim=1) via single covers
let reachable_down = reachable_from(idx, &predecessors); let mut boundary_wires: Vec<usize> = successors[idx]
.iter()
let mut boundary_wires: Vec<usize> = reachable_up .chain(predecessors[idx].iter())
.union(&reachable_down)
.filter(|v| wire_set.contains(v)) .filter(|v| wire_set.contains(v))
.copied() .copied()
.collect(); .collect();

View file

@ -223,12 +223,7 @@
], ],
"boundary_wires": [ "boundary_wires": [
5, 5,
8, 18
14,
15,
18,
19,
20
] ]
}, },
{ {
@ -242,9 +237,7 @@
], ],
"boundary_wires": [ "boundary_wires": [
5, 5,
15, 19
19,
20
] ]
}, },
{ {
@ -258,11 +251,7 @@
], ],
"boundary_wires": [ "boundary_wires": [
8, 8,
14, 18
15,
18,
19,
20
] ]
}, },
{ {
@ -276,9 +265,7 @@
], ],
"boundary_wires": [ "boundary_wires": [
8, 8,
15, 19
19,
20
] ]
}, },
{ {
@ -292,7 +279,6 @@
], ],
"boundary_wires": [ "boundary_wires": [
14, 14,
15,
18 18
] ]
}, },
@ -306,11 +292,8 @@
2.0 2.0
], ],
"boundary_wires": [ "boundary_wires": [
5,
8,
14, 14,
15, 15,
18,
19 19
] ]
}, },
@ -324,12 +307,7 @@
2.0 2.0
], ],
"boundary_wires": [ "boundary_wires": [
5,
8,
14,
15, 15,
18,
19,
20 20
] ]
}, },
@ -343,11 +321,7 @@
1.0 1.0
], ],
"boundary_wires": [ "boundary_wires": [
14, 18
15,
18,
19,
20
] ]
}, },
{ {

View file

@ -29,14 +29,14 @@ const GEOMETRY_DATA = {
"endpoints": [21, 22], "endpoint_coords": [[1.0, 1.0, 1.0], [3.0, 1.0, 1.0]] } "endpoints": [21, 22], "endpoint_coords": [[1.0, 1.0, 1.0], [3.0, 1.0, 1.0]] }
], ],
"surfaces": [ "surfaces": [
{ "id": 3, "label": "surface_0", "point": "r0,s0,r0", "coords": [0.0, 1.0, 0.0], "boundary_wires": [5, 8, 14, 15, 18, 19, 20] }, { "id": 3, "label": "surface_0", "point": "r0,s0,r0", "coords": [0.0, 1.0, 0.0], "boundary_wires": [5, 18] },
{ "id": 4, "label": "surface_1", "point": "r1,s0,r0", "coords": [2.0, 1.0, 0.0], "boundary_wires": [5, 15, 19, 20] }, { "id": 4, "label": "surface_1", "point": "r1,s0,r0", "coords": [2.0, 1.0, 0.0], "boundary_wires": [5, 19] },
{ "id": 6, "label": "surface_2", "point": "r0,s1,r0", "coords": [0.0, 3.0, 0.0], "boundary_wires": [8, 14, 15, 18, 19, 20] }, { "id": 6, "label": "surface_2", "point": "r0,s1,r0", "coords": [0.0, 3.0, 0.0], "boundary_wires": [8, 18] },
{ "id": 7, "label": "surface_3", "point": "r1,s1,r0", "coords": [2.0, 3.0, 0.0], "boundary_wires": [8, 15, 19, 20] }, { "id": 7, "label": "surface_3", "point": "r1,s1,r0", "coords": [2.0, 3.0, 0.0], "boundary_wires": [8, 19] },
{ "id": 11, "label": "surface_4", "point": "r0,s0,r1", "coords": [0.0, 1.0, 2.0], "boundary_wires": [14, 15, 18] }, { "id": 11, "label": "surface_4", "point": "r0,s0,r1", "coords": [0.0, 1.0, 2.0], "boundary_wires": [14, 18] },
{ "id": 12, "label": "surface_5", "point": "r1,s0,r1", "coords": [2.0, 1.0, 2.0], "boundary_wires": [5, 8, 14, 15, 18, 19] }, { "id": 12, "label": "surface_5", "point": "r1,s0,r1", "coords": [2.0, 1.0, 2.0], "boundary_wires": [14, 15, 19] },
{ "id": 13, "label": "surface_6", "point": "r2,s0,r1", "coords": [4.0, 1.0, 2.0], "boundary_wires": [5, 8, 14, 15, 18, 19, 20] }, { "id": 13, "label": "surface_6", "point": "r2,s0,r1", "coords": [4.0, 1.0, 2.0], "boundary_wires": [15, 20] },
{ "id": 16, "label": "surface_7", "point": "r0,r0,s0", "coords": [0.0, 0.0, 1.0], "boundary_wires": [14, 15, 18, 19, 20] }, { "id": 16, "label": "surface_7", "point": "r0,r0,s0", "coords": [0.0, 0.0, 1.0], "boundary_wires": [18] },
{ "id": 17, "label": "surface_8", "point": "r0,r1,s0", "coords": [0.0, 2.0, 1.0], "boundary_wires": [18] } { "id": 17, "label": "surface_8", "point": "r0,r1,s0", "coords": [0.0, 2.0, 1.0], "boundary_wires": [18] }
], ],
"volumes": [ "volumes": [