mirror of
https://github.com/Theaninova/Bampy.git
synced 2025-12-10 11:36:18 +00:00
feat: testing
This commit is contained in:
177
;
Normal file
177
;
Normal file
@@ -0,0 +1,177 @@
|
||||
use super::{
|
||||
axis::Axis,
|
||||
base_slices::BaseSlice,
|
||||
line::Line3,
|
||||
slice_path::{SlicePath, SurfacePathIterator},
|
||||
triangle::Triangle,
|
||||
FloatValue,
|
||||
};
|
||||
use bvh::{
|
||||
aabb::Aabb,
|
||||
bvh::{Bvh, BvhNode},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Mesh {
|
||||
pub aabb: Aabb<FloatValue, 3>,
|
||||
pub bvh: Bvh<FloatValue, 3>,
|
||||
pub triangles: Vec<Triangle>,
|
||||
}
|
||||
|
||||
impl From<Vec<Triangle>> for Mesh {
|
||||
fn from(mut triangles: Vec<Triangle>) -> 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mesh {
|
||||
pub fn slice_paths<'a>(
|
||||
self: &'a Mesh,
|
||||
axis: Axis,
|
||||
slice_height: FloatValue,
|
||||
) -> impl Iterator<Item = Vec<SlicePath>> + 'a {
|
||||
self.slice_base_slices(axis, slice_height)
|
||||
.map(|slice| slice.find_paths())
|
||||
.filter(|paths| !paths.is_empty())
|
||||
}
|
||||
|
||||
pub fn slice_surface(&self, axis: Axis, nozzle_width: FloatValue) -> SurfacePathIterator {
|
||||
SurfacePathIterator::new(self, axis, nozzle_width)
|
||||
}
|
||||
|
||||
pub fn slice_base_slices<'a>(
|
||||
self: &'a Mesh,
|
||||
axis: Axis,
|
||||
slice_height: FloatValue,
|
||||
) -> impl Iterator<Item = BaseSlice> + 'a {
|
||||
let layer_count = ((self.aabb.max[axis as usize] - self.aabb.min[axis as usize])
|
||||
/ slice_height)
|
||||
.floor() as usize;
|
||||
|
||||
(0..layer_count).map(move |i| {
|
||||
let layer = i as FloatValue * slice_height + self.aabb.min[axis as usize];
|
||||
let mut base_slice = BaseSlice {
|
||||
i,
|
||||
d: layer,
|
||||
axis,
|
||||
lines: vec![],
|
||||
};
|
||||
|
||||
let mut stack = Vec::<usize>::with_capacity(self.bvh.nodes.len());
|
||||
stack.push(0);
|
||||
while let Some(i) = stack.pop() {
|
||||
match self.bvh.nodes[i] {
|
||||
BvhNode::Node {
|
||||
parent_index: _,
|
||||
child_l_index,
|
||||
child_l_aabb,
|
||||
child_r_index,
|
||||
child_r_aabb,
|
||||
} => {
|
||||
assert!(child_l_aabb.min[axis as usize] <= child_l_aabb.max[axis as usize]);
|
||||
assert!(child_r_aabb.min[axis as usize] <= child_r_aabb.max[axis as usize]);
|
||||
if layer >= child_l_aabb.min[axis as usize]
|
||||
&& layer <= child_l_aabb.max[axis as usize]
|
||||
{
|
||||
stack.push(child_l_index);
|
||||
}
|
||||
if layer >= child_r_aabb.min[axis as usize]
|
||||
&& layer <= child_r_aabb.max[axis as usize]
|
||||
{
|
||||
stack.push(child_r_index);
|
||||
}
|
||||
}
|
||||
BvhNode::Leaf {
|
||||
parent_index: _,
|
||||
shape_index,
|
||||
} => {
|
||||
for line in self.triangles[shape_index].intersect(layer, axis as usize) {
|
||||
base_slice.lines.push(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base_slice
|
||||
})
|
||||
}
|
||||
|
||||
pub fn outline_base_slice(&self, axis: Axis) -> BaseSlice {
|
||||
let mut base_slice = BaseSlice {
|
||||
i: 0,
|
||||
d: 0.0,
|
||||
axis,
|
||||
lines: vec![],
|
||||
};
|
||||
'triangle: for (i, triangle) in self.triangles.iter().enumerate() {
|
||||
let mut stack = Vec::<usize>::with_capacity(self.bvh.nodes.len());
|
||||
stack.push(0);
|
||||
let mut ab = false;
|
||||
let mut ac = false;
|
||||
let mut bc = false;
|
||||
while let Some(i) = stack.pop() {
|
||||
match self.bvh.nodes[i] {
|
||||
BvhNode::Node {
|
||||
parent_index: _,
|
||||
child_l_index,
|
||||
child_l_aabb,
|
||||
child_r_index,
|
||||
child_r_aabb,
|
||||
} => {
|
||||
let coords = [triangle.a, triangle.b, triangle.c];
|
||||
macro_rules! match_aabb {
|
||||
($side:expr) => {
|
||||
coords
|
||||
.iter()
|
||||
.map(|point| {
|
||||
$side.approx_contains_eps(point, FloatValue::EPSILON) as i32
|
||||
})
|
||||
.sum::<i32>()
|
||||
>= 2
|
||||
};
|
||||
}
|
||||
if match_aabb!(child_l_aabb) {
|
||||
stack.push(child_l_index);
|
||||
}
|
||||
if match_aabb!(child_r_aabb) {
|
||||
stack.push(child_r_index);
|
||||
}
|
||||
}
|
||||
BvhNode::Leaf {
|
||||
parent_index: _,
|
||||
shape_index,
|
||||
} => {
|
||||
if i == shape_index {
|
||||
continue;
|
||||
}
|
||||
let other = &self.triangles[shape_index];
|
||||
let a = triangle.has_point(other.a);
|
||||
let b = triangle.has_point(other.b);
|
||||
let c = triangle.has_point(other.c);
|
||||
ab = ab || (a && b);
|
||||
ac = ac || (a && c);
|
||||
bc = bc || (b && c);
|
||||
|
||||
if ab && ac && bc {
|
||||
continue 'triangle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
base_slice
|
||||
}
|
||||
}
|
||||
165
bampy/src/lib.rs
165
bampy/src/lib.rs
@@ -1,7 +1,11 @@
|
||||
#![feature(extract_if)]
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use approx::relative_eq;
|
||||
use nalgebra::{point, vector, Vector3};
|
||||
use num::Float;
|
||||
use result::{Slice, SliceOptions, SliceResult};
|
||||
use slicer::axis::Axis;
|
||||
use slicer::{axis::Axis, slice_path::SlicePath, trace_surface::trace_surface};
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
use crate::slicer::{mesh::Mesh, split_surface::split_surface, triangle::Triangle, FloatValue};
|
||||
@@ -48,13 +52,10 @@ pub fn slice(
|
||||
);
|
||||
|
||||
slicable_triangles.push(triangle);
|
||||
let angle = triangle.normal.angle(&BED_NORMAL);
|
||||
let opposite_angle = std::f64::consts::PI - angle;
|
||||
if angle <= max_angle
|
||||
|| relative_eq!(angle, max_angle)
|
||||
|| opposite_angle <= max_angle
|
||||
|| relative_eq!(opposite_angle, max_angle)
|
||||
{
|
||||
let mut normal = triangle.normal.clone();
|
||||
normal.z = normal.z.abs();
|
||||
let angle = normal.angle(&BED_NORMAL);
|
||||
if angle <= max_angle || relative_eq!(angle, max_angle) {
|
||||
surface_triangles.push(triangle);
|
||||
}
|
||||
}
|
||||
@@ -62,45 +63,130 @@ pub fn slice(
|
||||
surface_triangles.shrink_to_fit();
|
||||
|
||||
console_log!("Creating Surfaces");
|
||||
let surfaces = split_surface(surface_triangles).into_iter().map(|mesh| {
|
||||
mesh.slice_surface(Axis::X, nozzle_diameter).filter(|path| {
|
||||
let mut length = 0.0;
|
||||
for pair in path.path.windows(2) {
|
||||
length += (pair[0].coords - pair[1].coords).norm();
|
||||
if length >= min_surface_path_length {
|
||||
let min_surface_area = std::f64::consts::PI * (nozzle_diameter / 2.0).powi(2);
|
||||
let mut surfaces = split_surface(surface_triangles)
|
||||
.into_iter()
|
||||
.filter(|mesh| {
|
||||
let mut surface_area = 0.0;
|
||||
for triangle in &mesh.triangles {
|
||||
surface_area += triangle.area();
|
||||
if surface_area >= min_surface_area {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})
|
||||
});
|
||||
.map(|mesh| {
|
||||
let outline = mesh
|
||||
.outline_base_slice(Axis::Z)
|
||||
.find_paths()
|
||||
.into_iter()
|
||||
.filter(|path| path.closed)
|
||||
.collect::<Vec<_>>();
|
||||
let surface = mesh
|
||||
.slice_surface(Axis::X, nozzle_diameter)
|
||||
.filter(|path| {
|
||||
let mut length = 0.0;
|
||||
for pair in path.path.windows(2) {
|
||||
length += (pair[0].coords - pair[1].coords).norm();
|
||||
if length >= min_surface_path_length {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
(mesh, outline, surface)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
surfaces
|
||||
.sort_unstable_by(|(a, _, _), (b, _, _)| a.aabb.min.z.partial_cmp(&b.aabb.min.z).unwrap());
|
||||
|
||||
console_log!("Creating Walls");
|
||||
let wallMesh = Mesh::from(slicable_triangles);
|
||||
let walls = wallMesh.slice_paths(Axis::Z, layer_height);
|
||||
let mut walls = wallMesh
|
||||
.slice_paths(Axis::Z, layer_height)
|
||||
.flat_map(|paths| paths.into_iter().filter(|path| path.closed))
|
||||
.collect::<VecDeque<_>>();
|
||||
let mut active_surfaces = Vec::new();
|
||||
let mut out = Vec::new();
|
||||
|
||||
/*console_log!("Tracing Surfaces");
|
||||
let a = max_angle.tan();
|
||||
for slice in &mut slices {
|
||||
for surface in &surfaces {
|
||||
if surface.aabb.min.z <= slice.z && surface.aabb.max.z > slice.z {
|
||||
trace_surface(slice, surface, a);
|
||||
console_log!("Resolving dependencies");
|
||||
while let Some(mut wall) = walls.pop_front() {
|
||||
active_surfaces.extend(
|
||||
surfaces
|
||||
.extract_if(|surface| surface.0.aabb.min.z <= wall.aabb.max.z)
|
||||
.map(|surface| (surface, Vec::new())),
|
||||
);
|
||||
|
||||
let deactivate =
|
||||
active_surfaces.extract_if(|element| element.0 .0.aabb.max.z < wall.aabb.min.z);
|
||||
for (surface, surface_walls) in deactivate {
|
||||
for ring in surface.1 {
|
||||
out.push(ring.points);
|
||||
}
|
||||
for path in surface.2 {
|
||||
out.push(path.path);
|
||||
}
|
||||
for wall in surface_walls {
|
||||
walls.push_front(wall);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
for surface in active_surfaces.iter_mut() {
|
||||
let held = wall
|
||||
.points
|
||||
.extract_if(|point| !trace_surface(&point, &surface.0 .0, max_angle))
|
||||
.collect::<Vec<_>>();
|
||||
if !held.is_empty() {
|
||||
surface.1.push(SlicePath {
|
||||
points: held,
|
||||
..wall
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if !wall.points.is_empty() {
|
||||
out.push(wall.points);
|
||||
}
|
||||
}
|
||||
|
||||
console_log!("Done");
|
||||
SliceResult {
|
||||
slices: surfaces
|
||||
.flatten()
|
||||
slices: out
|
||||
.into_iter()
|
||||
.map(|slice| Slice::Ring {
|
||||
position: slice
|
||||
.path
|
||||
.into_iter()
|
||||
.flat_map(|point| [point.x as f32, point.y as f32, point.z as f32])
|
||||
.collect(),
|
||||
})
|
||||
/*.chain(walls.flatten().map(|slice| {
|
||||
.collect(),
|
||||
}
|
||||
/*SliceResult {
|
||||
slices: surfaces
|
||||
.into_iter()
|
||||
.flat_map(|(_, outlines, slices)| {
|
||||
outlines
|
||||
.into_iter()
|
||||
.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(slices.into_iter().map(|slice| {
|
||||
Slice::Ring {
|
||||
position: slice
|
||||
.path
|
||||
.into_iter()
|
||||
.flat_map(|point| [point.x as f32, point.y as f32, point.z as f32])
|
||||
.collect(),
|
||||
}
|
||||
}))
|
||||
})
|
||||
.chain(walls.flatten().map(|slice| {
|
||||
Slice::Ring {
|
||||
position: slice
|
||||
.points
|
||||
@@ -108,28 +194,7 @@ pub fn slice(
|
||||
.flat_map(|point| [point.x as f32, point.y as f32, point.z as f32])
|
||||
.collect(),
|
||||
}
|
||||
}))*/
|
||||
/*.chain(surfaces.into_iter().map(|surface| {
|
||||
Slice::Surface {
|
||||
position: surface
|
||||
.triangles
|
||||
.into_iter()
|
||||
.flat_map(|triangle| {
|
||||
[
|
||||
triangle.a.x as f32,
|
||||
triangle.a.y as f32,
|
||||
triangle.a.z as f32,
|
||||
triangle.b.x as f32,
|
||||
triangle.b.y as f32,
|
||||
triangle.b.z as f32,
|
||||
triangle.c.x as f32,
|
||||
triangle.c.y as f32,
|
||||
triangle.c.z as f32,
|
||||
]
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}))*/
|
||||
}))
|
||||
.collect(),
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ use nalgebra::Vector3;
|
||||
|
||||
use super::FloatValue;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(usize)]
|
||||
pub enum Axis {
|
||||
#[default]
|
||||
X = 0,
|
||||
Y = 1,
|
||||
Z = 2,
|
||||
|
||||
@@ -13,6 +13,10 @@ pub struct Line3 {
|
||||
}
|
||||
|
||||
impl Line3 {
|
||||
pub fn new(start: Point3<FloatValue>, end: Point3<FloatValue>) -> Self {
|
||||
Self { start, end }
|
||||
}
|
||||
|
||||
pub fn norm(&self) -> FloatValue {
|
||||
(self.end - self.start).norm()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::{
|
||||
axis::Axis,
|
||||
base_slices::BaseSlice,
|
||||
line::Line3,
|
||||
slice_path::{SlicePath, SurfacePathIterator},
|
||||
triangle::Triangle,
|
||||
FloatValue,
|
||||
@@ -60,7 +61,7 @@ impl Mesh {
|
||||
/ slice_height)
|
||||
.floor() as usize;
|
||||
|
||||
(0..layer_count).map(move |i| {
|
||||
(0..=layer_count).map(move |i| {
|
||||
let layer = i as FloatValue * slice_height + self.aabb.min[axis as usize];
|
||||
let mut base_slice = BaseSlice {
|
||||
i,
|
||||
@@ -107,4 +108,79 @@ impl Mesh {
|
||||
base_slice
|
||||
})
|
||||
}
|
||||
|
||||
pub fn outline_base_slice(&self, axis: Axis) -> BaseSlice {
|
||||
let mut base_slice = BaseSlice {
|
||||
i: 0,
|
||||
d: 0.0,
|
||||
axis,
|
||||
lines: vec![],
|
||||
};
|
||||
'triangle: for (triangle_index, triangle) in self.triangles.iter().enumerate() {
|
||||
let mut stack = Vec::<usize>::with_capacity(self.bvh.nodes.len());
|
||||
stack.push(0);
|
||||
let mut ab = false;
|
||||
let mut ac = false;
|
||||
let mut bc = false;
|
||||
while let Some(i) = stack.pop() {
|
||||
match self.bvh.nodes[i] {
|
||||
BvhNode::Node {
|
||||
parent_index: _,
|
||||
child_l_index,
|
||||
child_l_aabb,
|
||||
child_r_index,
|
||||
child_r_aabb,
|
||||
} => {
|
||||
let coords = [triangle.a, triangle.b, triangle.c];
|
||||
macro_rules! match_aabb {
|
||||
($side:expr) => {
|
||||
coords
|
||||
.iter()
|
||||
.map(|point| {
|
||||
$side.approx_contains_eps(point, FloatValue::EPSILON) as i32
|
||||
})
|
||||
.sum::<i32>()
|
||||
>= 2
|
||||
};
|
||||
}
|
||||
if match_aabb!(child_l_aabb) {
|
||||
stack.push(child_l_index);
|
||||
}
|
||||
if match_aabb!(child_r_aabb) {
|
||||
stack.push(child_r_index);
|
||||
}
|
||||
}
|
||||
BvhNode::Leaf {
|
||||
parent_index: _,
|
||||
shape_index,
|
||||
} => {
|
||||
if triangle_index == shape_index {
|
||||
continue;
|
||||
}
|
||||
let other = &self.triangles[shape_index];
|
||||
let a = other.has_point(triangle.a);
|
||||
let b = other.has_point(triangle.b);
|
||||
let c = other.has_point(triangle.c);
|
||||
ab = ab || (a && b);
|
||||
ac = ac || (a && c);
|
||||
bc = bc || (b && c);
|
||||
|
||||
if ab && ac && bc {
|
||||
continue 'triangle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ab {
|
||||
base_slice.lines.push(Line3::new(triangle.a, triangle.b));
|
||||
}
|
||||
if !ac {
|
||||
base_slice.lines.push(Line3::new(triangle.a, triangle.c));
|
||||
}
|
||||
if !bc {
|
||||
base_slice.lines.push(Line3::new(triangle.b, triangle.c));
|
||||
}
|
||||
}
|
||||
base_slice
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use nalgebra::{vector, Point, TAffine, Transform, Vector2, Vector3};
|
||||
use super::FloatValue;
|
||||
|
||||
pub trait Sdf<const D: usize> {
|
||||
fn sdf(&self, p: Point<FloatValue, D>) -> FloatValue;
|
||||
fn sdf(&self, p: &Point<FloatValue, D>) -> FloatValue;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@@ -19,7 +19,7 @@ impl SdfSphere {
|
||||
}
|
||||
|
||||
impl Sdf<3> for SdfSphere {
|
||||
fn sdf(&self, p: Point<FloatValue, 3>) -> FloatValue {
|
||||
fn sdf(&self, p: &Point<FloatValue, 3>) -> FloatValue {
|
||||
p.coords.norm() - self.radius
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ impl SdfBox {
|
||||
}
|
||||
|
||||
impl Sdf<3> for SdfBox {
|
||||
fn sdf(&self, p: Point<FloatValue, 3>) -> FloatValue {
|
||||
fn sdf(&self, p: &Point<FloatValue, 3>) -> FloatValue {
|
||||
let q = p.coords.abs() - self.size.coords;
|
||||
q.sup(&Vector3::zeros()).add_scalar(q.max().min(0.0)).norm()
|
||||
}
|
||||
@@ -56,7 +56,7 @@ impl SdfInfiniteCone {
|
||||
}
|
||||
|
||||
impl Sdf<3> for SdfInfiniteCone {
|
||||
fn sdf(&self, p: Point<FloatValue, 3>) -> FloatValue {
|
||||
fn sdf(&self, p: &Point<FloatValue, 3>) -> FloatValue {
|
||||
let q = vector![p.coords.xy().norm(), p.z];
|
||||
let d = (q - self.angle.scale(q.dot(&self.angle).max(0.0))).norm();
|
||||
if q.x * self.angle.y - q.y * self.angle.x > 0.0 {
|
||||
@@ -80,8 +80,8 @@ impl<T: Sdf<3>> SdfTransform<T> {
|
||||
}
|
||||
|
||||
impl<T: Sdf<3>> Sdf<3> for SdfTransform<T> {
|
||||
fn sdf(&self, p: Point<FloatValue, 3>) -> FloatValue {
|
||||
self.sdf.sdf(self.transform.inverse_transform_point(&p))
|
||||
fn sdf(&self, p: &Point<FloatValue, 3>) -> FloatValue {
|
||||
self.sdf.sdf(&self.transform.inverse_transform_point(p))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,8 +98,8 @@ impl<const D: usize, T: Sdf<D>> SdfScale<D, T> {
|
||||
}
|
||||
|
||||
impl<const D: usize, T: Sdf<D>> Sdf<D> for SdfScale<D, T> {
|
||||
fn sdf(&self, p: Point<FloatValue, D>) -> FloatValue {
|
||||
self.sdf.sdf(p / self.scale) * self.scale
|
||||
fn sdf(&self, p: &Point<FloatValue, D>) -> FloatValue {
|
||||
self.sdf.sdf(&(p / self.scale)) * self.scale
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ impl<T: Sdf<3>> SdfOnion<T> {
|
||||
}
|
||||
|
||||
impl<T: Sdf<3>> Sdf<3> for SdfOnion<T> {
|
||||
fn sdf(&self, p: Point<FloatValue, 3>) -> FloatValue {
|
||||
fn sdf(&self, p: &Point<FloatValue, 3>) -> FloatValue {
|
||||
self.sdf.sdf(p).abs() - self.thickness
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,7 @@ impl<const D: usize, T: Sdf<D>, U: Sdf<D>> SdfUnion<D, T, U> {
|
||||
}
|
||||
|
||||
impl<const D: usize, T: Sdf<D>, U: Sdf<D>> Sdf<D> for SdfUnion<D, T, U> {
|
||||
fn sdf(&self, p: Point<FloatValue, D>) -> FloatValue {
|
||||
fn sdf(&self, p: &Point<FloatValue, D>) -> FloatValue {
|
||||
self.sdf_a.sdf(p).min(self.sdf_b.sdf(p))
|
||||
}
|
||||
}
|
||||
@@ -152,7 +152,7 @@ impl<const D: usize, T: Sdf<D>, U: Sdf<D>> SdfIntersection<D, T, U> {
|
||||
}
|
||||
|
||||
impl<const D: usize, T: Sdf<D>, U: Sdf<D>> Sdf<D> for SdfIntersection<D, T, U> {
|
||||
fn sdf(&self, p: Point<FloatValue, D>) -> FloatValue {
|
||||
fn sdf(&self, p: &Point<FloatValue, D>) -> FloatValue {
|
||||
self.sdf_a.sdf(p).max(self.sdf_b.sdf(p))
|
||||
}
|
||||
}
|
||||
@@ -170,7 +170,7 @@ impl<const D: usize, T: Sdf<D>, U: Sdf<D>> SdfDifference<D, T, U> {
|
||||
}
|
||||
|
||||
impl<const D: usize, T: Sdf<D>, U: Sdf<D>> Sdf<D> for SdfDifference<D, T, U> {
|
||||
fn sdf(&self, p: Point<FloatValue, D>) -> FloatValue {
|
||||
fn sdf(&self, p: &Point<FloatValue, D>) -> FloatValue {
|
||||
self.sdf_a.sdf(p).max(-self.sdf_b.sdf(p))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::ops::RangeInclusive;
|
||||
use std::{collections::VecDeque, ops::RangeInclusive};
|
||||
|
||||
use approx::relative_eq;
|
||||
use bvh::aabb::Aabb;
|
||||
@@ -6,7 +6,7 @@ use nalgebra::Point3;
|
||||
|
||||
use super::{axis::Axis, mesh::Mesh, FloatValue};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SlicePath {
|
||||
pub i: usize,
|
||||
pub d: FloatValue,
|
||||
@@ -75,6 +75,34 @@ impl SurfacePathIterator {
|
||||
}
|
||||
}
|
||||
|
||||
fn squish(points: &mut Vec<Point3<FloatValue>>, axis: Axis, d: FloatValue) {
|
||||
macro_rules! ax {
|
||||
($p: expr) => {
|
||||
$p.coords[axis as usize]
|
||||
};
|
||||
}
|
||||
let first = ax!(points.first().unwrap());
|
||||
let left = points.iter().position(|p| first - ax!(p) > d);
|
||||
let last = ax!(points.last().unwrap());
|
||||
let right = points.iter().rposition(|p| ax!(p) - last > d);
|
||||
|
||||
if let (Some(left), Some(right)) = (left, right) {
|
||||
if left > right {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
let total = ax!(points[left + 1]) - first;
|
||||
let delta = ax!(points[left]) - ax!(points[left + 1]);
|
||||
points.splice(
|
||||
0..left,
|
||||
vec![points[left].lerp(&points[left + 1], (d - total) / delta)],
|
||||
);
|
||||
|
||||
let total = last - ax!(points[right + 1]);
|
||||
let delta = ax!(points[right + 1]) - ax!(points[right]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for SurfacePathIterator {
|
||||
type Item = SurfacePath;
|
||||
|
||||
@@ -82,8 +110,10 @@ impl Iterator for SurfacePathIterator {
|
||||
self.slices.retain_mut(|slice| !slice.is_empty());
|
||||
|
||||
let (h_axis, _) = self.axis.other();
|
||||
let mut iter = self.slices.iter_mut();
|
||||
|
||||
let ring = self.slices.first_mut()?.pop()?;
|
||||
let mut ring = iter.next()?.pop()?;
|
||||
// TODO: squish(&mut ring.points, h_axis, self.nozzle_width);
|
||||
let mut item = Self::Item {
|
||||
i: ring.i..=ring.i,
|
||||
d: ring.d..=ring.d,
|
||||
@@ -92,7 +122,7 @@ impl Iterator for SurfacePathIterator {
|
||||
path: ring.points,
|
||||
};
|
||||
|
||||
for slice in self.slices.iter_mut().skip(1) {
|
||||
for slice in iter {
|
||||
if *item.i.end() != slice[0].i - 1 {
|
||||
break;
|
||||
}
|
||||
@@ -136,9 +166,11 @@ impl Iterator for SurfacePathIterator {
|
||||
}
|
||||
|
||||
if let Some(mut ring) = index.map(|i| slice.remove(i)) {
|
||||
// TODO: squish(&mut ring.points, h_axis, self.nozzle_width);
|
||||
if needs_reverse {
|
||||
ring.points.reverse();
|
||||
}
|
||||
|
||||
item.i = *item.i.start()..=ring.i;
|
||||
item.d = *item.d.start()..=ring.d;
|
||||
item.path.append(&mut ring.points);
|
||||
|
||||
@@ -1,47 +1,48 @@
|
||||
use bvh::bvh::BvhNode;
|
||||
use nalgebra::Point3;
|
||||
|
||||
use super::{mesh::Mesh, slice_path::SlicePath, z_projection::ToolpathIntersects, FloatValue};
|
||||
use crate::slicer::sdf::Sdf3dModifiers;
|
||||
|
||||
pub fn trace_surface(slice: &mut SlicePath, surface: &Mesh, a: FloatValue) {
|
||||
slice.points.retain_mut(|point| {
|
||||
let mut stack = Vec::<usize>::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);
|
||||
}
|
||||
if child_r_aabb.toolpath_intersects(point, a) {
|
||||
stack.push(child_r_index);
|
||||
}
|
||||
use super::{
|
||||
mesh::Mesh,
|
||||
sdf::{Sdf, SdfInfiniteCone},
|
||||
z_projection::ToolpathIntersects,
|
||||
FloatValue,
|
||||
};
|
||||
|
||||
pub fn trace_surface(point: &Point3<FloatValue>, surface: &Mesh, a: FloatValue) -> bool {
|
||||
let sdf = SdfInfiniteCone::new(a);
|
||||
let mut stack = Vec::<usize>::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,
|
||||
} => {
|
||||
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;
|
||||
}
|
||||
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];
|
||||
if sdf.translate(triangle.a).sdf(point) < 0.0
|
||||
|| sdf.translate(triangle.b).sdf(point) < 0.0
|
||||
|| sdf.translate(triangle.c).sdf(point) < 0.0
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ impl Triangle {
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
normal: (b - a).cross(&(c - a)).into(),
|
||||
normal: (b - a).cross(&(c - a)).normalize(),
|
||||
node_index: 0,
|
||||
aabb,
|
||||
}
|
||||
@@ -44,13 +44,6 @@ impl Triangle {
|
||||
self.has_point(other.a) || self.has_point(other.b) || self.has_point(other.c)
|
||||
}
|
||||
|
||||
pub fn shares_edge_with_triangle(&self, other: Triangle) -> bool {
|
||||
let a = self.has_point(other.a);
|
||||
let b = self.has_point(other.b);
|
||||
let c = self.has_point(other.c);
|
||||
a && b || a && c || b && c
|
||||
}
|
||||
|
||||
pub fn intersect(&self, value: FloatValue, axis: usize) -> Option<Line3> {
|
||||
let mut intersection = Vec::<Point3<FloatValue>>::with_capacity(3);
|
||||
let mut last = &self.c;
|
||||
|
||||
122
flake.nix
122
flake.nix
@@ -4,59 +4,77 @@
|
||||
rust-overlay.url = "github:oxalica/rust-overlay";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
rust-overlay,
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (system: let
|
||||
overlays = [(import rust-overlay)];
|
||||
pkgs = import nixpkgs {inherit system overlays;};
|
||||
rust-bin = pkgs.rust-bin.stable.latest.default.override {
|
||||
targets = [ "wasm32-unknown-unknown" ];
|
||||
extensions = ["rust-src" "rust-std" "clippy" "rust-analyzer"];
|
||||
};
|
||||
fontMin = pkgs.python311.withPackages (ps: with ps; [brotli fonttools] ++ (with fonttools.optional-dependencies; [woff]));
|
||||
tauriPkgs = nixpkgs.legacyPackages.${system};
|
||||
libraries = with tauriPkgs; [
|
||||
webkitgtk
|
||||
gtk3
|
||||
cairo
|
||||
gdk-pixbuf
|
||||
glib
|
||||
dbus
|
||||
openssl_3
|
||||
librsvg
|
||||
];
|
||||
packages =
|
||||
(with pkgs; [
|
||||
nodejs_18
|
||||
nodePackages.pnpm
|
||||
rust-bin
|
||||
fontMin
|
||||
wasm-pack
|
||||
])
|
||||
++ (with tauriPkgs; [
|
||||
curl
|
||||
wget
|
||||
pkg-config
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
rust-overlay,
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
overlays = [ (import rust-overlay) ];
|
||||
pkgs = import nixpkgs { inherit system overlays; };
|
||||
rust-bin = pkgs.rust-bin.nightly.latest.default.override {
|
||||
targets = [ "wasm32-unknown-unknown" ];
|
||||
extensions = [
|
||||
"rust-src"
|
||||
"rust-std"
|
||||
"clippy"
|
||||
"rust-analyzer"
|
||||
];
|
||||
};
|
||||
fontMin = pkgs.python311.withPackages (
|
||||
ps:
|
||||
with ps;
|
||||
[
|
||||
brotli
|
||||
fonttools
|
||||
]
|
||||
++ (with fonttools.optional-dependencies; [ woff ])
|
||||
);
|
||||
tauriPkgs = nixpkgs.legacyPackages.${system};
|
||||
libraries = with tauriPkgs; [
|
||||
webkitgtk
|
||||
gtk3
|
||||
cairo
|
||||
gdk-pixbuf
|
||||
glib
|
||||
dbus
|
||||
openssl_3
|
||||
glib
|
||||
gtk3
|
||||
libsoup
|
||||
webkitgtk
|
||||
librsvg
|
||||
# serial plugin
|
||||
udev
|
||||
]);
|
||||
in {
|
||||
devShell = pkgs.mkShell {
|
||||
buildInputs = packages;
|
||||
shellHook = ''
|
||||
export LD_LIBRARY_PATH=${pkgs.lib.makeLibraryPath libraries}:$LD_LIBRARY_PATH
|
||||
'';
|
||||
};
|
||||
});
|
||||
];
|
||||
packages =
|
||||
(with pkgs; [
|
||||
nodejs_18
|
||||
nodePackages.pnpm
|
||||
rust-bin
|
||||
fontMin
|
||||
wasm-pack
|
||||
])
|
||||
++ (with tauriPkgs; [
|
||||
curl
|
||||
wget
|
||||
pkg-config
|
||||
dbus
|
||||
openssl_3
|
||||
glib
|
||||
gtk3
|
||||
libsoup
|
||||
webkitgtk
|
||||
librsvg
|
||||
# serial plugin
|
||||
udev
|
||||
]);
|
||||
in
|
||||
{
|
||||
devShell = pkgs.mkShell {
|
||||
buildInputs = packages;
|
||||
shellHook = ''
|
||||
export LD_LIBRARY_PATH=${pkgs.lib.makeLibraryPath libraries}:$LD_LIBRARY_PATH
|
||||
'';
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,16 +33,17 @@
|
||||
progressLayer.set(event.data.layer);
|
||||
break;
|
||||
}
|
||||
case 'layer': {
|
||||
case 'result': {
|
||||
layers.update((layers) => {
|
||||
const layer = event.data.data;
|
||||
if (layer.type === 'ring' || layer.type === 'path') {
|
||||
layers.push(
|
||||
Array.from({ length: layer.position.length / 3 }, (_, i) =>
|
||||
new Vector3().fromArray(layer.position, i * 3)
|
||||
)
|
||||
);
|
||||
} else if (layer.type === 'surface') {
|
||||
for (const layer of event.data.data.slices) {
|
||||
if (layer.type === 'ring' || layer.type === 'path') {
|
||||
layers.push(
|
||||
Array.from({ length: layer.position.length / 3 }, (_, i) =>
|
||||
new Vector3().fromArray(layer.position, i * 3)
|
||||
)
|
||||
);
|
||||
} else if (layer.type === 'surface') {
|
||||
}
|
||||
}
|
||||
return layers;
|
||||
});
|
||||
@@ -81,7 +82,7 @@
|
||||
tolerance,
|
||||
maxNonPlanarAngle,
|
||||
nozzleDiameter,
|
||||
minSurfacePathLength: nozzleDiameter * 4,
|
||||
minSurfacePathLength: nozzleDiameter * 2,
|
||||
bedNormal: bedNormal.toArray()
|
||||
}
|
||||
} satisfies SliceEvent);
|
||||
@@ -111,7 +112,7 @@
|
||||
<!---{@const color = new Color(0, i / $layers.length, 0.2)}-->
|
||||
<T.Mesh {visible}>
|
||||
<MeshLineGeometry {points} />
|
||||
<MeshLineMaterial width={layerHeight / 6} {color} />
|
||||
<MeshLineMaterial width={nozzleDiameter * 0.25} {color} />
|
||||
</T.Mesh>
|
||||
{/each}
|
||||
|
||||
|
||||
@@ -22,11 +22,9 @@ addEventListener('message', async (event: MessageEvent<WorkerEvent>) => {
|
||||
nozzleDiameter: event.data.data.nozzleDiameter,
|
||||
minSurfacePathLength: event.data.data.minSurfacePathLength
|
||||
});
|
||||
for (const layer of result.slices) {
|
||||
self.postMessage({
|
||||
type: 'layer',
|
||||
data: layer
|
||||
});
|
||||
}
|
||||
self.postMessage({
|
||||
type: 'result',
|
||||
data: result
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user