From c7c8cf0e2eff98fdc53e8fa3e9ea4894ce654936 Mon Sep 17 00:00:00 2001 From: Connor Slade Date: Sat, 3 Aug 2024 22:15:34 -0400 Subject: [PATCH] Cleanup line supports --- mslicer/src/windows/supports.rs | 16 +- slicer/benches/benchmark.rs | 4 +- slicer/src/segments/mod.rs | 3 + .../{segments.rs => segments/segments_1d.rs} | 4 +- slicer/src/slicer.rs | 4 +- slicer/src/supports/line.rs | 177 ++++++++++-------- 6 files changed, 125 insertions(+), 83 deletions(-) create mode 100644 slicer/src/segments/mod.rs rename slicer/src/{segments.rs => segments/segments_1d.rs} (99%) diff --git a/mslicer/src/windows/supports.rs b/mslicer/src/windows/supports.rs index 47b95bf..8146773 100644 --- a/mslicer/src/windows/supports.rs +++ b/mslicer/src/windows/supports.rs @@ -1,13 +1,18 @@ use egui::{Context, Ui}; -use slicer::supports::line::generate_line_supports; +use slicer::supports::line::LineSupportGenerator; use crate::{app::App, ui::components::dragger}; pub fn ui(app: &mut App, ui: &mut Ui, _ctx: &Context) { if ui.button("Generate").clicked() { app.state.line_support_debug = Vec::new(); + let generator = LineSupportGenerator::new( + &app.state.line_support_config, + app.slice_config.platform_size, + ); + for mesh in app.meshes.read().iter() { - let supports = generate_line_supports(&mesh.mesh, &app.state.line_support_config); + let supports = generator.generate_line_supports(&mesh.mesh); app.state.line_support_debug.extend(supports); } } @@ -35,4 +40,11 @@ pub fn ui(app: &mut App, ui: &mut Ui, _ctx: &Context) { &mut app.state.line_support_config.min_angle, |x| x.speed(0.01), ); + + dragger( + ui, + "Face Support Spacing", + &mut app.state.line_support_config.face_support_spacing, + |x| x.speed(0.1), + ); } diff --git a/slicer/benches/benchmark.rs b/slicer/benches/benchmark.rs index 53ed977..e04d6f3 100644 --- a/slicer/benches/benchmark.rs +++ b/slicer/benches/benchmark.rs @@ -2,7 +2,7 @@ use std::{fs::File, io::BufReader, path::Path}; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; -use slicer::{mesh::load_mesh, segments::Segments}; +use slicer::{mesh::load_mesh, segments::Segments1D}; pub fn bench(c: &mut Criterion) { let mut group = c.benchmark_group("Mesh Intersections"); @@ -11,7 +11,7 @@ pub fn bench(c: &mut Criterion) { for mesh_name in ["teapot.stl", "dragon.stl", "david.stl"] { let mut file = BufReader::new(File::open(parent.join(mesh_name)).unwrap()); let mesh = load_mesh(&mut file, "stl").unwrap(); - let segments = Segments::from_mesh(&mesh, 100); + let segments = Segments1D::from_mesh(&mesh, 100); group.bench_with_input(BenchmarkId::new("Linier", mesh_name), &mesh, |b, i| { b.iter(|| i.intersect_plane(0.0)) diff --git a/slicer/src/segments/mod.rs b/slicer/src/segments/mod.rs new file mode 100644 index 0000000..264c2d4 --- /dev/null +++ b/slicer/src/segments/mod.rs @@ -0,0 +1,3 @@ +pub mod segments_1d; + +pub use segments_1d::Segments1D; diff --git a/slicer/src/segments.rs b/slicer/src/segments/segments_1d.rs similarity index 99% rename from slicer/src/segments.rs rename to slicer/src/segments/segments_1d.rs index 2a93056..7311a7e 100644 --- a/slicer/src/segments.rs +++ b/slicer/src/segments/segments_1d.rs @@ -6,7 +6,7 @@ use crate::mesh::Mesh; /// along the slicing axis and adding references to all the triangles that /// overlap each segment, to slice a layer, you don't need to loop through every /// triangle in the mesh to find all intersecting faces. -pub struct Segments { +pub struct Segments1D { start_height: f32, layer_height: f32, @@ -14,7 +14,7 @@ pub struct Segments { transformed_points: Vec>, } -impl Segments { +impl Segments1D { /// Creates a new Segments structure from a given mesh and segment count. pub fn from_mesh(mesh: &Mesh, layer_count: usize) -> Self { let (min, max) = mesh.minmax_point(); diff --git a/slicer/src/slicer.rs b/slicer/src/slicer.rs index 81f549f..9b45083 100644 --- a/slicer/src/slicer.rs +++ b/slicer/src/slicer.rs @@ -13,7 +13,7 @@ use common::{ use ordered_float::OrderedFloat; use rayon::iter::{IntoParallelIterator, ParallelIterator}; -use crate::{mesh::Mesh, segments::Segments, Pos}; +use crate::{mesh::Mesh, segments::Segments1D, Pos}; /// Used to slice a mesh. pub struct Slicer { @@ -80,7 +80,7 @@ impl Slicer { let segments = self .models .iter() - .map(|x| Segments::from_mesh(x, 100)) + .map(|x| Segments1D::from_mesh(x, 100)) .collect::>(); let layers = (0..self.progress.total) diff --git a/slicer/src/supports/line.rs b/slicer/src/supports/line.rs index cb45d45..cf5b11a 100644 --- a/slicer/src/supports/line.rs +++ b/slicer/src/supports/line.rs @@ -1,9 +1,14 @@ use std::{collections::HashSet, f32::consts::PI}; -use nalgebra::Vector3; +use nalgebra::{Vector2, Vector3}; use crate::{half_edge::HalfEdgeMesh, mesh::Mesh}; +pub struct LineSupportGenerator<'a> { + config: &'a LineSupportConfig, + bed_size: Vector3, +} + pub struct LineSupport { pub start: Vector3, pub end: Vector3, @@ -14,6 +19,101 @@ pub struct LineSupportConfig { pub max_origin_normal_z: f32, pub max_neighbor_z_diff: f32, pub min_angle: f32, + pub face_support_spacing: f32, +} + +impl<'a> LineSupportGenerator<'a> { + pub fn new(config: &'a LineSupportConfig, bed_size: Vector3) -> Self { + Self { config, bed_size } + } + + pub fn generate_line_supports(&self, mesh: &Mesh) -> Vec<[Vector3; 2]> { + // let half_edge_mesh = HalfEdgeMesh::new(mesh); + + // let points = detect_point_overhangs(mesh, &half_edge_mesh, config); + let points = self.detect_face_overhangs(mesh); + println!("Found {} overhangs", points.len()); + + points + } + + /// Find all points that are both lower than their surrounding points and have down facing normals + fn detect_point_overhangs( + &self, + mesh: &Mesh, + half_edge: &HalfEdgeMesh, + ) -> Vec<[Vector3; 2]> { + let mut overhangs = Vec::new(); + let mut seen = HashSet::new(); + + let vertices = mesh.vertices(); + let normals = mesh.normals(); + + for edge in 0..half_edge.half_edge_count() { + let origin = half_edge.get_edge(edge as u32); + if !seen.insert(origin.origin_vertex) { + continue; + } + + // Ignore points that are not on the bottom of the mesh + let origin_normal = mesh.transform_normal(&normals[origin.face as usize]); + if origin_normal.z >= self.config.max_origin_normal_z { + continue; + } + + // Only add to overhangs if the original point is lower than all connected points by one layer + let origin_pos = mesh.transform(&vertices[origin.origin_vertex as usize]); + let neighbors = half_edge.connected_vertices(edge as u32); + if neighbors.iter().all(|connected| { + (origin_pos.z - mesh.transform(&vertices[*connected as usize]).z) + <= self.config.max_neighbor_z_diff + }) { + overhangs.push([origin_pos, origin_normal]); + } + } + + overhangs + } + + fn detect_face_overhangs(&self, mesh: &Mesh) -> Vec<[Vector3; 2]> { + let mut overhangs = Vec::new(); + + let vertices = mesh.vertices(); + let normals = mesh.normals(); + + for (face, normal) in normals.iter().enumerate() { + let normal = mesh.transform_normal(&normal); + if normal.z >= self.config.max_origin_normal_z { + continue; + } + + let angle = normal.angle(&Vector3::z()); + if angle < self.config.min_angle { + continue; + } + + overhangs.push(face); + } + + let x_count = (self.bed_size.x / self.config.face_support_spacing) as i32; + let y_count = (self.bed_size.y / self.config.face_support_spacing) as i32; + + for x in 0..x_count { + for y in 0..y_count { + let pos = Vector2::new( + x as f32 * self.config.face_support_spacing, + y as f32 * self.config.face_support_spacing, + ); + + // intersect ray + // let intersections = + + // if normal of intersection is facing down then add to overhangs + } + } + + todo!() + } } impl Default for LineSupportConfig { @@ -22,80 +122,7 @@ impl Default for LineSupportConfig { max_origin_normal_z: 0.0, max_neighbor_z_diff: -0.01, min_angle: PI / 4.0, + face_support_spacing: 10.0, } } } - -pub fn generate_line_supports(mesh: &Mesh, config: &LineSupportConfig) -> Vec<[Vector3; 2]> { - // let half_edge_mesh = HalfEdgeMesh::new(mesh); - - // let points = detect_point_overhangs(mesh, &half_edge_mesh, config); - let points = detect_face_overhangs(mesh, config); - println!("Found {} overhangs", points.len()); - - points -} - -/// Find all points that are both lower than their surrounding points and have down facing normals -fn detect_point_overhangs( - mesh: &Mesh, - half_edge: &HalfEdgeMesh, - config: &LineSupportConfig, -) -> Vec<[Vector3; 2]> { - let mut overhangs = Vec::new(); - let mut seen = HashSet::new(); - - let vertices = mesh.vertices(); - let normals = mesh.normals(); - - for edge in 0..half_edge.half_edge_count() { - let origin = half_edge.get_edge(edge as u32); - if !seen.insert(origin.origin_vertex) { - continue; - } - - // Ignore points that are not on the bottom of the mesh - let origin_normal = mesh.transform_normal(&normals[origin.face as usize]); - if origin_normal.z >= config.max_origin_normal_z { - continue; - } - - // Only add to overhangs if the original point is lower than all connected points by one layer - let origin_pos = mesh.transform(&vertices[origin.origin_vertex as usize]); - let neighbors = half_edge.connected_vertices(edge as u32); - if neighbors.iter().all(|connected| { - (origin_pos.z - mesh.transform(&vertices[*connected as usize]).z) - <= config.max_neighbor_z_diff - }) { - overhangs.push([origin_pos, origin_normal]); - } - } - - overhangs -} - -fn detect_face_overhangs(mesh: &Mesh, config: &LineSupportConfig) -> Vec<[Vector3; 2]> { - let mut overhangs = Vec::new(); - - let vertices = mesh.vertices(); - for (face, normal) in mesh.faces().iter().zip(mesh.normals().iter()) { - let normal = mesh.transform_normal(&normal); - if normal.z >= config.max_origin_normal_z { - continue; - } - - let angle = normal.angle(&Vector3::z()); - if angle < config.min_angle { - continue; - } - - let center = face - .iter() - .fold(Vector3::zeros(), |acc, &v| acc + vertices[v as usize]) - / 3.0; - - overhangs.push([center, normal]); - } - - overhangs -}