zigzag-engine/examples/trace_scaffold.rs
Maximus Gorog c51e3274f9 Stage 2 complete: Construction 17 validated on real 3D data
Zigzag engine (6802 lines, 184 tests):
- Construction 17 normalisation: working through dimension 3+
- Import from homotopy-rs JSON: working (scalar, two_scalars, half_braid)
- Piece extraction via Embedding/restrict_diagram: working
- Type checking pipeline: working (Eckmann-Hilton half_braid passes)
- Essential identity detection: validated with full 2-diagram test

Bugs found and fixed:
- assemble_factorisations losing cospan legs during reassembly
- RewriteN::slice() using source offsets instead of target indices
- singular_preimage() not handling passthrough heights
- restrict_rewrite() not accounting for accumulated cone offsets
- Embedding::preimage() using regular_preimage for Singular case

Added vis-engine-spec.md: visualization engine specification
- 6-layer architecture from math primitives to scene graph
- SVG renderer for 2D, WebGL2 for 3D, custom hit testing
- Spring constraint integration point for semiotic rendering
- No external dependencies - game engine approach

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-09 05:26:15 -06:00

115 lines
4.6 KiB
Rust

//! Trace scaffold nodes for visible wires through all time heights
//!
//! Run with: cargo run --example trace_scaffold
use std::fs;
use zigzag_engine::diagram::Diagram;
use zigzag_engine::import::load_homotopy_diagram_n;
fn main() {
let json = fs::read_to_string("fixtures/half_braid.json")
.expect("Failed to read half_braid.json");
let half_braid = load_homotopy_diagram_n(&json)
.expect("Failed to parse");
println!("=== SCAFFOLD NODE TRACING ===\n");
// Time structure of the 3-diagram
println!("TIME STRUCTURE (coord[2]):");
println!(" The half_braid has {} cospan(s)", half_braid.cospans.len());
println!(" Time heights: r0 (source), s0 (merge), r1 (target)");
println!();
// Source 2-diagram heights
if let Diagram::DiagramN(src) = half_braid.source() {
println!("SOURCE 2-DIAGRAM (at time r0):");
println!(" {} cospans → heights: r0, s0, r1, s1, r2", src.cospans.len());
println!(" Y-mapping: r0→-1, s0→0, r1→0, s1→1, r2→+1");
println!();
}
// Target 2-diagram heights
let target = half_braid.target();
if let Diagram::DiagramN(tgt) = &target {
println!("TARGET 2-DIAGRAM (at time r1):");
println!(" {} cospan(s) → heights: r0, s0, r1", tgt.cospans.len());
println!(" Y-mapping: r0→-1, s0→0, r1→0");
println!();
}
println!("=== HEIGHT MAPPING THROUGH MERGE ===");
println!();
println!("The merge contracts source heights to target heights:");
println!(" Source r0 → Target r0 (preserved, y stays at -1)");
println!(" Source s0 → Target s0 (merges, y=0 → y=0)");
println!(" Source r1 → Target s0 (absorbed into merge, y=0 → y=0)");
println!(" Source s1 → Target s0 (merges, y=1 → y=0)");
println!(" Source r2 → Target r1 (contracts down, y=+1 → y=0)");
println!();
println!("=== SCAFFOLD NODE POSITIONS FOR EACH WIRE ===");
println!();
println!("Time positions: r0 at x=-1, s0 at x=0, r1 at x=+1");
println!("Depth positions: r0→z=-1, r1→z=0, r2→z=+1");
println!();
// Wire r0
println!("WIRE r0 (coord[0]=r0, depth z=-1):");
println!(" At time r0 (source): coord=(r0, r0, r0)");
println!(" → coord[1]=r0=Regular(0) → y = -1");
println!(" → position: (-1, -1, -1)");
println!();
println!(" At time s0 (merge): coord=(r0, s0, s0)");
println!(" → coord[1]=s0=Singular(0) → y = 0");
println!(" → position: (0, 0, -1)");
println!();
println!(" At time r1 (target): coord=(r0, r0, r1)");
println!(" → coord[1]=r0=Regular(0) → y = -1");
println!(" → position: (+1, -1, -1)");
println!();
println!(" Wire r0 polyline: [(-1,-1,-1), (0,0,-1), (+1,-1,-1)]");
println!(" Shape: DIPS to merge, returns to original height");
println!();
// Wire r1
println!("WIRE r1 (coord[0]=r1, depth z=0):");
println!(" At time r0 (source): coord=(r1, r1, r0)");
println!(" → coord[1]=r1=Regular(1) → y = 0");
println!(" → position: (-1, 0, 0)");
println!();
println!(" At time s0 (merge): coord=(r1, s0, s0)");
println!(" → coord[1]=s0=Singular(0) → y = 0");
println!(" → position: (0, 0, 0)");
println!();
println!(" At time r1 (target): coord=(r1, s0, r1)");
println!(" → coord[1]=s0=Singular(0) → y = 0 (r1 absorbed into s0)");
println!(" → position: (+1, 0, 0)");
println!();
println!(" Wire r1 polyline: [(-1,0,0), (0,0,0), (+1,0,0)]");
println!(" Shape: FLAT at y=0 throughout - this is the STEM");
println!();
// Wire r2
println!("WIRE r2 (coord[0]=r2, depth z=+1):");
println!(" At time r0 (source): coord=(r2, r2, r0)");
println!(" → coord[1]=r2=Regular(2) → y = +1");
println!(" → position: (-1, +1, +1)");
println!();
println!(" At time s0 (merge): coord=(r2, s0, s0)");
println!(" → coord[1]=s0=Singular(0) → y = 0");
println!(" → position: (0, 0, +1)");
println!();
println!(" At time r1 (target): coord=(r2, r1, r1)");
println!(" → coord[1]=r1=Regular(1) → y = 0 (r2 contracted to r1)");
println!(" → position: (+1, 0, +1)");
println!();
println!(" Wire r2 polyline: [(-1,+1,+1), (0,0,+1), (+1,0,+1)]");
println!(" Shape: DROPS from y=+1 to y=0, stays at y=0");
println!();
println!("=== SUMMARY ===");
println!();
println!("Wire r0: [(-1,-1,-1), (0,0,-1), (1,-1,-1)] // dips and returns");
println!("Wire r1: [(-1, 0, 0), (0,0, 0), (1, 0, 0)] // flat stem");
println!("Wire r2: [(-1,+1,+1), (0,0,+1), (1, 0,+1)] // drops and stays");
}