From 61d3b15c73466b0e97cbb0cebcd17af6ad5fe7ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thea=20Sch=C3=B6bl?= Date: Sun, 24 Mar 2024 16:45:17 +0100 Subject: [PATCH] feat: stuff --- bampy/src/lib.rs | 32 ++++--- bampy/src/slicer/base_slices.rs | 12 +-- bampy/src/slicer/line.rs | 10 +- bampy/src/slicer/mesh.rs | 20 ++-- bampy/src/slicer/mod.rs | 5 +- bampy/src/slicer/slice_rings.rs | 6 +- bampy/src/slicer/split_surface.rs | 2 +- bampy/src/slicer/trace_surface.rs | 77 ++++++++------- bampy/src/slicer/triangle.rs | 84 ++++++----------- bampy/src/slicer/z_projection.rs | 150 ++++++++++++++++++++++++++++++ 10 files changed, 260 insertions(+), 138 deletions(-) create mode 100644 bampy/src/slicer/z_projection.rs diff --git a/bampy/src/lib.rs b/bampy/src/lib.rs index 3495f38..a081dbb 100644 --- a/bampy/src/lib.rs +++ b/bampy/src/lib.rs @@ -6,7 +6,7 @@ use wasm_bindgen::prelude::wasm_bindgen; use crate::slicer::{ base_slices::create_slices, mesh::Mesh, split_surface::split_surface, - trace_surface::trace_surface, triangle::Triangle, SlicerOptions, + trace_surface::trace_surface, triangle::Triangle, FloatValue, SlicerOptions, }; mod slicer; @@ -57,24 +57,24 @@ pub fn slice( assert_eq!(positions.len() % 9, 0); - let mut surface_triangles = Vec::>::with_capacity(positions.len() / 9); - let mut slicable_triangles = Vec::>::with_capacity(positions.len() / 9); + let mut surface_triangles = Vec::::with_capacity(positions.len() / 9); + let mut slicable_triangles = Vec::::with_capacity(positions.len() / 9); for i in (0..positions.len()).step_by(9) { let triangle = Triangle::new( vector![ - positions[i] as f64, - positions[i + 1] as f64, - positions[i + 2] as f64 + positions[i] as FloatValue, + positions[i + 1] as FloatValue, + positions[i + 2] as FloatValue ], vector![ - positions[i + 3] as f64, - positions[i + 4] as f64, - positions[i + 5] as f64 + positions[i + 3] as FloatValue, + positions[i + 4] as FloatValue, + positions[i + 5] as FloatValue ], vector![ - positions[i + 6] as f64, - positions[i + 7] as f64, - positions[i + 8] as f64 + positions[i + 6] as FloatValue, + positions[i + 7] as FloatValue, + positions[i + 8] as FloatValue ], ); @@ -96,14 +96,18 @@ pub fn slice( let slicable = Mesh::from(slicable_triangles); console_log!("Creating Slices"); let mut slices = create_slices(&slicer_options, &slicable); - console_log!("Done"); + console_log!("Tracing Surfaces"); + let a = max_angle.tan(); for slice in &mut slices { for surface in &surfaces { - trace_surface(slice, surface) + if surface.aabb.min.z <= slice.z && surface.aabb.max.z > slice.z { + trace_surface(slice, surface, a); + } } } + console_log!("Done"); SliceResult { slices: slices .into_iter() diff --git a/bampy/src/slicer/base_slices.rs b/bampy/src/slicer/base_slices.rs index e3c994a..81828aa 100644 --- a/bampy/src/slicer/base_slices.rs +++ b/bampy/src/slicer/base_slices.rs @@ -2,25 +2,25 @@ use super::{ line::Line3, mesh::Mesh, slice_rings::{find_slice_rings, SliceRing}, - SlicerOptions, + FloatValue, SlicerOptions, }; use bvh::bvh::BvhNode; #[derive(Debug)] pub struct BaseSlice { - pub z: f64, - pub lines: Vec>, + pub z: FloatValue, + pub lines: Vec, } /// Creates base slices from the geometry, excluding surfaces. /// The slicse are not sorted or separated into rings. -pub fn create_slices(options: &SlicerOptions, slicable: &Mesh) -> Vec { - let layer_count = f64::floor(slicable.aabb.max.z / options.layer_height) as usize; +pub fn create_slices(options: &SlicerOptions, slicable: &Mesh) -> Vec { + let layer_count = (slicable.aabb.max.z / options.layer_height).floor() as usize; let mut rings = vec![]; let mut layer_index = 0; for i in 0..layer_count { - let layer = i as f64 * options.layer_height; + let layer = i as FloatValue * options.layer_height; let mut base_slice = BaseSlice { z: layer, lines: vec![], diff --git a/bampy/src/slicer/line.rs b/bampy/src/slicer/line.rs index 00b1ba2..5c16b2d 100644 --- a/bampy/src/slicer/line.rs +++ b/bampy/src/slicer/line.rs @@ -1,11 +1,13 @@ -use nalgebra::{Scalar, Vector3}; +use nalgebra::Vector3; + +use super::FloatValue; /// Lines are assumed to be with respect to the XY plane. /// The order of the points is therefore significant, /// clockwise order with respect to the inside of the shape, /// meaning the inside is on the right hand side of the line. #[derive(Debug, PartialEq, Clone, Copy)] -pub struct Line3 { - pub start: Vector3, - pub end: Vector3, +pub struct Line3 { + pub start: Vector3, + pub end: Vector3, } diff --git a/bampy/src/slicer/mesh.rs b/bampy/src/slicer/mesh.rs index 659c309..068897d 100644 --- a/bampy/src/slicer/mesh.rs +++ b/bampy/src/slicer/mesh.rs @@ -1,21 +1,15 @@ +use super::{triangle::Triangle, FloatValue}; 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>, +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 { +impl From> for Mesh { + fn from(mut triangles: Vec) -> Self { Self { aabb: triangles .get(0) diff --git a/bampy/src/slicer/mod.rs b/bampy/src/slicer/mod.rs index 2fa5e05..d19b3e2 100644 --- a/bampy/src/slicer/mod.rs +++ b/bampy/src/slicer/mod.rs @@ -7,8 +7,11 @@ pub mod slice_rings; pub mod split_surface; pub mod trace_surface; pub mod triangle; +pub mod z_projection; + +pub type FloatValue = f64; #[derive(Debug)] pub struct SlicerOptions { - pub layer_height: f64, + pub layer_height: FloatValue, } diff --git a/bampy/src/slicer/slice_rings.rs b/bampy/src/slicer/slice_rings.rs index bf36b69..fff7922 100644 --- a/bampy/src/slicer/slice_rings.rs +++ b/bampy/src/slicer/slice_rings.rs @@ -3,13 +3,13 @@ use nalgebra::Vector3; use crate::console_log; -use super::base_slices::BaseSlice; +use super::{base_slices::BaseSlice, FloatValue}; #[derive(Debug)] pub struct SliceRing { - pub z: f64, + pub z: FloatValue, /// The points of the ring, in clockwise order. - pub points: Vec>, + pub points: Vec>, } pub fn find_slice_rings(mut slice: BaseSlice, layer_index: &mut u32) -> Vec { diff --git a/bampy/src/slicer/split_surface.rs b/bampy/src/slicer/split_surface.rs index 0ff5b67..4af63fd 100644 --- a/bampy/src/slicer/split_surface.rs +++ b/bampy/src/slicer/split_surface.rs @@ -2,7 +2,7 @@ use super::{mesh::Mesh, triangle::Triangle}; use bvh::bvh::{Bvh, BvhNode}; /// Splits a surface into connected surfaces. -pub fn split_surface(mut triangles: Vec>) -> Vec> { +pub fn split_surface(mut triangles: Vec) -> Vec { let mut surfaces = vec![]; while let Some(triangle) = triangles.pop() { let mut surface = vec![triangle]; diff --git a/bampy/src/slicer/trace_surface.rs b/bampy/src/slicer/trace_surface.rs index 7a852d4..17dfa34 100644 --- a/bampy/src/slicer/trace_surface.rs +++ b/bampy/src/slicer/trace_surface.rs @@ -1,48 +1,47 @@ use bvh::bvh::BvhNode; -use super::{mesh::Mesh, slice_rings::SliceRing}; +use super::{mesh::Mesh, slice_rings::SliceRing, z_projection::ToolpathIntersects, FloatValue}; -pub fn trace_surface(slice: &mut SliceRing, surface: &Mesh) { - loop { - let mut mutated = false; - slice.points.retain_mut(|triangle| { - let mut stack = Vec::::new(); - stack.push(0); - while let Some(i) = stack.pop() { - match surface.bvh.nodes[i] { - BvhNode::Node { - parent_index: _, - child_l_index, - child_l_aabb, - child_r_index, - child_r_aabb, - } => { - if triangle.has_point_in_aabb(&child_l_aabb) { - stack.push(child_l_index); - } - if triangle.has_point_in_aabb(&child_r_aabb) { - stack.push(child_r_index); - } +pub fn trace_surface(slice: &mut SliceRing, surface: &Mesh, a: FloatValue) { + slice.points.retain_mut(|point| { + let mut stack = Vec::::new(); + stack.push(0); + while let Some(i) = stack.pop() { + match surface.bvh.nodes[i] { + BvhNode::Node { + parent_index: _, + child_l_index, + child_l_aabb, + child_r_index, + child_r_aabb, + } => { + if child_l_aabb.toolpath_intersects(point, a) { + stack.push(child_l_index); } - BvhNode::Leaf { - parent_index: _, - shape_index, - } => { - if triangle.shares_point_with_triangle(surface[shape_index]) { - mutated = true; - surface.push(*triangle); - let index = surface.len() - 1; - bvh.add_shape(&mut surface, index); - aabb.join_mut(&triangle.aabb); - return false; - } + if child_r_aabb.toolpath_intersects(point, a) { + stack.push(child_r_index); + } + } + BvhNode::Leaf { + parent_index: _, + shape_index, + } => { + let triangle = &surface.triangles[shape_index]; + macro_rules! check { + ( $var:ident ) => {{ + let x = point.x - triangle.$var.x; + let y = point.y - triangle.$var.y; + (point.z > triangle.aabb.min.z + && FloatValue::sqrt(x * x + y * y) + < (triangle.$var.z - point.z).abs() * a) + }}; + } + if check!(a) || check!(b) || check!(c) { + return false; } } } - true - }); - if !mutated { - break; } - } + true + }); } diff --git a/bampy/src/slicer/triangle.rs b/bampy/src/slicer/triangle.rs index a18b2c2..04e475e 100644 --- a/bampy/src/slicer/triangle.rs +++ b/bampy/src/slicer/triangle.rs @@ -1,28 +1,24 @@ -use approx::{relative_eq, AbsDiffEq, RelativeEq}; +use approx::relative_eq; use bvh::{ aabb::{Aabb, Bounded}, bounding_hierarchy::BHShape, }; -use nalgebra::{ClosedAdd, ClosedMul, ClosedSub, Point3, Scalar, SimdPartialOrd, Vector3}; -use num::{Float, FromPrimitive}; +use nalgebra::{Point3, Vector3}; -use super::line::Line3; +use super::{line::Line3, FloatValue}; #[derive(Debug, Clone, Copy)] -pub struct Triangle { - pub a: Vector3, - pub b: Vector3, - pub c: Vector3, - pub normal: Vector3, +pub struct Triangle { + pub a: Vector3, + pub b: Vector3, + pub c: Vector3, + pub normal: Vector3, node_index: usize, - pub aabb: Aabb, + pub aabb: Aabb, } #[inline(always)] -fn vec_inside_aabb( - vec: &Vector3, - aabb: &Aabb, -) -> bool { +fn vec_inside_aabb(vec: &Vector3, aabb: &Aabb) -> bool { macro_rules! within { ($axis:ident) => { ((vec.$axis >= aabb.min.$axis && vec.$axis <= aabb.max.$axis) @@ -33,19 +29,8 @@ fn vec_inside_aabb Triangle -where - T: SimdPartialOrd - + RelativeEq - + Scalar - + Copy - + ClosedMul - + ClosedAdd - + ClosedSub - + Float - + FromPrimitive, -{ - pub fn new(a: Vector3, b: Vector3, c: Vector3) -> Self { +impl Triangle { + pub fn new(a: Vector3, b: Vector3, c: Vector3) -> Self { Self { a, b, @@ -54,57 +39,42 @@ where node_index: 0, aabb: Aabb::with_bounds( Point3::new( - T::min(T::min(a.x, b.x), c.x), - T::min(T::min(a.y, b.y), c.y), - T::min(T::min(a.z, b.z), c.z), + FloatValue::min(FloatValue::max(a.x, b.x), c.x), + FloatValue::min(FloatValue::min(a.y, b.y), c.y), + FloatValue::min(FloatValue::min(a.z, b.z), c.z), ), Point3::new( - T::max(T::max(a.x, b.x), c.x), - T::max(T::max(a.y, b.y), c.y), - T::max(T::max(a.z, b.z), c.z), + FloatValue::max(FloatValue::max(a.x, b.x), c.x), + FloatValue::max(FloatValue::max(a.y, b.y), c.y), + FloatValue::max(FloatValue::max(a.z, b.z), c.z), ), ), } } - pub fn has_point_in_aabb(&self, aabb: &Aabb) -> bool { + pub fn has_point_in_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, - { + pub fn has_vec(&self, vec: Vector3) -> bool { relative_eq!(self.a, vec) || relative_eq!(self.b, vec) || relative_eq!(self.c, vec) } - pub fn shares_point_with_triangle(&self, other: Triangle) -> bool - where - T: RelativeEq + Clone, - ::Epsilon: Clone, - { + pub fn shares_point_with_triangle(&self, other: Triangle) -> bool { self.has_vec(other.a) || self.has_vec(other.b) || self.has_vec(other.c) } - pub fn shares_edge_with_triangle(&self, other: Triangle) -> bool - where - T: RelativeEq + Clone, - ::Epsilon: Clone, - { + pub fn shares_edge_with_triangle(&self, other: Triangle) -> bool { let a = self.has_vec(other.a); let b = self.has_vec(other.b); let c = self.has_vec(other.c); a && b || a && c || b && c } - pub fn intersect_z(&self, z: T) -> Option> - where - ::Epsilon: Clone, - { - let mut intersection = Vec::>::with_capacity(3); + pub fn intersect_z(&self, z: FloatValue) -> Option { + let mut intersection = Vec::>::with_capacity(3); let mut last = &self.c; for point in [self.a, self.b, self.c].iter() { if relative_eq!(point.z, z) { @@ -137,13 +107,13 @@ where } } -impl Bounded for Triangle { - fn aabb(&self) -> Aabb { +impl Bounded for Triangle { + fn aabb(&self) -> Aabb { self.aabb } } -impl BHShape for Triangle { +impl BHShape for Triangle { fn set_bh_node_index(&mut self, node_index: usize) { self.node_index = node_index; } diff --git a/bampy/src/slicer/z_projection.rs b/bampy/src/slicer/z_projection.rs new file mode 100644 index 0000000..18b0291 --- /dev/null +++ b/bampy/src/slicer/z_projection.rs @@ -0,0 +1,150 @@ +use bvh::aabb::Aabb; +use nalgebra::{Point2, Vector2, Vector3}; + +use super::{triangle::Triangle, FloatValue}; + +pub trait ProjectToolpath { + /// Projects the hypothetical toolpath of the object onto the z plane + fn project_toolpath_onto_z(&self, z: FloatValue, a: FloatValue) -> Option; +} + +pub trait ToolpathIntersects: ProjectToolpath { + /// Checks if a hypothetical toolpath that draws the object could intersect + /// with the given point, given the tangent of the angle of the toolhead + fn toolpath_intersects(&self, point: &Vector3, a: FloatValue) -> bool; +} + +pub trait ToolpathIntersection { + fn toolpath_intersection(&self) {} +} + +impl ProjectToolpath> for Aabb { + fn project_toolpath_onto_z(&self, z: FloatValue, a: FloatValue) -> Option> { + if z < self.min.z { + return None; + } + let delta_target_z = z - self.min.z; + let delta_z = self.max.z - self.min.z; + let delta = a * (delta_target_z / delta_z); + + let dx = (self.max.x - self.min.x) * delta; + let dy = (self.max.y - self.min.y) * delta; + + Some(Aabb { + min: Point2::new(self.min.x - dx, self.min.y - dy), + max: Point2::new(self.max.x + dx, self.max.y + dy), + }) + } +} + +impl ToolpathIntersects> for Aabb { + fn toolpath_intersects(&self, point: &Vector3, a: FloatValue) -> bool { + if let Some(aabb) = self.project_toolpath_onto_z(point.z, a) { + aabb.approx_contains_eps(&Point2::new(point.x, point.y), FloatValue::EPSILON) + } else { + false + } + } +} + +#[derive(Debug)] +pub struct Triangle2D { + pub a: Point2, + pub b: Point2, + pub c: Point2, +} + +impl ProjectToolpath for Triangle { + fn project_toolpath_onto_z(&self, z: FloatValue, a: FloatValue) -> Option { + if z <= self.aabb.min.z { + return None; + } + + let cx = (self.a.x + self.b.x + self.c.x) / 3.0; + let cy = (self.a.y + self.b.y + self.c.y) / 3.0; + + let expand = z - self.aabb.min.z; + + macro_rules! project { + ( $val:ident ) => { + let delta = FloatValue::min(z - self.$val.z, 0.0); + let dx = self.$val.x - cx; + let dy = self.$val.y - cy; + let px = self.$val.x + delta * cx; + let py = self.$val.y + delta * cy; + + Point2::new(px + expand * dy, py + expand * dx) + }; + } + + let ap = Point2::new(self.a.x, self.b.y); + + macro_rules! values { + ( $val:ident ) => {{ + let delta = 2.0 * a * (z - self.$val.z); + let dx = self.$val.x - cx; + let dy = self.$val.y - cy; + Point2::new(self.$val.x + dx * delta, self.$val.y + dy * delta) + }}; + } + + Some(Triangle2D { + a: values!(a), + b: values!(b), + c: values!(c), + }) + } +} + +#[cfg(test)] +mod tests { + use approx::assert_relative_eq; + use bvh::aabb::Aabb; + use nalgebra::{Point3, Vector2, Vector3}; + + use crate::slicer::{triangle::Triangle, z_projection::ProjectToolpath, FloatValue}; + + #[test] + fn test_project_aabb_toolpath() { + let aabb = Aabb { + min: Point3::new(0.0, 0.0, 0.0), + max: Point3::new(1.0, 1.0, 1.0), + }; + let a = FloatValue::to_radians(45.0).tan(); + + let projected = aabb.project_toolpath_onto_z(0.5, a).unwrap(); + + assert_relative_eq!(projected.min.x, -0.5); + assert_relative_eq!(projected.min.y, -0.5); + assert_relative_eq!(projected.max.x, 1.5); + assert_relative_eq!(projected.max.y, 1.5); + + let projected_b = aabb.project_toolpath_onto_z(0.0, a).unwrap(); + assert_relative_eq!(projected_b.min.x, 0.0); + assert_relative_eq!(projected_b.min.y, 0.0); + assert_relative_eq!(projected_b.max.x, 1.0); + assert_relative_eq!(projected_b.max.y, 1.0); + } + + #[test] + fn test_project_triangle_toolpath() { + let triangle = Triangle::new( + Vector3::new(0.0, 0.0, 0.0), + Vector3::new(0.0, 1.5, 1.0), + Vector3::new(-0.6, -1.4, 0.2), + ); + let a = FloatValue::to_radians(30.0).tan(); + + let projected = triangle.project_toolpath_onto_z(0.7, a).unwrap(); + println!("{:?}", projected); + + assert_relative_eq!(projected.a.x, -0.86, epsilon = 0.01); + assert_relative_eq!(projected.a.y, -0.86, epsilon = 0.01); + + assert_relative_eq!(projected.b.x, -0.54, epsilon = 0.01); + assert_relative_eq!(projected.b.y, 2.59, epsilon = 0.01); + + assert_relative_eq!(projected.c.x, 2.59, epsilon = 0.01); + assert_relative_eq!(projected.c.y, 1.94, epsilon = 0.01); + } +}