diff --git a/mslicer/src/windows/supports.rs b/mslicer/src/windows/supports.rs index 8146773..4999d49 100644 --- a/mslicer/src/windows/supports.rs +++ b/mslicer/src/windows/supports.rs @@ -1,7 +1,7 @@ use egui::{Context, Ui}; use slicer::supports::line::LineSupportGenerator; -use crate::{app::App, ui::components::dragger}; +use crate::{app::App, render::rendered_mesh::RenderedMesh, ui::components::dragger}; pub fn ui(app: &mut App, ui: &mut Ui, _ctx: &Context) { if ui.button("Generate").clicked() { @@ -11,9 +11,17 @@ pub fn ui(app: &mut App, ui: &mut Ui, _ctx: &Context) { app.slice_config.platform_size, ); - for mesh in app.meshes.read().iter() { + let mut meshes = app.meshes.write(); + + for i in 0..meshes.len() { + let mesh = &meshes[i]; + let supports = generator.generate_line_supports(&mesh.mesh); - app.state.line_support_debug.extend(supports); + let mesh = RenderedMesh::from_mesh(supports) + .with_name("Supports".into()) + .with_random_color(); + + meshes.push(mesh); } } diff --git a/slicer/src/builder.rs b/slicer/src/builder.rs new file mode 100644 index 0000000..50777bc --- /dev/null +++ b/slicer/src/builder.rs @@ -0,0 +1,108 @@ +use std::f32::consts::PI; + +use anyhow::Result; +use nalgebra::Vector3; +use stl_io::Triangle; + +use crate::mesh::Mesh; + +pub struct MeshBuilder { + vertices: Vec>, + normals: Vec>, + faces: Vec<[u32; 3]>, +} + +impl MeshBuilder { + pub fn new() -> Self { + Self { + vertices: Vec::new(), + normals: Vec::new(), + faces: Vec::new(), + } + } + + pub fn add_vertex(&mut self, vertex: Vector3) -> u32 { + self.vertices.push(vertex); + (self.vertices.len() - 1) as u32 + } + + pub fn add_face(&mut self, face: [u32; 3], normal: Vector3) { + self.faces.push(face); + self.normals.push(normal); + } + + pub fn add_quad(&mut self, quad: [u32; 4], normal: Vector3) { + self.add_face([quad[0], quad[1], quad[2]], normal); + self.add_face([quad[1], quad[2], quad[3]], normal); + } + + pub fn build(self) -> Mesh { + Mesh::new(self.vertices, self.faces, self.normals) + } + + pub fn save_stl(&self, path: &str) -> Result<()> { + let mut stl = Vec::new(); + + for (face, normal) in self.faces.iter().zip(self.normals.iter()) { + let (a, b, c) = ( + self.vertices[face[0] as usize], + self.vertices[face[1] as usize], + self.vertices[face[2] as usize], + ); + + let into_stl = |v: Vector3| stl_io::Vertex::new([v.x, v.y, v.z]); + stl.push(Triangle { + normal: into_stl(*normal), + vertices: [into_stl(a), into_stl(b), into_stl(c)], + }); + } + + let mut file = std::fs::File::create(path)?; + stl_io::write_stl(&mut file, stl.iter())?; + Ok(()) + } +} + +impl MeshBuilder { + pub fn add_vertical_cylinder( + &mut self, + bottom: Vector3, + height: f32, + radius: f32, + precision: u32, + ) { + let top = bottom + Vector3::new(0.0, 0.0, height); + let bottom_center = self.add_vertex(bottom); + let top_center = self.add_vertex(top); + + let mut last = None; + let mut fist = None; + for i in 0..precision { + let angle = 2.0 * PI * (i as f32) / (precision as f32); + let normal = Vector3::new(angle.cos(), angle.sin(), 0.0); + let offset = normal * radius; + + let top = self.add_vertex(top + offset); + let bottom = self.add_vertex(bottom + offset); + + if let Some((last_top, last_bottom)) = last { + self.add_quad([last_top, last_bottom, top, bottom], normal); + self.add_face([last_top, top, top_center], Vector3::z()); + self.add_face([last_bottom, bottom_center, bottom], -Vector3::z()); + } + + last = Some((top, bottom)); + if fist.is_none() { + fist = Some((top, bottom, normal)); + } + } + + if let Some((first_top, first_bottom, normal)) = fist { + if let Some((last_top, last_bottom)) = last { + self.add_quad([last_top, last_bottom, first_top, first_bottom], normal); + self.add_face([last_top, first_top, top_center], Vector3::z()); + self.add_face([last_bottom, bottom_center, first_bottom], -Vector3::z()); + } + } + } +} diff --git a/slicer/src/lib.rs b/slicer/src/lib.rs index 6c3ec72..6a40341 100644 --- a/slicer/src/lib.rs +++ b/slicer/src/lib.rs @@ -2,6 +2,7 @@ use nalgebra::Vector3; +pub mod builder; pub mod half_edge; pub mod mesh; pub mod segments; diff --git a/slicer/src/mesh.rs b/slicer/src/mesh.rs index cefc5b8..414c177 100644 --- a/slicer/src/mesh.rs +++ b/slicer/src/mesh.rs @@ -35,7 +35,13 @@ impl Mesh { /// transformations are all 0 by default. pub fn new(mut vertices: Vec, faces: Vec<[u32; 3]>, normals: Vec) -> Self { center_vertices(&mut vertices); + Self::new_uncentred(vertices, faces, normals) + } + /// Creates a new mesh from the givin vertices and faces. The + /// transformations are all 0 by default and the vertices are + /// not centered. + pub fn new_uncentred(vertices: Vec, faces: Vec<[u32; 3]>, normals: Vec) -> Self { Self { inner: Arc::new(MeshInner { vertices: vertices.into_boxed_slice(), diff --git a/slicer/src/supports/line.rs b/slicer/src/supports/line.rs index eb1e9b0..abe9a7a 100644 --- a/slicer/src/supports/line.rs +++ b/slicer/src/supports/line.rs @@ -4,7 +4,7 @@ use nalgebra::{Vector2, Vector3}; use ordered_float::OrderedFloat; use tracing::info; -use crate::{half_edge::HalfEdgeMesh, mesh::Mesh}; +use crate::{builder::MeshBuilder, half_edge::HalfEdgeMesh, mesh::Mesh}; pub struct LineSupportGenerator<'a> { config: &'a LineSupportConfig, @@ -18,6 +18,11 @@ pub struct LineSupport { } pub struct LineSupportConfig { + /// Support generation + pub support_radius: f32, + pub base_radius: f32, + + /// Overhang detection pub max_origin_normal_z: f32, pub max_neighbor_z_diff: f32, pub min_angle: f32, @@ -29,16 +34,33 @@ impl<'a> LineSupportGenerator<'a> { Self { config, bed_size } } - pub fn generate_line_supports(&self, mesh: &Mesh) -> Vec<[Vector3; 2]> { + pub fn generate_line_supports(&self, mesh: &Mesh) -> Mesh { let half_edge_mesh = HalfEdgeMesh::new(mesh); - let mut points = Vec::new(); - points.extend_from_slice(&self.detect_point_overhangs(mesh, &half_edge_mesh)); - points.extend_from_slice(&self.detect_face_overhangs(mesh)); + let mut overhangs = Vec::new(); - info!("Line support generation identified {} points", points.len()); + let point_overhangs = self.detect_point_overhangs(mesh, &half_edge_mesh); + overhangs.extend_from_slice(&point_overhangs); - points + let face_overhangs = self.detect_face_overhangs(mesh); + overhangs.extend_from_slice(&face_overhangs); + + info!( + "Found {} overhangs {{ face: {}, point: {} }}", + overhangs.len(), + face_overhangs.len(), + point_overhangs.len() + ); + + let mut out = MeshBuilder::new(); + for [origin, _normal] in overhangs { + let bottom = origin.xy().to_homogeneous(); + out.add_vertical_cylinder(bottom, origin.z, self.config.support_radius, 10); + } + + out.save_stl("debug.stl").unwrap(); + + out.build() } /// Find all points that are both lower than their surrounding points and have down facing normals @@ -183,6 +205,9 @@ fn ray_triangle_intersection( impl Default for LineSupportConfig { fn default() -> Self { Self { + support_radius: 0.1, + base_radius: 1.0, + max_origin_normal_z: 0.0, max_neighbor_z_diff: -0.01, min_angle: 3.0,