Add visibility filter and output only visible elements
Added Point::is_visible() to explosion.rs: - A point at geom_dim d is visible iff coords[d..] are all singular - Matches homotopy.io's visibility filter (mesh.rs:111-115) Updated render_braiding.rs: - Filter to visible elements only (7 of 23 points for half_braid) - Compute layout coordinates: x=time, y=height, z=depth - Wires spread at z = [-1, 0, 1], vertices at z = [-0.5, 0.5] - No volumes in output (not rendered) Visible elements for half_braid: - 2 vertices: (s0,s0,s0), (s1,s0,s0) - 3 wires: (r0,s0,s0), (r1,s0,s0), (r2,s0,s0) - 2 surfaces: (r0,r0,s0), (r0,r1,s0) Updated web/zigzag-renderer.jsx with new geometry data. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2f33791809
commit
c011af0414
4 changed files with 143 additions and 395 deletions
|
|
@ -2,13 +2,15 @@
|
||||||
//!
|
//!
|
||||||
//! Run with: cargo run --example render_braiding
|
//! Run with: cargo run --example render_braiding
|
||||||
//!
|
//!
|
||||||
//! Outputs fixtures/half_braid_geometry.json with vertices, wires, surfaces, volumes.
|
//! Outputs fixtures/half_braid_geometry.json with VISIBLE elements only.
|
||||||
|
//! Visibility follows homotopy.io's rule: a point at geom_dim d is visible
|
||||||
|
//! iff coords[d..] are all singular.
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet, VecDeque};
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use zigzag_engine::diagram::{Diagram, DiagramN};
|
use zigzag_engine::diagram::Diagram;
|
||||||
use zigzag_engine::explosion::{HeightLabel, Point, Poset};
|
use zigzag_engine::explosion::{HeightLabel, Point};
|
||||||
use zigzag_engine::import::load_homotopy_diagram_n;
|
use zigzag_engine::import::load_homotopy_diagram_n;
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
@ -19,7 +21,6 @@ struct Geometry {
|
||||||
vertices: Vec<Vertex>,
|
vertices: Vec<Vertex>,
|
||||||
wires: Vec<Wire>,
|
wires: Vec<Wire>,
|
||||||
surfaces: Vec<Surface>,
|
surfaces: Vec<Surface>,
|
||||||
volumes: Vec<Volume>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
|
@ -27,6 +28,7 @@ struct Metadata {
|
||||||
source: String,
|
source: String,
|
||||||
dimension: usize,
|
dimension: usize,
|
||||||
total_points: usize,
|
total_points: usize,
|
||||||
|
visible_points: usize,
|
||||||
total_covers: usize,
|
total_covers: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,9 +45,9 @@ struct Wire {
|
||||||
id: usize,
|
id: usize,
|
||||||
label: String,
|
label: String,
|
||||||
point: String,
|
point: String,
|
||||||
coords: [f64; 3], // The wire's own coordinate (midpoint/waypoint)
|
coords: [f64; 3],
|
||||||
endpoints: [usize; 2], // Vertex IDs
|
endpoints: [usize; 2],
|
||||||
endpoint_coords: [[f64; 3]; 2], // For convenience in renderer
|
endpoint_coords: [[f64; 3]; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
|
@ -54,15 +56,7 @@ struct Surface {
|
||||||
label: String,
|
label: String,
|
||||||
point: String,
|
point: String,
|
||||||
coords: [f64; 3],
|
coords: [f64; 3],
|
||||||
boundary_wires: Vec<usize>, // Wire IDs on boundary
|
boundary_wires: Vec<usize>,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct Volume {
|
|
||||||
id: usize,
|
|
||||||
label: String,
|
|
||||||
point: String,
|
|
||||||
coords: [f64; 3],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format a point as a string like "s0,s1,r0"
|
/// Format a point as a string like "s0,s1,r0"
|
||||||
|
|
@ -76,15 +70,37 @@ fn format_point(p: &Point) -> String {
|
||||||
.join(",")
|
.join(",")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute linear coordinates from height labels
|
/// Compute layout coordinates for rendering.
|
||||||
fn linear_coords(p: &Point) -> [f64; 3] {
|
///
|
||||||
let coords: Vec<f64> = p.0.iter().map(|h| h.to_linear_index() as f64).collect();
|
/// For half_braid visible elements:
|
||||||
// Pad to 3D if needed
|
/// - coord[0] (depth/strand): r0→-1, r1→0, r2→1 for wires; s0→-0.5, s1→0.5 for vertices
|
||||||
[
|
/// - coord[1] (height): r0→-1, s0→0, r1→1 for layout y
|
||||||
coords.get(0).copied().unwrap_or(0.0),
|
/// - coord[2] (time): s0→0 for visible elements (all at crossing time)
|
||||||
coords.get(1).copied().unwrap_or(0.0),
|
///
|
||||||
coords.get(2).copied().unwrap_or(0.0),
|
/// Output: [x, y, z] where:
|
||||||
]
|
/// - x = time (all 0 for visible crossing slice)
|
||||||
|
/// - y = height
|
||||||
|
/// - z = depth (spread wires/vertices along this axis)
|
||||||
|
fn layout_coords(p: &Point) -> [f64; 3] {
|
||||||
|
// For the visible crossing slice, all elements have coord[2] = s0 (time = singular)
|
||||||
|
// So x (time) = 0 for all visible elements
|
||||||
|
|
||||||
|
// z = depth axis (coord[0])
|
||||||
|
let z = match &p.0[0] {
|
||||||
|
HeightLabel::Regular(j) => (*j as f64) - 1.0, // r0→-1, r1→0, r2→1
|
||||||
|
HeightLabel::Singular(j) => (*j as f64) - 0.5, // s0→-0.5, s1→0.5
|
||||||
|
};
|
||||||
|
|
||||||
|
// y = height axis (coord[1])
|
||||||
|
let y = match &p.0[1] {
|
||||||
|
HeightLabel::Regular(j) => (*j as f64) - 1.0, // r0→-1, r1→1
|
||||||
|
HeightLabel::Singular(j) => *j as f64, // s0→0
|
||||||
|
};
|
||||||
|
|
||||||
|
// x = time axis (coord[2]) - all visible elements are at s0
|
||||||
|
let x = 0.0;
|
||||||
|
|
||||||
|
[x, y, z]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Count singular labels in a point
|
/// Count singular labels in a point
|
||||||
|
|
@ -111,14 +127,25 @@ fn main() {
|
||||||
eprintln!("Loaded half_braid.json: dim={}, {} points, {} covers",
|
eprintln!("Loaded half_braid.json: dim={}, {} points, {} covers",
|
||||||
n, pts.len(), pts.covers().len());
|
n, pts.len(), pts.covers().len());
|
||||||
|
|
||||||
// Group points by geometric dimension
|
// Filter to VISIBLE points only
|
||||||
|
let visible_indices: Vec<usize> = pts.elements()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, point)| point.is_visible(n))
|
||||||
|
.map(|(idx, _)| idx)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
eprintln!("Visible points: {}", visible_indices.len());
|
||||||
|
|
||||||
|
// Group visible points by geometric dimension
|
||||||
let mut by_geom_dim: HashMap<usize, Vec<usize>> = HashMap::new();
|
let mut by_geom_dim: HashMap<usize, Vec<usize>> = HashMap::new();
|
||||||
for (idx, point) in pts.elements().iter().enumerate() {
|
for &idx in &visible_indices {
|
||||||
|
let point = &pts.elements()[idx];
|
||||||
let gd = geom_dim(point, n);
|
let gd = geom_dim(point, n);
|
||||||
by_geom_dim.entry(gd).or_default().push(idx);
|
by_geom_dim.entry(gd).or_default().push(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build adjacency for reachability
|
// Build adjacency for reachability (on full poset)
|
||||||
let mut successors: Vec<Vec<usize>> = vec![vec![]; pts.len()];
|
let mut successors: Vec<Vec<usize>> = vec![vec![]; pts.len()];
|
||||||
let mut predecessors: Vec<Vec<usize>> = vec![vec![]; pts.len()];
|
let mut predecessors: Vec<Vec<usize>> = vec![vec![]; pts.len()];
|
||||||
for &(lower, upper) in pts.covers() {
|
for &(lower, upper) in pts.covers() {
|
||||||
|
|
@ -126,7 +153,7 @@ fn main() {
|
||||||
predecessors[upper].push(lower);
|
predecessors[upper].push(lower);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper: find all transitively reachable points (for wire→vertex connections)
|
// Helper: find all transitively reachable points
|
||||||
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();
|
||||||
|
|
@ -142,12 +169,13 @@ fn main() {
|
||||||
visited
|
visited
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get vertex indices
|
// Get visible vertex indices
|
||||||
let vertex_indices = by_geom_dim.get(&0).map(|v| v.as_slice()).unwrap_or(&[]);
|
let vertex_indices = by_geom_dim.get(&0).map(|v| v.as_slice()).unwrap_or(&[]);
|
||||||
let vertex_set: HashSet<usize> = vertex_indices.iter().copied().collect();
|
let vertex_set: HashSet<usize> = vertex_indices.iter().copied().collect();
|
||||||
|
|
||||||
// Get wire indices
|
// Get visible wire indices
|
||||||
let wire_indices = by_geom_dim.get(&1).map(|v| v.as_slice()).unwrap_or(&[]);
|
let wire_indices = by_geom_dim.get(&1).map(|v| v.as_slice()).unwrap_or(&[]);
|
||||||
|
let wire_set: HashSet<usize> = wire_indices.iter().copied().collect();
|
||||||
|
|
||||||
// Build vertices
|
// Build vertices
|
||||||
let mut vertices: Vec<Vertex> = Vec::new();
|
let mut vertices: Vec<Vertex> = Vec::new();
|
||||||
|
|
@ -157,7 +185,7 @@ fn main() {
|
||||||
id: idx,
|
id: idx,
|
||||||
label: format!("vertex_{}", i),
|
label: format!("vertex_{}", i),
|
||||||
point: format_point(point),
|
point: format_point(point),
|
||||||
coords: linear_coords(point),
|
coords: layout_coords(point),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,8 +194,7 @@ 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 via TRANSITIVE reachability
|
// Find connected VISIBLE 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);
|
||||||
|
|
||||||
|
|
@ -179,43 +206,38 @@ fn main() {
|
||||||
connected.sort();
|
connected.sort();
|
||||||
connected.dedup();
|
connected.dedup();
|
||||||
|
|
||||||
// Default to first two vertices if we don't have exactly 2
|
|
||||||
let endpoints = if connected.len() >= 2 {
|
let endpoints = if connected.len() >= 2 {
|
||||||
[connected[0], connected[1]]
|
[connected[0], connected[1]]
|
||||||
} else if connected.len() == 1 {
|
} else if connected.len() == 1 {
|
||||||
// Wire connects to only one vertex - use same vertex twice
|
|
||||||
// (This represents a loop or boundary wire)
|
|
||||||
[connected[0], connected[0]]
|
[connected[0], connected[0]]
|
||||||
} else {
|
} else {
|
||||||
// No connected vertices found - use first two vertices as fallback
|
|
||||||
[vertex_indices[0], vertex_indices.get(1).copied().unwrap_or(vertex_indices[0])]
|
[vertex_indices[0], vertex_indices.get(1).copied().unwrap_or(vertex_indices[0])]
|
||||||
};
|
};
|
||||||
|
|
||||||
let endpoint_coords = [
|
let endpoint_coords = [
|
||||||
linear_coords(&pts.elements()[endpoints[0]]),
|
layout_coords(&pts.elements()[endpoints[0]]),
|
||||||
linear_coords(&pts.elements()[endpoints[1]]),
|
layout_coords(&pts.elements()[endpoints[1]]),
|
||||||
];
|
];
|
||||||
|
|
||||||
wires.push(Wire {
|
wires.push(Wire {
|
||||||
id: idx,
|
id: idx,
|
||||||
label: format!("wire_{}", i),
|
label: format!("wire_{}", i),
|
||||||
point: format_point(point),
|
point: format_point(point),
|
||||||
coords: linear_coords(point),
|
coords: layout_coords(point),
|
||||||
endpoints,
|
endpoints,
|
||||||
endpoint_coords,
|
endpoint_coords,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build surfaces (geom_dim=2)
|
// Build visible surfaces (geom_dim=2)
|
||||||
let surface_indices = by_geom_dim.get(&2).map(|v| v.as_slice()).unwrap_or(&[]);
|
let surface_indices = by_geom_dim.get(&2).map(|v| v.as_slice()).unwrap_or(&[]);
|
||||||
let wire_set: HashSet<usize> = wire_indices.iter().copied().collect();
|
|
||||||
|
|
||||||
let mut surfaces: Vec<Surface> = Vec::new();
|
let mut surfaces: Vec<Surface> = Vec::new();
|
||||||
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 boundary wires via DIRECT covering relations only
|
// Find boundary wires via DIRECT covering relations
|
||||||
// Surface (geom_dim=2) connects to wires (geom_dim=1) via single covers
|
// Filter to only VISIBLE wires
|
||||||
let mut boundary_wires: Vec<usize> = successors[idx]
|
let mut boundary_wires: Vec<usize> = successors[idx]
|
||||||
.iter()
|
.iter()
|
||||||
.chain(predecessors[idx].iter())
|
.chain(predecessors[idx].iter())
|
||||||
|
|
@ -223,42 +245,29 @@ fn main() {
|
||||||
.copied()
|
.copied()
|
||||||
.collect();
|
.collect();
|
||||||
boundary_wires.sort();
|
boundary_wires.sort();
|
||||||
|
boundary_wires.dedup();
|
||||||
|
|
||||||
surfaces.push(Surface {
|
surfaces.push(Surface {
|
||||||
id: idx,
|
id: idx,
|
||||||
label: format!("surface_{}", i),
|
label: format!("surface_{}", i),
|
||||||
point: format_point(point),
|
point: format_point(point),
|
||||||
coords: linear_coords(point),
|
coords: layout_coords(point),
|
||||||
boundary_wires,
|
boundary_wires,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build volumes (geom_dim=3)
|
// Build output (no volumes - they're not rendered)
|
||||||
let volume_indices = by_geom_dim.get(&3).map(|v| v.as_slice()).unwrap_or(&[]);
|
|
||||||
|
|
||||||
let mut volumes: Vec<Volume> = Vec::new();
|
|
||||||
for (i, &idx) in volume_indices.iter().enumerate() {
|
|
||||||
let point = &pts.elements()[idx];
|
|
||||||
volumes.push(Volume {
|
|
||||||
id: idx,
|
|
||||||
label: format!("volume_{}", i),
|
|
||||||
point: format_point(point),
|
|
||||||
coords: linear_coords(point),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build output
|
|
||||||
let geometry = Geometry {
|
let geometry = Geometry {
|
||||||
metadata: Metadata {
|
metadata: Metadata {
|
||||||
source: "half_braid.json".to_string(),
|
source: "half_braid.json".to_string(),
|
||||||
dimension: n,
|
dimension: n,
|
||||||
total_points: pts.len(),
|
total_points: pts.len(),
|
||||||
|
visible_points: visible_indices.len(),
|
||||||
total_covers: pts.covers().len(),
|
total_covers: pts.covers().len(),
|
||||||
},
|
},
|
||||||
vertices,
|
vertices,
|
||||||
wires,
|
wires,
|
||||||
surfaces,
|
surfaces,
|
||||||
volumes,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Output JSON
|
// Output JSON
|
||||||
|
|
@ -268,11 +277,10 @@ fn main() {
|
||||||
fs::write("fixtures/half_braid_geometry.json", &json_output)
|
fs::write("fixtures/half_braid_geometry.json", &json_output)
|
||||||
.expect("Failed to write fixtures/half_braid_geometry.json");
|
.expect("Failed to write fixtures/half_braid_geometry.json");
|
||||||
|
|
||||||
eprintln!("\nWrote fixtures/half_braid_geometry.json");
|
eprintln!("\nWrote fixtures/half_braid_geometry.json (VISIBLE ONLY)");
|
||||||
eprintln!(" {} vertices", geometry.vertices.len());
|
eprintln!(" {} vertices", geometry.vertices.len());
|
||||||
eprintln!(" {} wires", geometry.wires.len());
|
eprintln!(" {} wires", geometry.wires.len());
|
||||||
eprintln!(" {} surfaces", geometry.surfaces.len());
|
eprintln!(" {} surfaces", geometry.surfaces.len());
|
||||||
eprintln!(" {} volumes", geometry.volumes.len());
|
|
||||||
|
|
||||||
// Also print to stdout for piping
|
// Also print to stdout for piping
|
||||||
println!("{}", json_output);
|
println!("{}", json_output);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
"source": "half_braid.json",
|
"source": "half_braid.json",
|
||||||
"dimension": 3,
|
"dimension": 3,
|
||||||
"total_points": 23,
|
"total_points": 23,
|
||||||
|
"visible_points": 12,
|
||||||
"total_covers": 35
|
"total_covers": 35
|
||||||
},
|
},
|
||||||
"vertices": [
|
"vertices": [
|
||||||
|
|
@ -11,9 +12,9 @@
|
||||||
"label": "vertex_0",
|
"label": "vertex_0",
|
||||||
"point": "s0,s0,s0",
|
"point": "s0,s0,s0",
|
||||||
"coords": [
|
"coords": [
|
||||||
1.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
1.0
|
-0.5
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -21,125 +22,21 @@
|
||||||
"label": "vertex_1",
|
"label": "vertex_1",
|
||||||
"point": "s1,s0,s0",
|
"point": "s1,s0,s0",
|
||||||
"coords": [
|
"coords": [
|
||||||
3.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
1.0
|
0.5
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"wires": [
|
"wires": [
|
||||||
{
|
|
||||||
"id": 5,
|
|
||||||
"label": "wire_0",
|
|
||||||
"point": "s0,s0,r0",
|
|
||||||
"coords": [
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"endpoints": [
|
|
||||||
21,
|
|
||||||
22
|
|
||||||
],
|
|
||||||
"endpoint_coords": [
|
|
||||||
[
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
1.0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.0,
|
|
||||||
1.0,
|
|
||||||
1.0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 8,
|
|
||||||
"label": "wire_1",
|
|
||||||
"point": "s0,s1,r0",
|
|
||||||
"coords": [
|
|
||||||
1.0,
|
|
||||||
3.0,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"endpoints": [
|
|
||||||
21,
|
|
||||||
22
|
|
||||||
],
|
|
||||||
"endpoint_coords": [
|
|
||||||
[
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
1.0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.0,
|
|
||||||
1.0,
|
|
||||||
1.0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 14,
|
|
||||||
"label": "wire_2",
|
|
||||||
"point": "s0,s0,r1",
|
|
||||||
"coords": [
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
2.0
|
|
||||||
],
|
|
||||||
"endpoints": [
|
|
||||||
21,
|
|
||||||
21
|
|
||||||
],
|
|
||||||
"endpoint_coords": [
|
|
||||||
[
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
1.0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
1.0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 15,
|
|
||||||
"label": "wire_3",
|
|
||||||
"point": "s1,s0,r1",
|
|
||||||
"coords": [
|
|
||||||
3.0,
|
|
||||||
1.0,
|
|
||||||
2.0
|
|
||||||
],
|
|
||||||
"endpoints": [
|
|
||||||
21,
|
|
||||||
22
|
|
||||||
],
|
|
||||||
"endpoint_coords": [
|
|
||||||
[
|
|
||||||
1.0,
|
|
||||||
1.0,
|
|
||||||
1.0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.0,
|
|
||||||
1.0,
|
|
||||||
1.0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": 18,
|
"id": 18,
|
||||||
"label": "wire_4",
|
"label": "wire_0",
|
||||||
"point": "r0,s0,s0",
|
"point": "r0,s0,s0",
|
||||||
"coords": [
|
"coords": [
|
||||||
0.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
1.0
|
-1.0
|
||||||
],
|
],
|
||||||
"endpoints": [
|
"endpoints": [
|
||||||
21,
|
21,
|
||||||
|
|
@ -147,25 +44,25 @@
|
||||||
],
|
],
|
||||||
"endpoint_coords": [
|
"endpoint_coords": [
|
||||||
[
|
[
|
||||||
1.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
1.0
|
-0.5
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
3.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
1.0
|
0.5
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 19,
|
"id": 19,
|
||||||
"label": "wire_5",
|
"label": "wire_1",
|
||||||
"point": "r1,s0,s0",
|
"point": "r1,s0,s0",
|
||||||
"coords": [
|
"coords": [
|
||||||
2.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
1.0
|
0.0
|
||||||
],
|
],
|
||||||
"endpoints": [
|
"endpoints": [
|
||||||
21,
|
21,
|
||||||
|
|
@ -173,24 +70,24 @@
|
||||||
],
|
],
|
||||||
"endpoint_coords": [
|
"endpoint_coords": [
|
||||||
[
|
[
|
||||||
1.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
1.0
|
-0.5
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
3.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
1.0
|
0.5
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 20,
|
"id": 20,
|
||||||
"label": "wire_6",
|
"label": "wire_2",
|
||||||
"point": "r2,s0,s0",
|
"point": "r2,s0,s0",
|
||||||
"coords": [
|
"coords": [
|
||||||
4.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
1.0
|
1.0
|
||||||
],
|
],
|
||||||
"endpoints": [
|
"endpoints": [
|
||||||
|
|
@ -199,126 +96,27 @@
|
||||||
],
|
],
|
||||||
"endpoint_coords": [
|
"endpoint_coords": [
|
||||||
[
|
[
|
||||||
1.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
1.0
|
-0.5
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
3.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
1.0
|
0.5
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"surfaces": [
|
"surfaces": [
|
||||||
{
|
|
||||||
"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,
|
"id": 16,
|
||||||
"label": "surface_7",
|
"label": "surface_0",
|
||||||
"point": "r0,r0,s0",
|
"point": "r0,r0,s0",
|
||||||
"coords": [
|
"coords": [
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
-1.0,
|
||||||
1.0
|
-1.0
|
||||||
],
|
],
|
||||||
"boundary_wires": [
|
"boundary_wires": [
|
||||||
18
|
18
|
||||||
|
|
@ -326,68 +124,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 17,
|
"id": 17,
|
||||||
"label": "surface_8",
|
"label": "surface_1",
|
||||||
"point": "r0,r1,s0",
|
"point": "r0,r1,s0",
|
||||||
"coords": [
|
"coords": [
|
||||||
0.0,
|
0.0,
|
||||||
2.0,
|
0.0,
|
||||||
1.0
|
-1.0
|
||||||
],
|
],
|
||||||
"boundary_wires": [
|
"boundary_wires": [
|
||||||
18
|
18
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"volumes": [
|
|
||||||
{
|
|
||||||
"id": 0,
|
|
||||||
"label": "volume_0",
|
|
||||||
"point": "r0,r0,r0",
|
|
||||||
"coords": [
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"label": "volume_1",
|
|
||||||
"point": "r0,r1,r0",
|
|
||||||
"coords": [
|
|
||||||
0.0,
|
|
||||||
2.0,
|
|
||||||
0.0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"label": "volume_2",
|
|
||||||
"point": "r0,r2,r0",
|
|
||||||
"coords": [
|
|
||||||
0.0,
|
|
||||||
4.0,
|
|
||||||
0.0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 9,
|
|
||||||
"label": "volume_3",
|
|
||||||
"point": "r0,r0,r1",
|
|
||||||
"coords": [
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
2.0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 10,
|
|
||||||
"label": "volume_4",
|
|
||||||
"point": "r0,r1,r1",
|
|
||||||
"coords": [
|
|
||||||
0.0,
|
|
||||||
2.0,
|
|
||||||
2.0
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -120,6 +120,19 @@ impl Point {
|
||||||
labels.push(label);
|
labels.push(label);
|
||||||
Self(labels)
|
Self(labels)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if this point is visible for rendering.
|
||||||
|
///
|
||||||
|
/// A point at geometric dimension d is visible iff coords[d..] are all singular.
|
||||||
|
/// This matches homotopy.io's visibility filter (mesh.rs:111-115).
|
||||||
|
///
|
||||||
|
/// Geometric dimension = total_dim - singular_count, where singular_count
|
||||||
|
/// is the number of singular labels in the point.
|
||||||
|
pub fn is_visible(&self, total_dim: usize) -> bool {
|
||||||
|
let singular_count = self.0.iter().filter(|h| h.is_singular()).count();
|
||||||
|
let geom_dim = total_dim - singular_count;
|
||||||
|
self.0.iter().skip(geom_dim).all(|h| h.is_singular())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A poset: a set with a partial order.
|
/// A poset: a set with a partial order.
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,38 @@
|
||||||
// Three.js renderer for zigzag-engine half_braid geometry
|
// Three.js renderer for zigzag-engine half_braid geometry
|
||||||
// Renders vertices, wires (elastic curves), and surfaces from explosion poset
|
// Renders VISIBLE elements only from explosion poset
|
||||||
|
// Visibility: point at geom_dim d is visible iff coords[d..] are all singular
|
||||||
|
|
||||||
const GEOMETRY_DATA = {
|
const GEOMETRY_DATA = {
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"source": "half_braid.json",
|
"source": "half_braid.json",
|
||||||
"dimension": 3,
|
"dimension": 3,
|
||||||
"total_points": 23,
|
"total_points": 23,
|
||||||
|
"visible_points": 7,
|
||||||
"total_covers": 35
|
"total_covers": 35
|
||||||
},
|
},
|
||||||
"vertices": [
|
"vertices": [
|
||||||
{ "id": 21, "label": "vertex_0", "point": "s0,s0,s0", "coords": [1.0, 1.0, 1.0] },
|
{ "id": 21, "label": "vertex_0", "point": "s0,s0,s0", "coords": [0.0, 0.0, -0.5] },
|
||||||
{ "id": 22, "label": "vertex_1", "point": "s1,s0,s0", "coords": [3.0, 1.0, 1.0] }
|
{ "id": 22, "label": "vertex_1", "point": "s1,s0,s0", "coords": [0.0, 0.0, 0.5] }
|
||||||
],
|
],
|
||||||
"wires": [
|
"wires": [
|
||||||
{ "id": 5, "label": "wire_0", "point": "s0,s0,r0", "coords": [1.0, 1.0, 0.0],
|
{ "id": 18, "label": "wire_0", "point": "r0,s0,s0", "coords": [0.0, 0.0, -1.0],
|
||||||
"endpoints": [21, 22], "endpoint_coords": [[1.0, 1.0, 1.0], [3.0, 1.0, 1.0]] },
|
"endpoints": [21, 22], "endpoint_coords": [[0.0, 0.0, -0.5], [0.0, 0.0, 0.5]] },
|
||||||
{ "id": 8, "label": "wire_1", "point": "s0,s1,r0", "coords": [1.0, 3.0, 0.0],
|
{ "id": 19, "label": "wire_1", "point": "r1,s0,s0", "coords": [0.0, 0.0, 0.0],
|
||||||
"endpoints": [21, 22], "endpoint_coords": [[1.0, 1.0, 1.0], [3.0, 1.0, 1.0]] },
|
"endpoints": [21, 22], "endpoint_coords": [[0.0, 0.0, -0.5], [0.0, 0.0, 0.5]] },
|
||||||
{ "id": 14, "label": "wire_2", "point": "s0,s0,r1", "coords": [1.0, 1.0, 2.0],
|
{ "id": 20, "label": "wire_2", "point": "r2,s0,s0", "coords": [0.0, 0.0, 1.0],
|
||||||
"endpoints": [21, 21], "endpoint_coords": [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]] },
|
"endpoints": [21, 22], "endpoint_coords": [[0.0, 0.0, -0.5], [0.0, 0.0, 0.5]] }
|
||||||
{ "id": 15, "label": "wire_3", "point": "s1,s0,r1", "coords": [3.0, 1.0, 2.0],
|
|
||||||
"endpoints": [21, 22], "endpoint_coords": [[1.0, 1.0, 1.0], [3.0, 1.0, 1.0]] },
|
|
||||||
{ "id": 18, "label": "wire_4", "point": "r0,s0,s0", "coords": [0.0, 1.0, 1.0],
|
|
||||||
"endpoints": [21, 22], "endpoint_coords": [[1.0, 1.0, 1.0], [3.0, 1.0, 1.0]] },
|
|
||||||
{ "id": 19, "label": "wire_5", "point": "r1,s0,s0", "coords": [2.0, 1.0, 1.0],
|
|
||||||
"endpoints": [21, 22], "endpoint_coords": [[1.0, 1.0, 1.0], [3.0, 1.0, 1.0]] },
|
|
||||||
{ "id": 20, "label": "wire_6", "point": "r2,s0,s0", "coords": [4.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, 18] },
|
{ "id": 16, "label": "surface_0", "point": "r0,r0,s0", "coords": [0.0, -1.0, -1.0], "boundary_wires": [18] },
|
||||||
{ "id": 4, "label": "surface_1", "point": "r1,s0,r0", "coords": [2.0, 1.0, 0.0], "boundary_wires": [5, 19] },
|
{ "id": 17, "label": "surface_1", "point": "r0,r1,s0", "coords": [0.0, 0.0, -1.0], "boundary_wires": [18] }
|
||||||
{ "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": [
|
|
||||||
{ "id": 0, "label": "volume_0", "point": "r0,r0,r0", "coords": [0.0, 0.0, 0.0] },
|
|
||||||
{ "id": 1, "label": "volume_1", "point": "r0,r1,r0", "coords": [0.0, 2.0, 0.0] },
|
|
||||||
{ "id": 2, "label": "volume_2", "point": "r0,r2,r0", "coords": [0.0, 4.0, 0.0] },
|
|
||||||
{ "id": 9, "label": "volume_3", "point": "r0,r0,r1", "coords": [0.0, 0.0, 2.0] },
|
|
||||||
{ "id": 10, "label": "volume_4", "point": "r0,r1,r1", "coords": [0.0, 2.0, 2.0] }
|
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
// Coordinate mapping: coord[0]→z, coord[1]→y, coord[2]→x, scale 1.2
|
// Coordinate mapping: coords are already [x, y, z] layout coordinates
|
||||||
const SCALE = 1.2;
|
// Scale for better visualization
|
||||||
|
const SCALE = 2.0;
|
||||||
function mapCoords(coords) {
|
function mapCoords(coords) {
|
||||||
return [coords[2] * SCALE, coords[1] * SCALE, coords[0] * SCALE];
|
return [coords[0] * SCALE, coords[1] * SCALE, coords[2] * SCALE];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wire colors by group
|
// Wire colors by group
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue