mirror of
https://github.com/Theaninova/Bampy.git
synced 2026-01-03 15:22:50 +00:00
stuff
This commit is contained in:
@@ -23,6 +23,8 @@ bvh = "0.8.0"
|
|||||||
nalgebra = "0.32.4"
|
nalgebra = "0.32.4"
|
||||||
num = "0.4.1"
|
num = "0.4.1"
|
||||||
approx = "0.5.1"
|
approx = "0.5.1"
|
||||||
|
serde = "1.0.197"
|
||||||
|
tsify = { version = "0.4.5", features = ["js"] }
|
||||||
|
|
||||||
[dependencies.getrandom]
|
[dependencies.getrandom]
|
||||||
features = ["js"]
|
features = ["js"]
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
use nalgebra::{vector, Vector3};
|
use nalgebra::{vector, Vector3};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tsify::Tsify;
|
||||||
use wasm_bindgen::prelude::wasm_bindgen;
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
|
|
||||||
use crate::slicer::{
|
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,
|
SlicerOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -11,8 +13,32 @@ mod util;
|
|||||||
|
|
||||||
const BED_NORMAL: Vector3<f32> = vector![0f32, 0f32, 1f32];
|
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]
|
#[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));
|
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||||
|
|
||||||
assert_eq!(positions.len() % 9, 0);
|
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 {
|
if triangle.normal.angle(&BED_NORMAL) > max_angle {
|
||||||
slicable_triangles.push(triangle);
|
slicable_triangles.push(triangle);
|
||||||
} else {
|
} else {
|
||||||
|
slicable_triangles.push(triangle);
|
||||||
surface_triangles.push(triangle);
|
surface_triangles.push(triangle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slicable_triangles.shrink_to_fit();
|
slicable_triangles.shrink_to_fit();
|
||||||
surface_triangles.shrink_to_fit();
|
surface_triangles.shrink_to_fit();
|
||||||
|
|
||||||
console_log!("Computing BVH");
|
|
||||||
|
|
||||||
let slicer_options = SlicerOptions { layer_height };
|
let slicer_options = SlicerOptions { layer_height };
|
||||||
|
|
||||||
console_log!("Creating Surfaces");
|
console_log!("Creating Surfaces");
|
||||||
let surfaces = split_surface(surface_triangles);
|
let surfaces = split_surface(surface_triangles);
|
||||||
|
|
||||||
console_log!("Creating Slices");
|
console_log!("Computing BVH");
|
||||||
let slicable = Mesh::from(slicable_triangles);
|
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");
|
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(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
use bvh::bvh::BvhNode;
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct BaseSlice {
|
pub struct BaseSlice {
|
||||||
@@ -10,11 +17,12 @@ pub struct BaseSlice {
|
|||||||
|
|
||||||
/// 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.
|
/// 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 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 {
|
for i in 0..layer_count {
|
||||||
|
console_log!("Layer {}", i);
|
||||||
let layer = i as f32 * options.layer_height;
|
let layer = i as f32 * options.layer_height;
|
||||||
let mut base_slice = BaseSlice {
|
let mut base_slice = BaseSlice {
|
||||||
z: layer,
|
z: layer,
|
||||||
@@ -33,10 +41,10 @@ pub fn create_base_slices(options: &SlicerOptions, slicable: &Mesh<f32>) -> Vec<
|
|||||||
child_r_aabb,
|
child_r_aabb,
|
||||||
} => {
|
} => {
|
||||||
if layer >= child_l_aabb.min.z && layer <= child_l_aabb.max.z {
|
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 {
|
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 {
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use std::fmt::Debug;
|
|||||||
pub mod base_slices;
|
pub mod base_slices;
|
||||||
pub mod line;
|
pub mod line;
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
|
pub mod slice_rings;
|
||||||
pub mod split_surface;
|
pub mod split_surface;
|
||||||
pub mod triangle;
|
pub mod triangle;
|
||||||
|
|
||||||
|
|||||||
54
bampy/src/slicer/slice_rings.rs
Normal file
54
bampy/src/slicer/slice_rings.rs
Normal 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
|
||||||
|
}
|
||||||
@@ -23,16 +23,24 @@ fn vec_inside_aabb<T: SimdPartialOrd + Scalar + Copy + Float>(
|
|||||||
aabb: &Aabb<T, 3>,
|
aabb: &Aabb<T, 3>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
vec.x >= aabb.min.x
|
vec.x >= aabb.min.x
|
||||||
&& vec.x <= aabb.max.x
|
|
||||||
&& vec.y >= aabb.min.y
|
&& vec.y >= aabb.min.y
|
||||||
&& vec.y <= aabb.max.y
|
|
||||||
&& vec.z >= aabb.min.z
|
&& vec.z >= aabb.min.z
|
||||||
|
&& vec.x <= aabb.max.x
|
||||||
|
&& vec.y <= aabb.max.y
|
||||||
&& vec.z <= aabb.max.z
|
&& vec.z <= aabb.max.z
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Triangle<T>
|
impl<T> Triangle<T>
|
||||||
where
|
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 {
|
pub fn new(a: Vector3<T>, b: Vector3<T>, c: Vector3<T>) -> Self {
|
||||||
let normal = (b - a).cross(&(c - a));
|
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)
|
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>> {
|
pub fn intersect_z(&self, z: T) -> Option<Line3<T>> {
|
||||||
let mut intersection = Vec::with_capacity(3);
|
let mut intersection = Vec::with_capacity(3);
|
||||||
let mut last = self.c;
|
let mut last = self.c;
|
||||||
for point in self.iter() {
|
for point in [self.a, self.b, self.c].iter() {
|
||||||
if point.z == z {
|
if relative_eq!(point.z, z) {
|
||||||
intersection.push(*point);
|
intersection.push(*point);
|
||||||
} else if last.z < z && point.z > z || last.z > z && point.z < z {
|
} 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)));
|
intersection.push(last.lerp(&point, (z - last.z) / (point.z - last.z)));
|
||||||
|
|||||||
@@ -101,9 +101,9 @@
|
|||||||
{@const visible = showSlices >= i / $layers.length}
|
{@const visible = showSlices >= i / $layers.length}
|
||||||
{@const color = new Color(0, i / $layers.length, 0.2)}
|
{@const color = new Color(0, i / $layers.length, 0.2)}
|
||||||
{#if type === LayerType.Line}
|
{#if type === LayerType.Line}
|
||||||
<T.LineSegments {geometry} {visible}>
|
<T.Line {geometry} {visible}>
|
||||||
<T.LineBasicMaterial {color} />
|
<T.LineBasicMaterial {color} />
|
||||||
</T.LineSegments>
|
</T.Line>
|
||||||
{:else if type === LayerType.Surface}
|
{:else if type === LayerType.Surface}
|
||||||
<T.Mesh {geometry} {visible}>
|
<T.Mesh {geometry} {visible}>
|
||||||
<T.MeshMatcapMaterial {color} side={DoubleSide} />
|
<T.MeshMatcapMaterial {color} side={DoubleSide} />
|
||||||
|
|||||||
27
src/lib/slicer/newFile.ts
Normal file
27
src/lib/slicer/newFile.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
import { BufferGeometryLoader } from 'three';
|
import { BufferGeometry, BufferGeometryLoader, Float32BufferAttribute } from 'three';
|
||||||
import { type SliceArguments, type ProgressMessage, type WorkerEvent } from './worker-data';
|
import {
|
||||||
|
type SliceArguments,
|
||||||
|
type ProgressMessage,
|
||||||
|
type WorkerEvent,
|
||||||
|
type LayerMessage,
|
||||||
|
LayerType
|
||||||
|
} from './worker-data';
|
||||||
import init, { slice } from 'bampy';
|
import init, { slice } from 'bampy';
|
||||||
|
|
||||||
addEventListener('message', async (event: MessageEvent<WorkerEvent>) => {
|
addEventListener('message', async (event: MessageEvent<WorkerEvent>) => {
|
||||||
@@ -9,11 +15,20 @@ addEventListener('message', async (event: MessageEvent<WorkerEvent>) => {
|
|||||||
geometry.toNonIndexed();
|
geometry.toNonIndexed();
|
||||||
}
|
}
|
||||||
await init();
|
await init();
|
||||||
slice(
|
const result = slice({
|
||||||
geometry.attributes.position.array as Float32Array,
|
positions: geometry.attributes.position.array as Float32Array,
|
||||||
event.data.data.layerHeight,
|
layerHeight: event.data.data.layerHeight,
|
||||||
event.data.data.maxNonPlanarAngle
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user