From c66eaf153cc817a60ff5a5eded17487acc49aa94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thea=20Sch=C3=B6bl?= Date: Sun, 10 Mar 2024 20:01:49 +0100 Subject: [PATCH] feat: improvements --- bampy/Cargo.toml | 2 + bampy/src/lib.rs | 69 +++++++++--------- bampy/src/slicer/base_slices.rs | 31 ++++---- bampy/src/slicer/line.rs | 7 ++ bampy/src/slicer/mesh.rs | 34 +++++++++ bampy/src/slicer/mod.rs | 48 ++----------- bampy/src/slicer/split_surface.rs | 45 ++++++++++++ bampy/src/slicer/triangle.rs | 113 ++++++++++++++++++++++++++++++ src/lib/slicer/worker.ts | 4 +- 9 files changed, 258 insertions(+), 95 deletions(-) create mode 100644 bampy/src/slicer/line.rs create mode 100644 bampy/src/slicer/mesh.rs create mode 100644 bampy/src/slicer/split_surface.rs create mode 100644 bampy/src/slicer/triangle.rs diff --git a/bampy/Cargo.toml b/bampy/Cargo.toml index 44e85b8..a6499f0 100644 --- a/bampy/Cargo.toml +++ b/bampy/Cargo.toml @@ -9,6 +9,7 @@ crate-type = ["cdylib", "rlib"] [features] default = ["console_error_panic_hook"] +console_error_panic_hook = ["dep:console_error_panic_hook"] [dependencies] wasm-bindgen = "0.2.84" @@ -21,6 +22,7 @@ console_error_panic_hook = { version = "0.1.7", optional = true } bvh = "0.8.0" nalgebra = "0.32.4" num = "0.4.1" +approx = "0.5.1" [dependencies.getrandom] features = ["js"] diff --git a/bampy/src/lib.rs b/bampy/src/lib.rs index b6f93ea..e0ea8e1 100644 --- a/bampy/src/lib.rs +++ b/bampy/src/lib.rs @@ -1,46 +1,49 @@ -use bvh::{aabb::Bounded, bvh::Bvh}; -use nalgebra::Point; +use std::collections::LinkedList; + +use nalgebra::{vector, Vector3}; use wasm_bindgen::prelude::wasm_bindgen; -use crate::slicer::{base_slices, SlicerOptions, Triangle}; +use crate::slicer::{ + base_slices::create_base_slices, split_surface::split_surface, Mesh, SlicerOptions, Triangle, +}; mod slicer; mod util; +const BED_NORMAL: Vector3 = vector![0f32, 0f32, 1f32]; + #[wasm_bindgen] -pub fn slice(positions: &[f32], face_normals: &[f32], layer_height: f32) { - debug_assert_eq!(positions.len() % 9, 0); - debug_assert_eq!(face_normals.len() % 3, 0); - debug_assert_eq!(positions.len() / 9, face_normals.len() / 3); +pub fn slice(positions: &[f32], layer_height: f32, max_angle: f32) { + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - let mut triangles = Vec::with_capacity(positions.len() / 9); + assert_eq!(positions.len() % 9, 0); + + let mut surface_triangles = LinkedList::>::new(); + let mut slicable_triangles = Vec::with_capacity(positions.len() / 9); for i in (0..positions.len()).step_by(9) { - let triangle = Triangle { - a: Point::::new(positions[i], positions[i + 1], positions[i + 2]), - b: Point::::new(positions[i + 3], positions[i + 4], positions[i + 5]), - c: Point::::new(positions[i + 6], positions[i + 7], positions[i + 8]), - normal: Point::::new( - face_normals[i / 9], - face_normals[i / 9 + 1], - face_normals[i / 9 + 2], - ), - node_index: 0, - }; - triangles.push(triangle); - } + let triangle = Triangle::new( + vector![positions[i], positions[i + 1], positions[i + 2]], + vector![positions[i + 3], positions[i + 4], positions[i + 5]], + vector![positions[i + 6], positions[i + 7], positions[i + 8]], + ); - let mut aabb = triangles[0].aabb(); - for triangle in &triangles { - aabb.grow_mut(&triangle.a); - aabb.grow_mut(&triangle.b); - aabb.grow_mut(&triangle.c); + if triangle.normal.angle(&BED_NORMAL) > max_angle { + slicable_triangles.push(triangle); + } else { + surface_triangles.push_back(triangle); + } } + slicable_triangles.shrink_to_fit(); - let slicer_options = SlicerOptions { - aabb, - bvh: Bvh::build(&mut triangles), - triangles, - layer_height, - }; - let base_slices = base_slices::create_base_slices(&slicer_options, &vec![]); + console_log!("Computing BVH"); + + let slicer_options = SlicerOptions { layer_height }; + + console_log!("Creating Surfaces"); + let surfaces = split_surface(surface_triangles); + + console_log!("Creating Slices"); + let slicable = Mesh::from(slicable_triangles); + let base_slices = create_base_slices(&slicer_options, &slicable); + console_log!("Done"); } diff --git a/bampy/src/slicer/base_slices.rs b/bampy/src/slicer/base_slices.rs index 15ed047..dc0e97b 100644 --- a/bampy/src/slicer/base_slices.rs +++ b/bampy/src/slicer/base_slices.rs @@ -1,34 +1,30 @@ use bvh::bvh::BvhNode; -use nalgebra::Point; -use super::{Line, SlicerOptions}; +use super::{line::Line3, mesh::Mesh, SlicerOptions}; #[derive(Debug)] pub struct BaseSlice { - z: f32, - lines: Vec>, + pub z: f32, + pub lines: Vec>, } -/** - * Creates base slices from the geometry, excluding surfaces. - * - * The slicse are not sorted or separated into rings. - */ -pub fn create_base_slices(options: &SlicerOptions, surface_triangles: &[bool]) -> Vec { - let layer_count = f32::floor(options.aabb.max.z / options.layer_height) as usize; +/// Creates base slices from the geometry, excluding surfaces. +/// The slicse are not sorted or separated into rings. +pub fn create_base_slices(options: &SlicerOptions, slicable: &Mesh) -> Vec { + let layer_count = f32::floor(slicable.aabb.max.z / options.layer_height) as usize; let mut base_slices = Vec::::with_capacity(layer_count); for i in 0..layer_count { let layer = i as f32 * options.layer_height; - let base_slice = BaseSlice { + let mut base_slice = BaseSlice { z: layer, lines: vec![], }; - let mut stack = Vec::::with_capacity(options.bvh.nodes.len()); + let mut stack = Vec::::with_capacity(slicable.bvh.nodes.len()); stack.push(0); while let Some(i) = stack.pop() { - match options.bvh.nodes[i] { + match slicable.bvh.nodes[i] { BvhNode::Node { parent_index: _, child_l_index, @@ -47,8 +43,11 @@ pub fn create_base_slices(options: &SlicerOptions, surface_triangles: &[bool]) - parent_index: _, shape_index, } => { - let triangle = options.triangles[shape_index]; - let a = Point::::new(triangle.a.x, triangle.a.y, layer); + slicable.triangles[shape_index] + .intersect_z(layer) + .map(|line| { + base_slice.lines.push(line); + }); } } } diff --git a/bampy/src/slicer/line.rs b/bampy/src/slicer/line.rs new file mode 100644 index 0000000..96ce89b --- /dev/null +++ b/bampy/src/slicer/line.rs @@ -0,0 +1,7 @@ +use nalgebra::{Scalar, Vector3}; + +#[derive(Debug, PartialEq, Clone, Copy)] +pub struct Line3 { + pub start: Vector3, + pub end: Vector3, +} diff --git a/bampy/src/slicer/mesh.rs b/bampy/src/slicer/mesh.rs new file mode 100644 index 0000000..400c4e2 --- /dev/null +++ b/bampy/src/slicer/mesh.rs @@ -0,0 +1,34 @@ +use bvh::{aabb::Aabb, bvh::Bvh}; +use nalgebra::{ClosedAdd, ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; +use num::{Float, FromPrimitive}; + +use super::triangle::Triangle; + +#[derive(Debug)] +pub struct Mesh { + pub aabb: Aabb, + pub bvh: Bvh, + pub triangles: Vec>, +} + +impl From>> for Mesh +where + T: SimdPartialOrd + Scalar + Copy + ClosedMul + ClosedAdd + ClosedSub + Float + FromPrimitive, +{ + fn from(mut triangles: Vec>) -> Self { + Self { + aabb: triangles + .get(0) + .map(|triangle| { + let mut aabb = triangle.aabb(); + for triangle in triangles.iter().skip(1) { + aabb.join_mut(&triangle.aabb); + } + aabb + }) + .unwrap_or_else(|| Aabb::empty()), + bvh: Bvh::build(&mut triangles), + triangles, + } + } +} diff --git a/bampy/src/slicer/mod.rs b/bampy/src/slicer/mod.rs index c9e3575..a362a3c 100644 --- a/bampy/src/slicer/mod.rs +++ b/bampy/src/slicer/mod.rs @@ -1,52 +1,12 @@ use std::fmt::Debug; -use bvh::{ - aabb::{Aabb, Bounded}, - bounding_hierarchy::BHShape, - bvh::Bvh, -}; -use nalgebra::{Point, Scalar, SimdPartialOrd}; - pub mod base_slices; - -#[derive(Debug, PartialEq, Clone, Copy)] -pub struct Triangle { - pub a: Point, - pub b: Point, - pub c: Point, - pub normal: Point, - pub node_index: usize, -} - -impl Bounded for Triangle { - fn aabb(&self) -> Aabb { - let mut aabb = self.a.aabb(); - aabb.grow_mut(&self.b); - aabb.grow_mut(&self.c); - aabb - } -} - -impl BHShape for Triangle { - fn set_bh_node_index(&mut self, node_index: usize) { - self.node_index = node_index; - } - - fn bh_node_index(&self) -> usize { - self.node_index - } -} - -#[derive(Debug, PartialEq, Clone, Copy)] -pub struct Line { - pub start: Point, - pub end: Point, -} +pub mod line; +pub mod mesh; +pub mod split_surface; +pub mod triangle; #[derive(Debug)] pub struct SlicerOptions { - pub aabb: bvh::aabb::Aabb, - pub bvh: Bvh, - pub triangles: Vec>, pub layer_height: f32, } diff --git a/bampy/src/slicer/split_surface.rs b/bampy/src/slicer/split_surface.rs new file mode 100644 index 0000000..827b53a --- /dev/null +++ b/bampy/src/slicer/split_surface.rs @@ -0,0 +1,45 @@ +use super::triangle::Triangle; +use bvh::bvh::{Bvh, BvhNode}; + +/// Splits a surface into connected surfaces. +pub fn split_surface(mut triangles: Vec>) -> Vec>> { + let mut surfaces = vec![]; + while let Some(triangle) = triangles.pop() { + let mut surface = vec![triangle]; + let mut bvh = Bvh::build(&mut surface); + triangles.retain_mut(|triangle| { + let mut stack = Vec::::new(); + stack.push(0); + while let Some(i) = stack.pop() { + match bvh.nodes[i] { + BvhNode::Node { + parent_index: _, + child_l_index, + child_l_aabb, + child_r_index, + child_r_aabb, + } => { + if triangle.intersects_aabb(&child_l_aabb) { + stack.push(child_l_index); + } + if triangle.intersects_aabb(&child_r_aabb) { + stack.push(child_r_index); + } + } + BvhNode::Leaf { + parent_index: _, + shape_index, + } => { + if triangle.connected_with_triangle(surface[shape_index]) { + surface.push(*triangle); + bvh.add_shape(&mut surface, surface.len() - 1); + return false; + } + } + } + } + true + }) + } + surfaces +} diff --git a/bampy/src/slicer/triangle.rs b/bampy/src/slicer/triangle.rs new file mode 100644 index 0000000..b650407 --- /dev/null +++ b/bampy/src/slicer/triangle.rs @@ -0,0 +1,113 @@ +use approx::{relative_eq, AbsDiffEq, RelativeEq}; +use bvh::{ + aabb::{Aabb, Bounded}, + bounding_hierarchy::BHShape, +}; +use nalgebra::{ClosedAdd, ClosedMul, ClosedSub, Scalar, SimdPartialOrd, Vector3}; +use num::{Float, FromPrimitive}; + +use super::line::Line3; + +#[derive(Debug, Clone, Copy)] +pub struct Triangle { + pub a: Vector3, + pub b: Vector3, + pub c: Vector3, + pub normal: Vector3, + node_index: usize, + pub aabb: Aabb, +} + +fn vec_inside_aabb( + vec: &Vector3, + aabb: &Aabb, +) -> bool { + vec.x >= aabb.min.x + && vec.x <= aabb.max.x + && vec.y >= aabb.min.y + && vec.y <= aabb.max.y + && vec.z >= aabb.min.z + && vec.z <= aabb.max.z +} + +impl Triangle +where + T: SimdPartialOrd + Scalar + Copy + ClosedMul + ClosedAdd + ClosedSub + Float + FromPrimitive, +{ + pub fn new(a: Vector3, b: Vector3, c: Vector3) -> Self { + let normal = (b - a).cross(&(c - a)); + let mut aabb = Aabb::with_bounds(a.into(), b.into()); + aabb.grow_mut(&c.into()); + Self { + a, + b, + c, + normal: normal.into(), + node_index: 0, + aabb, + } + } + + pub fn intersects_aabb(&self, aabb: &Aabb) -> bool { + vec_inside_aabb(&self.a, aabb) + || vec_inside_aabb(&self.b, aabb) + || vec_inside_aabb(&self.c, aabb) + } + + pub fn has_vec(&self, vec: Vector3) -> bool + where + T: RelativeEq + Clone, + ::Epsilon: Clone, + { + relative_eq!(self.a, vec) || relative_eq!(self.b, vec) || relative_eq!(self.c, vec) + } + + pub fn connected_with_triangle(&self, other: Triangle) -> bool + where + T: RelativeEq + Clone, + ::Epsilon: Clone, + { + self.has_vec(other.a) || self.has_vec(other.b) || self.has_vec(other.c) + } + + pub fn iter(&self) -> impl Iterator> { + vec![&self.a, &self.b, &self.c].into_iter() + } + + pub fn intersect_z(&self, z: T) -> Option> { + let mut intersection = Vec::with_capacity(3); + let mut last = self.c; + for point in self.iter() { + if point.z == z { + intersection.push(*point); + } else if last.z < z && point.z > z || last.z > z && point.z < z { + intersection.push(last.lerp(&point, (z - last.z) / (point.z - last.z))); + } + last = *point; + } + if intersection.len() == 2 { + Some(Line3 { + start: intersection[0], + end: intersection[1], + }) + } else { + None + } + } +} + +impl Bounded for Triangle { + fn aabb(&self) -> Aabb { + self.aabb + } +} + +impl BHShape for Triangle { + fn set_bh_node_index(&mut self, node_index: usize) { + self.node_index = node_index; + } + + fn bh_node_index(&self) -> usize { + self.node_index + } +} diff --git a/src/lib/slicer/worker.ts b/src/lib/slicer/worker.ts index c6ca2e3..c424b14 100644 --- a/src/lib/slicer/worker.ts +++ b/src/lib/slicer/worker.ts @@ -27,8 +27,8 @@ addEventListener('message', async (event: MessageEvent) => { await init(); slice( geometry.attributes.position.array as Float32Array, - geometry.attributes.normal.array as Float32Array, - event.data.data.layerHeight + event.data.data.layerHeight, + event.data.data.maxNonPlanarAngle ); } });