From e45e8eb21f5de9f2c853db25dcd598a94da2b280 Mon Sep 17 00:00:00 2001 From: Don Garrett Date: Thu, 27 Aug 2026 15:59:08 -0500 Subject: [PATCH] Adjust to be sandbox wrapper friendly. --- .gitignore | 1 + Cargo.toml | 26 +-- src/render.rs | 562 +++++++++++++++++++++++++------------------------- 3 files changed, 295 insertions(+), 294 deletions(-) diff --git a/.gitignore b/.gitignore index 48d1b54..8a3e7e4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target mesh.stl +.bash_history diff --git a/Cargo.toml b/Cargo.toml index b130391..39b848f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,13 @@ -[package] -name = "mushroom_generator" -version = "0.1.0" -edition = "2021" - -[dependencies] -approx = "0.5.1" -cached = "0.51.4" -clap = { version = "4.5.4", features = ["derive"] } -rocket = "0.5.1" -serde = "1.0.203" -serde_derive = "1.0.203" -stl_io = "0.8.5" +[package] +name = "mushroom_generator" +version = "0.1.0" +edition = "2021" + +[dependencies] +approx = "0.5.1" +cached = "0.51.4" +clap = { version = "4.5.4", features = ["derive"] } +rocket = "0.5.1" +serde = "1.0.203" +serde_derive = "1.0.203" +stl_io = "0.8.5" diff --git a/src/render.rs b/src/render.rs index 511cbff..a8cac04 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,281 +1,281 @@ -use cached::proc_macro::cached; -use stl_io::{self, Normal, Triangle, Vertex}; - -use crate::layers::{Layer, Layers}; - -fn new_triangle(p1: &Vertex, p2: &Vertex, p3: &Vertex) -> stl_io::Triangle { - // Set Vector U to (Triangle.p2 minus Triangle.p1) - // Set Vector V to (Triangle.p3 minus Triangle.p1) - - // Set Normal.x to (multiply U.y by V.z) minus (multiply U.z by V.y) - // Set Normal.y to (multiply U.z by V.x) minus (multiply U.x by V.z) - // Set Normal.z to (multiply U.x by V.y) minus (multiply U.y by V.x) - - let u = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]]; - let v = [p3[0] - p1[0], p3[1] - p1[1], p3[2] - p1[2]]; - - let n_x = (u[1] * v[2]) - (u[2] * v[1]); - let n_y = (u[2] * v[0]) - (u[0] * v[2]); - let n_z = (u[0] * v[1]) - (u[1] * v[0]); - - stl_io::Triangle { - normal: Normal::new([n_x, n_y, n_z]), - vertices: [*p1, *p2, *p3], - } -} - -/// Find a point on the cirle at a given angle (in radians). -fn vertex_on_circle(center: &Vertex, radius: f32, angle: f32) -> Vertex { - let x = center[0] + (radius * angle.cos()); - let y = center[1] + (radius * angle.sin()); - let z = center[2]; - - Vertex::new([x, y, z]) -} - -fn vertexes_on_circle(layer: &Layer, steps: u32) -> Vec { - let mut result = Vec::with_capacity(steps as usize); - let stepsize = std::f64::consts::TAU / steps as f64; - - for i in 0..steps { - let angle = (i as f64 * stepsize) as f32; - result.push(vertex_on_circle(&layer.center, layer.radius, angle)); - } - result -} - -fn tube_base(center: &Vertex, edge: &[Vertex]) -> Vec { - let mut result = Vec::with_capacity(edge.len()); - - for i in 0..edge.len() { - result.push(new_triangle(center, &edge[(i + 1) % edge.len()], &edge[i])); - } - - result -} - -fn tube_sides(botoom_edge: &[Vertex], top_edge: &[Vertex]) -> Vec { - assert_eq!(botoom_edge.len(), top_edge.len()); - - let mut result = Vec::with_capacity(2 * botoom_edge.len()); - - for i in 0..botoom_edge.len() { - let j = (i + 1) % botoom_edge.len(); - - result.push(new_triangle(&botoom_edge[i], &top_edge[j], &top_edge[i])); - result.push(new_triangle(&botoom_edge[i], &botoom_edge[j], &top_edge[j])); - } - - result -} - -fn tube_top(center: &Vertex, edge: &[Vertex]) -> Vec { - // The top is the same, except the normals are reversed. We can do that by reversing the order of the vertexes. - let mut edge = edge.to_owned(); - edge.reverse(); - - tube_base(center, &edge) -} - -#[cached(key = "String", convert = r#"{ format!("{:?}_{}", layers, resolution) }"#)] -pub fn render(layers: &Layers, resolution: f32) -> Vec { - // Top and Bottom must be a single point, and must be at least one middle layer. - assert!(!layers.layers.is_empty()); - - // How many vertexes per circle. IE: the resolution of the circle. - let circle_steps = - ((layers.max_radius() * std::f32::consts::TAU) / resolution).max(64.0) as u32; - - let mut layer_vertexes: Vec> = Vec::with_capacity(layers.layers.len()); - - for layer in &layers.layers { - layer_vertexes.push(vertexes_on_circle(layer, circle_steps)); - } - - // Estimate triangle count: base + sides + top - let estimated_triangles = layers.layers.len() * circle_steps as usize * 2; - let mut result: Vec = Vec::with_capacity(estimated_triangles); - - result.append(&mut tube_base(&layers.base, &layer_vertexes[0])); - for i in 0..layer_vertexes.len() - 1 { - result.append(&mut tube_sides(&layer_vertexes[i], &layer_vertexes[i + 1])); - } - result.append(&mut tube_top(&layers.top, layer_vertexes.last().unwrap())); - - result -} - -#[cfg(test)] -mod tests { - use approx::relative_eq; - - use super::*; - - #[test] - fn new_triangle_normal() { - // Simple triangle - let a = Vertex::new([1.0, 0.0, 0.0]); - let b = Vertex::new([0.0, 1.0, 0.0]); - let c = Vertex::new([-1.0, 0.0, 0.0]); - - let tri = new_triangle(&a, &b, &c); - - assert_eq!(tri.normal, Normal::new([0.0, 0.0, 2.0])); - - // Same triangle, reverse order. - let tri = new_triangle(&c, &b, &a); - - assert_eq!(tri.normal, Normal::new([0.0, 0.0, -2.0])); - - // Uneven numbers. - let a = Vertex::new([2.0, 0.0, 0.0]); - let b = Vertex::new([0.0, 3.0, 0.0]); - let c = Vertex::new([-4.0, 0.0, 0.0]); - - let tri_big = new_triangle(&a, &b, &c); - - assert_eq!(tri_big.normal, Normal::new([0.0, 0.0, 18.0])); - } - - fn validate_vec_vertex_relative_eq(val: &Vec, expected: &Vec) { - let mut pass = true; - - if val.len() != expected.len() { - pass = false; - } - - for i in 0..val.len() { - for j in 0..3 { - if !relative_eq!(val[i][j], expected[i][j]) { - pass = false; - } - } - } - - if !pass { - // We re-compare here (which will always fail) to get the friendly error message, - // which I can't figure out how to generate directly. - assert_eq!(val, expected); - } - } - - #[test] - fn vertexes_on_circle_basic() { - let val = vertexes_on_circle( - &Layer { - center: Vertex::new([0.0, 0.0, 0.0]), - radius: 1.0, - }, - 4, - ); - - let expected = [ - Vertex::new([1.0, 0.0, 0.0]), - Vertex::new([0.0, 1.0, 0.0]), - Vertex::new([-1.0, 0.0, 0.0]), - Vertex::new([0.0, -1.0, 0.0]), - ] - .to_vec(); - - validate_vec_vertex_relative_eq(&val, &expected); - } - - #[test] - fn vertexes_on_circle_complex() { - let val = vertexes_on_circle( - &Layer { - center: Vertex::new([4.0, 5.0, 6.0]), - radius: 3.0, - }, - 4, - ); - - let expected = [ - Vertex::new([7.0, 5.0, 6.0]), - Vertex::new([4.0, 8.0, 6.0]), - Vertex::new([1.0, 5.0, 6.0]), - Vertex::new([4.0, 2.0, 6.0]), - ] - .to_vec(); - - validate_vec_vertex_relative_eq(&val, &expected); - } - - #[test] - fn tube_base_test() { - // fn tube_base(center: &Vertex, edge: &Vec) -> Vec { - - let center = Vertex::new([0.0, 0.0, 0.0]); - let edge = vertexes_on_circle( - &Layer { - center: center.clone(), - radius: 1.0, - }, - 4, - ); - - let triangles = tube_base(¢er, &edge); - - assert_eq!(triangles.len(), edge.len()); - - for t in triangles { - // All of these triangles should have the same normal (down). - assert_eq!(t.normal, Vertex::new([0.0, 0.0, -1.0])); - } - } - - #[test] - fn tube_top_test() { - let center = Vertex::new([0.0, 0.0, 0.0]); - let edge = vertexes_on_circle( - &Layer { - center: center.clone(), - radius: 1.0, - }, - 4, - ); - - let triangles = tube_top(¢er, &edge); - - assert_eq!(triangles.len(), edge.len()); - - for t in triangles { - // All of these triangles should have the same normal (up). - assert_eq!(t.normal, Vertex::new([0.0, 0.0, 1.0])); - } - } - - #[test] - fn render_test() { - let layers = Layers { - base: Vertex::new([0.0, 0.0, 0.0]), - layers: [ - Layer { - center: Vertex::new([0.0, 0.0, 1.0]), - radius: 1.0, - }, - Layer { - center: Vertex::new([0.0, 0.0, 2.0]), - radius: 1.0, - }, - ] - .to_vec(), - top: Vertex::new([0.0, 0.0, 3.0]), - }; - - let mesh = render(&layers, 10.0); // Large resolution ensures minimum steps of 64. - - // 64 triangles for top, 128 for sides, 64 for bottom. - assert_eq!(mesh.len(), 64 + 128 + 64); - - for i in 0..mesh.len() { - if i < 64 { - assert!(mesh[i].normal[2] < 0.0) // Bottom triangles point down. - } else if i < (64 + 128) { - assert!(mesh[i].normal[2] == 0.0) // side triangles point out. - } else { - assert!(mesh[i].normal[2] > 0.0) // top triangles point up. - } - } - } -} +use cached::proc_macro::cached; +use stl_io::{self, Normal, Triangle, Vertex}; + +use crate::layers::{Layer, Layers}; + +fn new_triangle(p1: &Vertex, p2: &Vertex, p3: &Vertex) -> stl_io::Triangle { + // Set Vector U to (Triangle.p2 minus Triangle.p1) + // Set Vector V to (Triangle.p3 minus Triangle.p1) + + // Set Normal.x to (multiply U.y by V.z) minus (multiply U.z by V.y) + // Set Normal.y to (multiply U.z by V.x) minus (multiply U.x by V.z) + // Set Normal.z to (multiply U.x by V.y) minus (multiply U.y by V.x) + + let u = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]]; + let v = [p3[0] - p1[0], p3[1] - p1[1], p3[2] - p1[2]]; + + let n_x = (u[1] * v[2]) - (u[2] * v[1]); + let n_y = (u[2] * v[0]) - (u[0] * v[2]); + let n_z = (u[0] * v[1]) - (u[1] * v[0]); + + stl_io::Triangle { + normal: Normal::new([n_x, n_y, n_z]), + vertices: [*p1, *p2, *p3], + } +} + +/// Find a point on the cirle at a given angle (in radians). +fn vertex_on_circle(center: &Vertex, radius: f32, angle: f32) -> Vertex { + let x = center[0] + (radius * angle.cos()); + let y = center[1] + (radius * angle.sin()); + let z = center[2]; + + Vertex::new([x, y, z]) +} + +fn vertexes_on_circle(layer: &Layer, steps: u32) -> Vec { + let mut result = Vec::with_capacity(steps as usize); + let stepsize = std::f64::consts::TAU / steps as f64; + + for i in 0..steps { + let angle = (i as f64 * stepsize) as f32; + result.push(vertex_on_circle(&layer.center, layer.radius, angle)); + } + result +} + +fn tube_base(center: &Vertex, edge: &[Vertex]) -> Vec { + let mut result = Vec::with_capacity(edge.len()); + + for i in 0..edge.len() { + result.push(new_triangle(center, &edge[(i + 1) % edge.len()], &edge[i])); + } + + result +} + +fn tube_sides(botoom_edge: &[Vertex], top_edge: &[Vertex]) -> Vec { + assert_eq!(botoom_edge.len(), top_edge.len()); + + let mut result = Vec::with_capacity(2 * botoom_edge.len()); + + for i in 0..botoom_edge.len() { + let j = (i + 1) % botoom_edge.len(); + + result.push(new_triangle(&botoom_edge[i], &top_edge[j], &top_edge[i])); + result.push(new_triangle(&botoom_edge[i], &botoom_edge[j], &top_edge[j])); + } + + result +} + +fn tube_top(center: &Vertex, edge: &[Vertex]) -> Vec { + // The top is the same, except the normals are reversed. We can do that by reversing the order of the vertexes. + let mut edge = edge.to_owned(); + edge.reverse(); + + tube_base(center, &edge) +} + +#[cached(key = "String", convert = r#"{ format!("{:?}_{}", layers, resolution) }"#)] +pub fn render(layers: &Layers, resolution: f32) -> Vec { + // Top and Bottom must be a single point, and must be at least one middle layer. + assert!(!layers.layers.is_empty()); + + // How many vertexes per circle. IE: the resolution of the circle. + let circle_steps = + ((layers.max_radius() * std::f32::consts::TAU) / resolution).max(64.0) as u32; + + let mut layer_vertexes: Vec> = Vec::with_capacity(layers.layers.len()); + + for layer in &layers.layers { + layer_vertexes.push(vertexes_on_circle(layer, circle_steps)); + } + + // Estimate triangle count: base + sides + top + let estimated_triangles = layers.layers.len() * circle_steps as usize * 2; + let mut result: Vec = Vec::with_capacity(estimated_triangles); + + result.append(&mut tube_base(&layers.base, &layer_vertexes[0])); + for i in 0..layer_vertexes.len() - 1 { + result.append(&mut tube_sides(&layer_vertexes[i], &layer_vertexes[i + 1])); + } + result.append(&mut tube_top(&layers.top, layer_vertexes.last().unwrap())); + + result +} + +#[cfg(test)] +mod tests { + use approx::relative_eq; + + use super::*; + + #[test] + fn new_triangle_normal() { + // Simple triangle + let a = Vertex::new([1.0, 0.0, 0.0]); + let b = Vertex::new([0.0, 1.0, 0.0]); + let c = Vertex::new([-1.0, 0.0, 0.0]); + + let tri = new_triangle(&a, &b, &c); + + assert_eq!(tri.normal, Normal::new([0.0, 0.0, 2.0])); + + // Same triangle, reverse order. + let tri = new_triangle(&c, &b, &a); + + assert_eq!(tri.normal, Normal::new([0.0, 0.0, -2.0])); + + // Uneven numbers. + let a = Vertex::new([2.0, 0.0, 0.0]); + let b = Vertex::new([0.0, 3.0, 0.0]); + let c = Vertex::new([-4.0, 0.0, 0.0]); + + let tri_big = new_triangle(&a, &b, &c); + + assert_eq!(tri_big.normal, Normal::new([0.0, 0.0, 18.0])); + } + + fn validate_vec_vertex_relative_eq(val: &Vec, expected: &Vec) { + let mut pass = true; + + if val.len() != expected.len() { + pass = false; + } + + for i in 0..val.len() { + for j in 0..3 { + if !relative_eq!(val[i][j], expected[i][j]) { + pass = false; + } + } + } + + if !pass { + // We re-compare here (which will always fail) to get the friendly error message, + // which I can't figure out how to generate directly. + assert_eq!(val, expected); + } + } + + #[test] + fn vertexes_on_circle_basic() { + let val = vertexes_on_circle( + &Layer { + center: Vertex::new([0.0, 0.0, 0.0]), + radius: 1.0, + }, + 4, + ); + + let expected = [ + Vertex::new([1.0, 0.0, 0.0]), + Vertex::new([0.0, 1.0, 0.0]), + Vertex::new([-1.0, 0.0, 0.0]), + Vertex::new([0.0, -1.0, 0.0]), + ] + .to_vec(); + + validate_vec_vertex_relative_eq(&val, &expected); + } + + #[test] + fn vertexes_on_circle_complex() { + let val = vertexes_on_circle( + &Layer { + center: Vertex::new([4.0, 5.0, 6.0]), + radius: 3.0, + }, + 4, + ); + + let expected = [ + Vertex::new([7.0, 5.0, 6.0]), + Vertex::new([4.0, 8.0, 6.0]), + Vertex::new([1.0, 5.0, 6.0]), + Vertex::new([4.0, 2.0, 6.0]), + ] + .to_vec(); + + validate_vec_vertex_relative_eq(&val, &expected); + } + + #[test] + fn tube_base_test() { + // fn tube_base(center: &Vertex, edge: &Vec) -> Vec { + + let center = Vertex::new([0.0, 0.0, 0.0]); + let edge = vertexes_on_circle( + &Layer { + center: center.clone(), + radius: 1.0, + }, + 4, + ); + + let triangles = tube_base(¢er, &edge); + + assert_eq!(triangles.len(), edge.len()); + + for t in triangles { + // All of these triangles should have the same normal (down). + assert_eq!(t.normal, Vertex::new([0.0, 0.0, -1.0])); + } + } + + #[test] + fn tube_top_test() { + let center = Vertex::new([0.0, 0.0, 0.0]); + let edge = vertexes_on_circle( + &Layer { + center: center.clone(), + radius: 1.0, + }, + 4, + ); + + let triangles = tube_top(¢er, &edge); + + assert_eq!(triangles.len(), edge.len()); + + for t in triangles { + // All of these triangles should have the same normal (up). + assert_eq!(t.normal, Vertex::new([0.0, 0.0, 1.0])); + } + } + + #[test] + fn render_test() { + let layers = Layers { + base: Vertex::new([0.0, 0.0, 0.0]), + layers: [ + Layer { + center: Vertex::new([0.0, 0.0, 1.0]), + radius: 1.0, + }, + Layer { + center: Vertex::new([0.0, 0.0, 2.0]), + radius: 1.0, + }, + ] + .to_vec(), + top: Vertex::new([0.0, 0.0, 3.0]), + }; + + let mesh = render(&layers, 10.0); // Large resolution ensures minimum steps of 64. + + // 64 triangles for top, 128 for sides, 64 for bottom. + assert_eq!(mesh.len(), 64 + 128 + 64); + + for i in 0..mesh.len() { + if i < 64 { + assert!(mesh[i].normal[2] < 0.0) // Bottom triangles point down. + } else if i < (64 + 128) { + assert!(mesh[i].normal[2] == 0.0) // side triangles point out. + } else { + assert!(mesh[i].normal[2] > 0.0) // top triangles point up. + } + } + } +}