feat: testing some stuff

This commit is contained in:
2024-07-17 01:08:23 +02:00
parent 44b792cc58
commit 0e8479af91
6 changed files with 158 additions and 105 deletions

View File

@@ -1,11 +1,14 @@
use std::iter::empty;
use approx::relative_eq; use approx::relative_eq;
use nalgebra::{vector, Vector3}; use nalgebra::{vector, SimdBool, Vector3};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use slicer::{line::Line3, slice_rings::slice_rings};
use tsify::Tsify; use tsify::Tsify;
use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::prelude::wasm_bindgen;
use crate::slicer::{ use crate::slicer::{
base_slices::create_slices, mesh::Mesh, split_surface::split_surface, base_slices::create_base_slices, mesh::Mesh, split_surface::split_surface,
trace_surface::trace_surface, triangle::Triangle, FloatValue, SlicerOptions, trace_surface::trace_surface, triangle::Triangle, FloatValue, SlicerOptions,
}; };
@@ -36,6 +39,10 @@ pub enum Slice {
#[tsify(type = "Float32Array")] #[tsify(type = "Float32Array")]
position: Vec<f32>, position: Vec<f32>,
}, },
Path {
#[tsify(type = "Float32Array")]
position: Vec<f32>,
},
} }
#[derive(Tsify, Serialize, Deserialize)] #[derive(Tsify, Serialize, Deserialize)]
@@ -95,12 +102,47 @@ pub fn slice(
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).into_iter().map(|mesh| {
slice_rings(1, &slicer_options, &mesh)
.flat_map(|mut rings| {
/*let mut rings = rings
.into_iter()
.map(|mut ring| {
ring.points.sort_unstable_by(|a, b| {
a[0].partial_cmp(&b[0]).unwrap_or(std::cmp::Ordering::Equal)
});
ring
})
.collect::<Vec<_>>();*/
rings.sort_unstable_by(|a, b| {
a.points
.first()
.unwrap()
.partial_cmp(b.points.first().unwrap())
.unwrap_or(std::cmp::Ordering::Equal)
});
rings
})
.fold(Vec::<Vector3<FloatValue>>::new(), |mut acc, mut curr| {
if acc
.last()
.zip(curr.points.first())
.zip(curr.points.last())
.map_or(false, |((last, start), end)| {
start.metric_distance(last) > end.metric_distance(last)
})
{
curr.points.reverse();
}
acc.extend(curr.points);
acc
})
});
console_log!("Computing BVH"); console_log!("Computing BVH");
let slicable = Mesh::from(slicable_triangles); let slicable = Mesh::from(slicable_triangles);
console_log!("Creating Slices"); console_log!("Creating Slices");
let mut slices = create_slices(&slicer_options, &slicable); let slices = slice_rings(2, &slicer_options, &slicable);
/*console_log!("Tracing Surfaces"); /*console_log!("Tracing Surfaces");
let a = max_angle.tan(); let a = max_angle.tan();
@@ -114,16 +156,23 @@ pub fn slice(
console_log!("Done"); console_log!("Done");
SliceResult { SliceResult {
slices: slices slices: surfaces
.into_iter()
.map(|slice| Slice::Ring { .map(|slice| Slice::Ring {
position: slice position: slice
.points
.into_iter() .into_iter()
.flat_map(|point| [point.x as f32, point.y as f32, point.z as f32]) .flat_map(|point| [point.x as f32, point.y as f32, point.z as f32])
.collect(), .collect(),
}) })
.chain(surfaces.into_iter().map(|surface| { /*.chain(slices.flatten().map(|slice| {
Slice::Ring {
position: slice
.points
.into_iter()
.flat_map(|point| [point.x as f32, point.y as f32, point.z as f32])
.collect(),
}
}))*/
/*.chain(surfaces.into_iter().map(|surface| {
Slice::Surface { Slice::Surface {
position: surface position: surface
.triangles .triangles
@@ -143,7 +192,7 @@ pub fn slice(
}) })
.collect(), .collect(),
} }
})) }))*/
.collect(), .collect(),
} }
} }

View File

