This commit is contained in:
2024-03-11 00:43:31 +01:00
parent 00cfdfd46e
commit 608c2b08ec
9 changed files with 181 additions and 31 deletions

View File

@@ -23,6 +23,8 @@ bvh = "0.8.0"
nalgebra = "0.32.4"
num = "0.4.1"
approx = "0.5.1"
serde = "1.0.197"
tsify = { version = "0.4.5", features = ["js"] }
[dependencies.getrandom]
features = ["js"]

View File

@@ -1,8 +1,10 @@
use nalgebra::{vector, Vector3};
use serde::{Deserialize, Serialize};
use tsify::Tsify;
use wasm_bindgen::prelude::wasm_bindgen;
use crate::slicer::{
base_slices::create_base_slices, mesh::Mesh, split_surface::split_surface, triangle::Triangle,
base_slices::create_slices, mesh::Mesh, split_surface::split_surface, triangle::Triangle,
SlicerOptions,
};
@@ -11,8 +13,32 @@ mod util;
const BED_NORMAL: Vector3<f32> = vector![0f32, 0f32, 1f32];
#[derive(Tsify, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[tsify(from_wasm_abi)]
pub struct SliceOptions {
#[tsify(type = "Float32Array")]
positions: Vec<f32>,
layer_height: f32,
max_angle: f32,
}
#[derive(Tsify, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[tsify(into_wasm_abi)]
pub struct SliceResult {
#[tsify(type = "Array<Float32Array>")]
rings: Vec<Vec<f32>>,
}
#[wasm_bindgen]
pub fn slice(positions: &[f32], layer_height: f32, max_angle: f32) {
pub fn slice(
SliceOptions {
positions,
layer_height,
max_angle,
}: SliceOptions,
) -> SliceResult {
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
assert_eq!(positions.len() % 9, 0);
@@ -29,21 +55,34 @@ pub fn slice(positions: &[f32], layer_height: f32, max_angle: f32) {
if triangle.normal.angle(&BED_NORMAL) > max_angle {
slicable_triangles.push(triangle);
} else {
slicable_triangles.push(triangle);
surface_triangles.push(triangle);
}
}
slicable_triangles.shrink_to_fit();
surface_triangles.shrink_to_fit();
console_log!("Computing BVH");
let slicer_options = SlicerOptions { layer_height };
console_log!("Creating Surfaces");
let surfaces = split_surface(surface_triangles);
console_log!("Creating Slices");
console_log!("Computing BVH");
let slicable = Mesh::from(slicable_triangles);
let base_slices = create_base_slices(&slicer_options, &slicable);
console_log!("Creating Slices");
let slices = create_slices(&slicer_options, &slicable);
console_log!("Done");
SliceResult {
rings: slices
.into_iter()
.map(|slice| {
slice
.points
.into_iter()
.flat_map(|point| [point.x, point.y, point.z])
.collect()
})
.collect(),
}
}

View File

@@ -1,6 +1,13 @@
use bvh::bvh::BvhNode;
use super::{line::Line3, mesh::Mesh, SlicerOptions};
use crate::console_log;
use super::{
line::Line3,
mesh::Mesh,
slice_rings::{find_slice_rings, SliceRing},
SlicerOptions,
};
#[derive(Debug)]
pub struct BaseSlice {
@@ -10,11 +17,12 @@ pub struct BaseSlice {
/// 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> {
pub fn create_slices(options: &SlicerOptions, slicable: &Mesh<f32>) -> Vec<SliceRing> {
let layer_count = f32::floor(slicable.aabb.max.z / options.layer_height) as usize;
let mut base_slices = Vec::<BaseSlice>::with_capacity(layer_count);
let mut rings = vec![];
for i in 0..layer_count {
console_log!("Layer {}", i);
let layer = i as f32 * options.layer_height;
let mut base_slice = BaseSlice {
z: layer,
@@ -33,10 +41,10 @@ pub fn create_base_slices(options: &SlicerOptions, slicable: &Mesh<f32>) -> Vec<
child_r_aabb,
} => {
if layer >= child_l_aabb.min.z && layer <= child_l_aabb.max.z {
stack.push(child_r_index);
stack.push(child_l_index);
}
if layer >= child_r_aabb.min.z && layer <= child_r_aabb.max.z {
stack.push(child_l_index);
stack.push(child_r_index);
}
}
BvhNode::Leaf {
@@ -52,8 +60,8 @@ pub fn create_base_slices(options: &SlicerOptions, slicable: &Mesh<f32>) -> Vec<
}
}
base_slices.push(base_slice);
rings.append(&mut find_slice_rings(base_slice));
}
base_slices
rings
}

View File

@@ -3,6 +3,7 @@ use std::fmt::Debug;
pub mod base_slices;
pub mod line;
pub mod mesh;
pub mod slice_rings;
pub mod split_surface;
pub mod triangle;

View File

@@ -0,0 +1,54 @@
use approx::{relative_eq, relative_ne};
use nalgebra::Vector3;
use crate::console_log;
use super::base_slices::BaseSlice;
#[derive(Debug)]
pub struct SliceRing {
pub z: f32,
pub points: Vec<Vector3<f32>>,
}
pub fn find_slice_rings(mut slice: BaseSlice) -> Vec<SliceRing> {
let mut rings = vec![];
while let Some(line) = slice.lines.pop() {
let mut ring = SliceRing {
z: slice.z,
points: vec![line.start, line.end],
};
let mut right = ring.points[1];
let mut previous_len = usize::MAX;
while relative_ne!(ring.points[0], right) {
if previous_len == ring.points.len() {
console_log!(
"Error: Could not find a ring for slice at z = {}, {} items left.",
slice.z,
ring.points.len()
);
break;
}
previous_len = ring.points.len();
slice.lines.retain_mut(|line| {
if relative_eq!(line.start, right, epsilon = 0.001) {
ring.points.push(line.end);
right = line.end;
false
} else if relative_eq!(line.end, right, epsilon = 0.001) {
ring.points.push(line.start);
right = line.start;
false
} else {
true
}
})
}
rings.push(ring)
}
rings
}

View File

@@ -23,16 +23,24 @@ fn vec_inside_aabb<T: SimdPartialOrd + Scalar + Copy + Float>(
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.x <= aabb.max.x
&& vec.y <= aabb.max.y
&& vec.z <= aabb.max.z
}
impl<T> Triangle<T>
where
T: SimdPartialOrd + Scalar + Copy + ClosedMul + ClosedAdd + ClosedSub + Float + FromPrimitive,
T: SimdPartialOrd
+ RelativeEq
+ 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));
@@ -70,15 +78,11 @@ where
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 {
for point in [self.a, self.b, self.c].iter() {
if relative_eq!(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)));

