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);
}
// 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 mut visited = HashSet::new();
let mut queue = VecDeque::new();
@ -166,7 +166,8 @@ fn main() {
for (i, &idx) in wire_indices.iter().enumerate() {
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_down = reachable_from(idx, &predecessors);
@ -213,12 +214,11 @@ fn main() {
for (i, &idx) in surface_indices.iter().enumerate() {
let point = &pts.elements()[idx];
// Find connected wires (boundary)
let reachable_up = reachable_from(idx, &successors);
let reachable_down = reachable_from(idx, &predecessors);
let mut boundary_wires: Vec<usize> = reachable_up
.union(&reachable_down)
// Find boundary wires via DIRECT covering relations only
// Surface (geom_dim=2) connects to wires (geom_dim=1) via single covers
let mut boundary_wires: Vec<usize> = successors[idx]
.iter()
.chain(predecessors[idx].iter())
.filter(|v| wire_set.contains(v))
.copied()
.collect();

View file

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

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]] }
],
"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": 4, "label": "surface_1", "point": "r1,s0,r0", "coords": [2.0, 1.0, 0.0], "boundary_wires": [5, 15, 19, 20] },
{ "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": 7, "label": "surface_3", "point": "r1,s1,r0", "coords": [2.0, 3.0, 0.0], "boundary_wires": [8, 15, 19, 20] },
{ "id": 11, "label": "surface_4", "point": "r0,s0,r1", "coords": [0.0, 1.0, 2.0], "boundary_wires": [14, 15, 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": 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": 16, "label": "surface_7", "point": "r0,r0,s0", "coords": [0.0, 0.0, 1.0], "boundary_wires": [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, 19] },
{ "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, 19] },
{ "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": [14, 15, 19] },
{ "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": [18] },
{ "id": 17, "label": "surface_8", "point": "r0,r1,s0", "coords": [0.0, 2.0, 1.0], "boundary_wires": [18] }
],
"volumes": [