@@ -1,28 +1,26 @@
use super::{ use super::{line::Line3, mesh::Mesh, FloatValue, SlicerOptions};
line::Line3, use crate::console_log;
mesh::Mesh,
slice_rings::{find_slice_rings, SliceRing},
FloatValue, SlicerOptions,
};
use bvh::bvh::BvhNode; use bvh::bvh::BvhNode;
#[derive(Debug)] #[derive(Debug)]
pub struct BaseSlice { pub struct BaseSlice {
pub z: FloatValue, pub d: FloatValue,
pub lines: Vec<Line3>, pub lines: Vec<Line3>,
} }
/// Creates base slices from the geometry, excluding surfaces. /// Creates base slices from the geometry
/// The slicse are not sorted or separated into rings. pub fn create_base_slices<'a>(
pub fn create_slices(options: &SlicerOptions, slicable: &Mesh) -> Vec<SliceRing> { axis: usize,
let layer_count = (slicable.aabb.max.z / options.layer_height).floor() as usize; options: &'a SlicerOptions,
let mut rings = vec![]; slicable: &'a Mesh,
let mut layer_index = 0; ) -> impl Iterator<Item = BaseSlice> + 'a {
let layer_count = ((slicable.aabb.max[axis] - slicable.aabb.min[axis]) / options.layer_height)
.floor() as usize;
for i in 0..layer_count { (0..layer_count).map(move |i| {
let layer = i as FloatValue * options.layer_height; let layer = i as FloatValue * options.layer_height + slicable.aabb.min[axis];
let mut base_slice = BaseSlice { let mut base_slice = BaseSlice {
z: layer, d: layer,
lines: vec![], lines: vec![],
}; };
@@ -37,12 +35,12 @@ pub fn create_slices(options: &SlicerOptions, slicable: &Mesh) -> Vec<SliceRing>
child_r_index, child_r_index,
child_r_aabb, child_r_aabb,
} => { } => {
assert!(child_l_aabb.min.z <= child_l_aabb.max.z); assert!(child_l_aabb.min[axis] <= child_l_aabb.max[axis]);
assert!(child_r_aabb.min.z <= child_r_aabb.max.z); assert!(child_r_aabb.min[axis] <= child_r_aabb.max[axis]);
if layer >= child_l_aabb.min.z && layer <= child_l_aabb.max.z { if layer >= child_l_aabb.min[axis] && layer <= child_l_aabb.max[axis] {
stack.push(child_l_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[axis] && layer <= child_r_aabb.max[axis] {
stack.push(child_r_index); stack.push(child_r_index);
} }
} }
@@ -51,7 +49,7 @@ pub fn create_slices(options: &SlicerOptions, slicable: &Mesh) -> Vec<SliceRing>
shape_index, shape_index,
} => { } => {
slicable.triangles[shape_index] slicable.triangles[shape_index]
.intersect_z(layer) .intersect(layer, axis)
.map(|line| { .map(|line| {
base_slice.lines.push(line); base_slice.lines.push(line);
}); });
@@ -59,8 +57,6 @@ pub fn create_slices(options: &SlicerOptions, slicable: &Mesh) -> Vec<SliceRing>
} }
} }
rings.append(&mut find_slice_rings(base_slice, &mut layer_index)); base_slice
} })
rings
} }

View File