View File

@@ -101,9 +101,9 @@
{@const visible = showSlices >= i / $layers.length}
{@const color = new Color(0, i / $layers.length, 0.2)}
{#if type === LayerType.Line}
<T.LineSegments {geometry} {visible}>
<T.Line {geometry} {visible}>
<T.LineBasicMaterial {color} />
</T.LineSegments>
</T.Line>
{:else if type === LayerType.Surface}
<T.Mesh {geometry} {visible}>
<T.MeshMatcapMaterial {color} side={DoubleSide} />

27
src/lib/slicer/newFile.ts Normal file
View File

@@ -0,0 +1,27 @@
import { BufferGeometry, BufferGeometryLoader, Float32BufferAttribute } from 'three';
import { type WorkerEvent } from './worker-data';
import init, { slice } from 'bampy';
addEventListener('message', async (event: MessageEvent<WorkerEvent>) => {
if (event.data.type === 'slice') {
const geometry = new BufferGeometryLoader().parse(event.data.data.stl);
if (geometry.index !== null) {
geometry.toNonIndexed();
}
await init();
const result = slice({
positions: geometry.attributes.position.array as Float32Array,
layerHeight: event.data.data.layerHeight,
maxAngle: event.data.data.maxNonPlanarAngle
});
for (const layer of result.rings) {
const geometry = new BufferGeometry();
geometry.setAttribute('position', new Float32BufferAttribute(layer, 3));
self.postMessage({
type: 'layer',
data: { type: LayerType.Line, geometry: layerGeometry.toJSON() }
} satisfies LayerMessage);
}
}
});

View File

@@ -1,5 +1,11 @@
import { BufferGeometryLoader } from 'three';
import { type SliceArguments, type ProgressMessage, type WorkerEvent } from './worker-data';
import { BufferGeometry, BufferGeometryLoader, Float32BufferAttribute } from 'three';
import {
type SliceArguments,
type ProgressMessage,
type WorkerEvent,
type LayerMessage,
LayerType
} from './worker-data';
import init, { slice } from 'bampy';
addEventListener('message', async (event: MessageEvent<WorkerEvent>) => {
@@ -9,11 +15,20 @@ addEventListener('message', async (event: MessageEvent<WorkerEvent>) => {
geometry.toNonIndexed();
}
await init();
slice(
geometry.attributes.position.array as Float32Array,
event.data.data.layerHeight,
event.data.data.maxNonPlanarAngle
);
const result = slice({
positions: geometry.attributes.position.array as Float32Array,
layerHeight: event.data.data.layerHeight,
maxAngle: event.data.data.maxNonPlanarAngle
});
for (const layer of result.rings) {
const geometry = new BufferGeometry();
geometry.setAttribute('position', new Float32BufferAttribute(layer, 3));
self.postMessage({
type: 'layer',
data: { type: LayerType.Line, geometry: geometry.toJSON() }
} satisfies LayerMessage);
}
}
});