mirror of
https://github.com/Theaninova/Bampy.git
synced 2026-01-06 16:52:48 +00:00
feat: improvements
This commit is contained in:
@@ -9,6 +9,7 @@ crate-type = ["cdylib", "rlib"]
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["console_error_panic_hook"]
|
default = ["console_error_panic_hook"]
|
||||||
|
console_error_panic_hook = ["dep:console_error_panic_hook"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasm-bindgen = "0.2.84"
|
wasm-bindgen = "0.2.84"
|
||||||
@@ -21,6 +22,7 @@ console_error_panic_hook = { version = "0.1.7", optional = true }
|
|||||||
bvh = "0.8.0"
|
bvh = "0.8.0"
|
||||||
nalgebra = "0.32.4"
|
nalgebra = "0.32.4"
|
||||||
num = "0.4.1"
|
num = "0.4.1"
|
||||||
|
approx = "0.5.1"
|
||||||
|
|
||||||
[dependencies.getrandom]
|
[dependencies.getrandom]
|
||||||
features = ["js"]
|
features = ["js"]
|
||||||
|
|||||||
@@ -1,46 +1,49 @@
|
|||||||
use bvh::{aabb::Bounded, bvh::Bvh};
|
use std::collections::LinkedList;
|
||||||
use nalgebra::Point;
|
|
||||||
|
use nalgebra::{vector, Vector3};
|
||||||
use wasm_bindgen::prelude::wasm_bindgen;
|
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 slicer;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
const BED_NORMAL: Vector3<f32> = vector![0f32, 0f32, 1f32];
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn slice(positions: &[f32], face_normals: &[f32], layer_height: f32) {
|
pub fn slice(positions: &[f32], layer_height: f32, max_angle: f32) {
|
||||||
debug_assert_eq!(positions.len() % 9, 0);
|
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||||
debug_assert_eq!(face_normals.len() % 3, 0);
|
|
||||||
debug_assert_eq!(positions.len() / 9, face_normals.len() / 3);
|
|
||||||
|
|
||||||
let mut triangles = Vec::with_capacity(positions.len() / 9);
|
assert_eq!(positions.len() % 9, 0);
|
||||||
|
|
||||||
|
let mut surface_triangles = LinkedList::<Triangle<f32>>::new();
|
||||||
|
let mut slicable_triangles = Vec::with_capacity(positions.len() / 9);
|
||||||
for i in (0..positions.len()).step_by(9) {
|
for i in (0..positions.len()).step_by(9) {
|
||||||
let triangle = Triangle {
|
let triangle = Triangle::new(
|
||||||
a: Point::<f32, 3>::new(positions[i], positions[i + 1], positions[i + 2]),
|
vector![positions[i], positions[i + 1], positions[i + 2]],
|
||||||
b: Point::<f32, 3>::new(positions[i + 3], positions[i + 4], positions[i + 5]),
|
vector![positions[i + 3], positions[i + 4], positions[i + 5]],
|
||||||
c: Point::<f32, 3>::new(positions[i + 6], positions[i + 7], positions[i + 8]),
|
vector![positions[i + 6], positions[i + 7], positions[i + 8]],
|
||||||
normal: Point::<f32, 3>::new(
|
);
|
||||||
face_normals[i / 9],
|
|
||||||
face_normals[i / 9 + 1],
|
|
||||||
face_normals[i / 9 + 2],
|
|
||||||
),
|
|
||||||
node_index: 0,
|
|
||||||
};
|
|
||||||
triangles.push(triangle);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut aabb = triangles[0].aabb();
|
if triangle.normal.angle(&BED_NORMAL) > max_angle {
|
||||||
for triangle in &triangles {
|
slicable_triangles.push(triangle);
|
||||||
aabb.grow_mut(&triangle.a);
|
} else {
|
||||||
aabb.grow_mut(&triangle.b);
|
surface_triangles.push_back(triangle);
|
||||||
aabb.grow_mut(&triangle.c);
|
}
|
||||||
}
|
}
|
||||||
|
slicable_triangles.shrink_to_fit();
|
||||||
|
|
||||||
let slicer_options = SlicerOptions {
|
console_log!("Computing BVH");
|
||||||
aabb,
|
|
||||||
bvh: Bvh::build(&mut triangles),
|
let slicer_options = SlicerOptions { layer_height };
|
||||||
triangles,
|
|
||||||
layer_height,
|
console_log!("Creating Surfaces");
|
||||||
};
|
let surfaces = split_surface(surface_triangles);
|
||||||
let base_slices = base_slices::create_base_slices(&slicer_options, &vec![]);
|
|
||||||
|
console_log!("Creating Slices");
|
||||||
|
let slicable = Mesh::from(slicable_triangles);
|
||||||
|
let base_slices = create_base_slices(&slicer_options, &slicable);
|
||||||
|
console_log!("Done");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,30 @@
|
|||||||
use bvh::bvh::BvhNode;
|
use bvh::bvh::BvhNode;
|
||||||
use nalgebra::Point;
|
|
||||||
|
|
||||||
use super::{Line, SlicerOptions};
|
use super::{line::Line3, mesh::Mesh, SlicerOptions};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BaseSlice {
|
pub struct BaseSlice {
|
||||||
z: f32,
|
pub z: f32,
|
||||||
lines: Vec<Line<f32, 3>>,
|
pub lines: Vec<Line3<f32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/// Creates base slices from the geometry, excluding surfaces.
|
||||||
* 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<f32>) -> Vec<BaseSlice> {
|
||||||
* The slicse are not sorted or separated into rings.
|
let layer_count = f32::floor(slicable.aabb.max.z / options.layer_height) as usize;
|
||||||
*/
|
|
||||||
pub fn create_base_slices(options: &SlicerOptions, surface_triangles: &[bool]) -> Vec<BaseSlice> {
|
|
||||||
let layer_count = f32::floor(options.aabb.max.z / options.layer_height) as usize;
|
|
||||||
let mut base_slices = Vec::<BaseSlice>::with_capacity(layer_count);
|
let mut base_slices = Vec::<BaseSlice>::with_capacity(layer_count);
|
||||||
|
|
||||||
for i in 0..layer_count {
|
for i in 0..layer_count {
|
||||||
let layer = i as f32 * options.layer_height;
|
let layer = i as f32 * options.layer_height;
|
||||||
let base_slice = BaseSlice {
|
let mut base_slice = BaseSlice {
|
||||||
z: layer,
|
z: layer,
|
||||||
lines: vec![],
|
lines: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut stack = Vec::<usize>::with_capacity(options.bvh.nodes.len());
|
let mut stack = Vec::<usize>::with_capacity(slicable.bvh.nodes.len());
|
||||||
stack.push(0);
|
stack.push(0);
|
||||||
while let Some(i) = stack.pop() {
|
while let Some(i) = stack.pop() {
|
||||||
match options.bvh.nodes[i] {
|
match slicable.bvh.nodes[i] {
|
||||||
BvhNode::Node {
|
BvhNode::Node {
|
||||||
parent_index: _,
|
parent_index: _,
|
||||||
child_l_index,
|
child_l_index,
|
||||||
@@ -47,8 +43,11 @@ pub fn create_base_slices(options: &SlicerOptions, surface_triangles: &[bool]) -
|
|||||||
parent_index: _,
|
parent_index: _,
|
||||||
shape_index,
|
shape_index,
|
||||||
} => {
|
} => {
|
||||||
let triangle = options.triangles[shape_index];
|
slicable.triangles[shape_index]
|
||||||
let a = Point::<f32, 3>::new(triangle.a.x, triangle.a.y, layer);
|
.intersect_z(layer)
|
||||||
|
.map(|line| {
|
||||||
|
base_slice.lines.push(line);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
bampy/src/slicer/line.rs
Normal file
7
bampy/src/slicer/line.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
use nalgebra::{Scalar, Vector3};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
|
pub struct Line3<T: Scalar> {
|
||||||
|
pub start: Vector3<T>,
|
||||||
|
pub end: Vector3<T>,
|
||||||
|
}
|
||||||
34
bampy/src/slicer/mesh.rs
Normal file
34
bampy/src/slicer/mesh.rs
Normal file
@@ -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<T: SimdPartialOrd + Scalar + Copy> {
|
||||||
|
pub aabb: Aabb<T, 3>,
|
||||||
|
pub bvh: Bvh<T, 3>,
|
||||||
|
pub triangles: Vec<Triangle<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<Vec<Triangle<T>>> for Mesh<T>
|
||||||
|
where
|
||||||
|
T: SimdPartialOrd + Scalar + Copy + ClosedMul + ClosedAdd + ClosedSub + Float + FromPrimitive,
|
||||||
|
{
|
||||||
|
fn from(mut triangles: Vec<Triangle<T>>) -> 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,52 +1,12 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use bvh::{
|
|
||||||
aabb::{Aabb, Bounded},
|
|
||||||
bounding_hierarchy::BHShape,
|
|
||||||
bvh::Bvh,
|
|
||||||
};
|
|
||||||
use nalgebra::{Point, Scalar, SimdPartialOrd};
|
|
||||||
|
|
||||||
pub mod base_slices;
|
pub mod base_slices;
|
||||||
|
pub mod line;
|
||||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
pub mod mesh;
|
||||||
pub struct Triangle<T: Scalar, const D: usize> {
|
pub mod split_surface;
|
||||||
pub a: Point<T, D>,
|
pub mod triangle;
|
||||||
pub b: Point<T, D>,
|
|
||||||
pub c: Point<T, D>,
|
|
||||||
pub normal: Point<T, D>,
|
|
||||||
pub node_index: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SimdPartialOrd + Scalar + Copy, const D: usize> Bounded<T, D> for Triangle<T, D> {
|
|
||||||
fn aabb(&self) -> Aabb<T, D> {
|
|
||||||
let mut aabb = self.a.aabb();
|
|
||||||
aabb.grow_mut(&self.b);
|
|
||||||
aabb.grow_mut(&self.c);
|
|
||||||
aabb
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: SimdPartialOrd + Scalar + Copy, const D: usize> BHShape<T, D> for Triangle<T, D> {
|
|
||||||
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<T: Scalar, const D: usize> {
|
|
||||||
pub start: Point<T, D>,
|
|
||||||
pub end: Point<T, D>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SlicerOptions {
|
pub struct SlicerOptions {
|
||||||
pub aabb: bvh::aabb::Aabb<f32, 3>,
|
|
||||||
pub bvh: Bvh<f32, 3>,
|
|
||||||
pub triangles: Vec<Triangle<f32, 3>>,
|
|
||||||
pub layer_height: f32,
|
pub layer_height: f32,
|
||||||
}
|
}
|
||||||
|
|||||||
45
bampy/src/slicer/split_surface.rs
Normal file
45
bampy/src/slicer/split_surface.rs
Normal file
@@ -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<Triangle<f32>>) -> Vec<Vec<Triangle<f32>>> {
|
||||||
|
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::<usize>::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
|
||||||
|
}
|
||||||
113
bampy/src/slicer/triangle.rs
Normal file
113
bampy/src/slicer/triangle.rs
Normal file
@@ -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<T: SimdPartialOrd + Scalar + Copy> {
|
||||||
|
pub a: Vector3<T>,
|
||||||
|
pub b: Vector3<T>,
|
||||||
|
pub c: Vector3<T>,
|
||||||
|
pub normal: Vector3<T>,
|
||||||
|
node_index: usize,
|
||||||
|
pub aabb: Aabb<T, 3>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vec_inside_aabb<T: SimdPartialOrd + Scalar + Copy + Float>(
|
||||||
|
vec: &Vector3<T>,
|
||||||
|
aabb: &Aabb<T, 3>,
|
||||||
|
) -> 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<T> Triangle<T>
|
||||||
|
where
|
||||||
|
T: SimdPartialOrd + Scalar + Copy + ClosedMul + ClosedAdd + ClosedSub + Float + FromPrimitive,
|
||||||
|
{
|
||||||
|
pub fn new(a: Vector3<T>, b: Vector3<T>, c: Vector3<T>) -> 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<T, 3>) -> 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<T>) -> bool
|
||||||
|
where
|
||||||
|
T: RelativeEq + Clone,
|
||||||
|
<T as AbsDiffEq>::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<T>) -> bool
|
||||||
|
where
|
||||||
|
T: RelativeEq + Clone,
|
||||||
|
<T as AbsDiffEq>::Epsilon: Clone,
|
||||||
|
{
|
||||||
|
self.has_vec(other.a) || self.has_vec(other.b) || self.has_vec(other.c)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = &Vector3<T>> {
|
||||||
|
vec![&self.a, &self.b, &self.c].into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersect_z(&self, z: T) -> Option<Line3<T>> {
|
||||||
|
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<T: SimdPartialOrd + Scalar + Copy> Bounded<T, 3> for Triangle<T> {
|
||||||
|
fn aabb(&self) -> Aabb<T, 3> {
|
||||||
|
self.aabb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SimdPartialOrd + Scalar + Copy> BHShape<T, 3> for Triangle<T> {
|
||||||
|
fn set_bh_node_index(&mut self, node_index: usize) {
|
||||||
|
self.node_index = node_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bh_node_index(&self) -> usize {
|
||||||
|
self.node_index
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,8 +27,8 @@ addEventListener('message', async (event: MessageEvent<WorkerEvent>) => {
|
|||||||
await init();
|
await init();
|
||||||
slice(
|
slice(
|
||||||
geometry.attributes.position.array as Float32Array,
|
geometry.attributes.position.array as Float32Array,
|
||||||
geometry.attributes.normal.array as Float32Array,
|
event.data.data.layerHeight,
|
||||||
event.data.data.layerHeight
|
event.data.data.maxNonPlanarAngle
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user