@@ -3,68 +3,72 @@ use nalgebra::Vector3;
use crate::console_log; use crate::console_log;
use super::{base_slices::BaseSlice, FloatValue}; use super::{
base_slices::{create_base_slices, BaseSlice},
mesh::Mesh,
FloatValue, SlicerOptions,
};
#[derive(Debug)] #[derive(Debug)]
pub struct SliceRing { pub struct SliceRing {
pub z: FloatValue, pub d: FloatValue,
/// The points of the ring, in clockwise order. /// The points of the ring, in clockwise order.
pub points: Vec<Vector3<FloatValue>>, pub points: Vec<Vector3<FloatValue>>,
pub closed: bool,
} }
pub fn find_slice_rings(mut slice: BaseSlice, layer_index: &mut u32) -> Vec<SliceRing> { pub fn slice_rings<'a>(
axis: usize,
options: &'a SlicerOptions,
slicable: &'a Mesh,
) -> impl Iterator<Item = Vec<SliceRing>> + 'a {
let mut layer_index = 0;
create_base_slices(axis, options, slicable)
.map(move |slice| find_slice_rings(axis, slice, &mut layer_index))
}
pub fn find_slice_rings(
axis: usize,
mut slice: BaseSlice,
layer_index: &mut u32,
) -> Vec<SliceRing> {
let axis_a = (axis + 1) % 3;
let axis_b = (axis + 2) % 3;
let mut rings = vec![]; let mut rings = vec![];
while let Some(line) = slice.lines.pop() { while let Some(line) = slice.lines.pop() {
if relative_eq!(line.start, line.end) { if relative_eq!(line.start, line.end) {
continue; continue;
} }
let mut ring = SliceRing { let mut right = vec![line.end];
z: slice.z, let mut left = vec![line.start];
points: vec![line.start, line.end],
};
let mut right_start = ring.points[0];
let mut right = ring.points[1];
let mut sum_of_edges = (right.x - right_start.x) * (right.y + right_start.y);
let mut previous_len = usize::MAX; let mut previous_len = usize::MAX;
let mut done = false; let mut closed = false;
while !done { while !closed {
if previous_len == slice.lines.len() { if previous_len == slice.lines.len() {
console_log!(
"Error: Could not close ring {}, d = {}, {} items left.",
layer_index,
ring.points[0].metric_distance(&right),
slice.lines.len()
);
break; break;
} }
previous_len = slice.lines.len(); previous_len = slice.lines.len();
slice.lines.retain_mut(|line| { slice.lines.retain_mut(|line| {
if done { if closed {
return true; return true;
} }
macro_rules! add { let test = |side: &mut Vec<Vector3<FloatValue>>| {
( $point:expr ) => { let last = side.last().unwrap();
if !relative_eq!($point, right_start) { let s = relative_eq!(line.start, last);
right_start = right; let e = relative_eq!(line.end, last);
right = $point; if s && !e {
ring.points.push(right); side.push(line.end);
sum_of_edges = (right.x - right_start.x) * (right.y + right_start.y); } else if !s && e {
done = relative_eq!(ring.points[0], right); side.push(line.start);
} }
}; s || e
} };
let s = relative_eq!(line.start, right); if test(&mut left) || test(&mut right) {
let e = relative_eq!(line.end, right); closed = relative_eq!(left.last().unwrap(), right.last().unwrap());
if s && !e {
add!(line.end);
false
} else if e && !s {
add!(line.start);
false false
} else { } else {
true true
@@ -72,10 +76,21 @@ pub fn find_slice_rings(mut slice: BaseSlice, layer_index: &mut u32) -> Vec<Slic
}) })
} }
// The end point is duplicate, so not part of the winding order calculation. left.reverse();
if sum_of_edges < 0.0 { left.extend(right);
let mut ring = SliceRing {
d: slice.d,
closed,
points: left,
};
if ring.points.windows(2).fold(0.0, |acc, curr| {
acc + (curr[1][axis_a] - curr[0][axis_a]) * (curr[1][axis_b] + curr[0][axis_b])
}) < 0.0
{
ring.points.reverse(); ring.points.reverse();
} }
rings.push(ring); rings.push(ring);
*layer_index += 1; *layer_index += 1;
} }

View File

@@ -73,26 +73,24 @@ impl Triangle {
a && b || a && c || b && c a && b || a && c || b && c
} }
pub fn intersect_z(&self, z: FloatValue) -> Option<Line3> { pub fn intersect(&self, value: FloatValue, axis: usize) -> Option<Line3> {
let mut intersection = Vec::<Vector3<FloatValue>>::with_capacity(3); let mut intersection = Vec::<Vector3<FloatValue>>::with_capacity(3);
let mut last = &self.c; let mut last = &self.c;
for point in [self.a, self.b, self.c].iter() { for point in [self.a, self.b, self.c].iter() {
if relative_eq!(point.z, z) { if relative_eq!(point[axis], value) {
intersection.push(Vector3::new(point.x, point.y, z)); let mut new_point = *point;
} else if last.z < z && point.z > z { new_point[axis] = value;
let ratio = (z - last.z) / (point.z - last.z); intersection.push(new_point);
intersection.push(Vector3::new( } else if last[axis] < value && point[axis] > value {
last.x + (point.x - last.x) * ratio, let ratio = (value - last[axis]) / (point[axis] - last[axis]);
last.y + (point.y - last.y) * ratio, let mut new_point = last + (point - last) * ratio;
z, new_point[axis] = value;
)) intersection.push(new_point);
} else if last.z > z && point.z < z { } else if last[axis] > value && point[axis] < value {
let ratio = (z - point.z) / (last.z - point.z); let ratio = (value - point[axis]) / (last[axis] - point[axis]);
intersection.push(Vector3::new( let mut new_point = point + (last - point) * ratio;
point.x + (last.x - point.x) * ratio, new_point[axis] = value;
point.y + (last.y - point.y) * ratio, intersection.push(new_point);
z,
))
} }
last = point; last = point;
} }

View File

@@ -37,4 +37,3 @@
"vite-plugin-wasm": "^3.3.0" "vite-plugin-wasm": "^3.3.0"
} }
} }

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { T, type AsyncWritable } from '@threlte/core'; import { T, type AsyncWritable } from '@threlte/core';
import { Gizmo, Grid, MeshLineGeometry, MeshLineMaterial, OrbitControls } from '@threlte/extras'; import { Gizmo, Grid, OrbitControls, MeshLineGeometry, MeshLineMaterial } from '@threlte/extras';
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js'; import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js';
import { useLoader } from '@threlte/core'; import { useLoader } from '@threlte/core';
import { import {
@@ -9,9 +9,7 @@
Vector3, Vector3,
DoubleSide, DoubleSide,
Color, Color,
BufferGeometryLoader, BufferGeometryLoader
TubeGeometry,
CatmullRomCurve3
} from 'three'; } from 'three';
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
@@ -38,15 +36,12 @@
case 'layer': { case 'layer': {
layers.update((layers) => { layers.update((layers) => {
const layer = event.data.data; const layer = event.data.data;
if (layer.type === 'ring') { if (layer.type === 'ring' || layer.type === 'path') {
const curve = new CatmullRomCurve3( layers.push(
Array.from({ length: layer.position.length / 3 }, (_, i) => Array.from({ length: layer.position.length / 3 }, (_, i) =>
new Vector3().fromArray(layer.position, i * 3) new Vector3().fromArray(layer.position, i * 3)
) )
); );
const geometry = new TubeGeometry(curve, undefined, 0.1);
layers.push(geometry);
} else if (layer.type === 'surface') { } else if (layer.type === 'surface') {
} }
return layers; return layers;
@@ -72,7 +67,7 @@
export let maxNonPlanarAngle = MathUtils.degToRad(20); export let maxNonPlanarAngle = MathUtils.degToRad(20);
export let bedNormal = new Vector3(0, 0, 1); export let bedNormal = new Vector3(0, 0, 1);
let layers = writable<Layer[]>([]); let layers = writable<Vector3[][]>([]);
const stl: AsyncWritable<BufferGeometry> = useLoader(STLLoader).load('/benchy.stl'); const stl: AsyncWritable<BufferGeometry> = useLoader(STLLoader).load('/benchy.stl');
@@ -107,12 +102,13 @@
gridSize={[buildSurface[0], buildSurface[1]]} gridSize={[buildSurface[0], buildSurface[1]]}
/> />
{#each $layers as geometry, i} {#each $layers as points, i}
{@const visible = maxZ !== 0 ? i === maxZ : showSlices >= i / $layers.length} {@const visible = maxZ !== 0 ? i === maxZ : showSlices >= i / $layers.length}
{@const color = new Color(Math.random() * 0xffffff)} {@const color = new Color(Math.random() * 0xffffff)}
<!---{@const color = new Color(0, i / $layers.length, 0.2)}--> <!---{@const color = new Color(0, i / $layers.length, 0.2)}-->
<T.Mesh {geometry} {visible}> <T.Mesh {visible}>
<T.MeshMatcapMaterial {color} side={DoubleSide} /> <MeshLineGeometry {points} />
<MeshLineMaterial width={layerHeight / 6} {color} />
</T.Mesh> </T.Mesh>
{/each} {/each}