Fixed mesh self intersection issue
omg this has been such a long standing issue im actually crying rn
This commit is contained in:
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
A work in progress FOSS slicer for resin printers.
|
A work in progress FOSS slicer for resin printers.
|
||||||
|
|
||||||

|

|
||||||
|
9
TODO.md
9
TODO.md
@@ -1,9 +1,9 @@
|
|||||||
# Todo
|
# Todo
|
||||||
|
|
||||||
- [ ] Verify slicer on more models
|
- [ ] Verify slicer on more models
|
||||||
- [ ] Preprocess mesh to remove self-intersections?
|
- [x] Preprocess mesh to remove self-intersections?
|
||||||
- [ ] Separate each surface from a mesh before slicing to fix the self intersection problem?
|
- [x] Separate each surface from a mesh before slicing to fix the self intersection problem?
|
||||||
- [ ] Integrate remote send into ui
|
- [x] Integrate remote send into ui
|
||||||
- [x] Allow slicing multiple modals at once
|
- [x] Allow slicing multiple modals at once
|
||||||
- [x] Fix translation in slicer (its currently moved by pixels not mm)
|
- [x] Fix translation in slicer (its currently moved by pixels not mm)
|
||||||
- [x] Allow rotating modals
|
- [x] Allow rotating modals
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
- [x] Clamp camera rotations
|
- [x] Clamp camera rotations
|
||||||
- [x] Fix slicing rotated objects
|
- [x] Fix slicing rotated objects
|
||||||
- [ ] 0-pad slice preview layer display so its width doesn't change as you move the slider
|
- [ ] 0-pad slice preview layer display so its width doesn't change as you move the slider
|
||||||
- [ ] Preload layer 1 in slice preview
|
- [x] Preload layer 1 in slice preview
|
||||||
- [x] Better camera movement (left click to orbit, right click to change orbit point)
|
- [x] Better camera movement (left click to orbit, right click to change orbit point)
|
||||||
- [ ] Don't crash when slicing an object outside of bounds
|
- [ ] Don't crash when slicing an object outside of bounds
|
||||||
- [x] Add support for loading .obj files
|
- [x] Add support for loading .obj files
|
||||||
@@ -48,7 +48,6 @@
|
|||||||
- [x] Use egui_dock to get a more clean look
|
- [x] Use egui_dock to get a more clean look
|
||||||
- [x] Align to bed button
|
- [x] Align to bed button
|
||||||
- [x] Define all crate versions in workspace toml
|
- [x] Define all crate versions in workspace toml
|
||||||
- [ ] MQTT commands use refs to strings
|
|
||||||
- [ ] Ask for filename when sending to printer
|
- [ ] Ask for filename when sending to printer
|
||||||
- [ ] Printer scanning (UDP broadcast)
|
- [ ] Printer scanning (UDP broadcast)
|
||||||
- [ ] Verify printer capabilities before uploading / printing
|
- [ ] Verify printer capabilities before uploading / printing
|
||||||
|
@@ -62,6 +62,10 @@ impl Mesh {
|
|||||||
self.faces().get(index).unwrap()
|
self.faces().get(index).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn normal(&self, index: usize) -> &Pos {
|
||||||
|
self.normals().get(index).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn vertex_count(&self) -> usize {
|
pub fn vertex_count(&self) -> usize {
|
||||||
self.vertices().len()
|
self.vertices().len()
|
||||||
}
|
}
|
||||||
@@ -138,6 +142,11 @@ impl Mesh {
|
|||||||
(self.transformation_matrix * pos.push(1.0)).xyz()
|
(self.transformation_matrix * pos.push(1.0)).xyz()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Transforms a normal according to the models scale and rotation.
|
||||||
|
pub fn transform_normal(&self, normal: &Pos) -> Pos {
|
||||||
|
(self.transformation_matrix * normal.to_homogeneous()).xyz()
|
||||||
|
}
|
||||||
|
|
||||||
/// Undoes the transformation of a point from the models translation, scale, and rotation.
|
/// Undoes the transformation of a point from the models translation, scale, and rotation.
|
||||||
pub fn inv_transform(&self, pos: &Pos) -> Pos {
|
pub fn inv_transform(&self, pos: &Pos) -> Pos {
|
||||||
(self.inv_transformation_matrix * pos.push(1.0)).xyz()
|
(self.inv_transformation_matrix * pos.push(1.0)).xyz()
|
||||||
|
@@ -53,7 +53,8 @@ impl Segments {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Intersects a plane with the mesh this Segments instance was built with.
|
/// Intersects a plane with the mesh this Segments instance was built with.
|
||||||
pub fn intersect_plane(&self, mesh: &Mesh, height: f32) -> Vec<Vector3<f32>> {
|
/// Returns a list of line segments along with the direction of the face.
|
||||||
|
pub fn intersect_plane(&self, mesh: &Mesh, height: f32) -> Vec<([Vector3<f32>; 2], bool)> {
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
|
|
||||||
let layer = (height - self.start_height) / self.layer_height;
|
let layer = (height - self.start_height) / self.layer_height;
|
||||||
@@ -62,7 +63,10 @@ impl Segments {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for &face in self.layers[layer as usize].iter() {
|
for &face in self.layers[layer as usize].iter() {
|
||||||
intersect_triangle(mesh, &self.transformed_points, face, height, &mut out);
|
let segment = intersect_triangle(mesh, &self.transformed_points, face, height);
|
||||||
|
if let Some(segment) = segment {
|
||||||
|
out.push((segment, mesh.transform_normal(mesh.normal(face)).x > 0.0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out
|
out
|
||||||
@@ -90,8 +94,7 @@ fn intersect_triangle(
|
|||||||
points: &[Vector3<f32>],
|
points: &[Vector3<f32>],
|
||||||
face: usize,
|
face: usize,
|
||||||
height: f32,
|
height: f32,
|
||||||
out: &mut Vec<Vector3<f32>>,
|
) -> Option<[Vector3<f32>; 2]> {
|
||||||
) {
|
|
||||||
// Get all the vertices of the face
|
// Get all the vertices of the face
|
||||||
let face = mesh.face(face);
|
let face = mesh.face(face);
|
||||||
let v0 = points[face[0] as usize];
|
let v0 = points[face[0] as usize];
|
||||||
@@ -104,13 +107,17 @@ fn intersect_triangle(
|
|||||||
let (a, b, c) = (v0.z - height, v1.z - height, v2.z - height);
|
let (a, b, c) = (v0.z - height, v1.z - height, v2.z - height);
|
||||||
let (a_pos, b_pos, c_pos) = (a > 0.0, b > 0.0, c > 0.0);
|
let (a_pos, b_pos, c_pos) = (a > 0.0, b > 0.0, c > 0.0);
|
||||||
|
|
||||||
|
let mut out = [Vector3::zeros(); 2];
|
||||||
|
let mut n = 0;
|
||||||
|
|
||||||
// Closure called when the line segment from v0 to v1 is intersecting the
|
// Closure called when the line segment from v0 to v1 is intersecting the
|
||||||
// plane. t is how far along the line the intersection is and intersections,
|
// plane. t is how far along the line the intersection is and intersections,
|
||||||
// it well the point that is intersecting with the plane.
|
// it well the point that is intersecting with the plane.
|
||||||
let mut push_intersection = |a: f32, b: f32, v0: Vector3<f32>, v1: Vector3<f32>| {
|
let mut push_intersection = |a: f32, b: f32, v0: Vector3<f32>, v1: Vector3<f32>| {
|
||||||
let t = a / (a - b);
|
let t = a / (a - b);
|
||||||
let intersection = v0 + t * (v1 - v0);
|
let intersection = v0 + t * (v1 - v0);
|
||||||
out.push(intersection);
|
out[n] = intersection;
|
||||||
|
n += 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
// And as you can see my aversion to else blocks now includes if blocks...
|
// And as you can see my aversion to else blocks now includes if blocks...
|
||||||
@@ -119,4 +126,6 @@ fn intersect_triangle(
|
|||||||
(a_pos ^ b_pos).then(|| push_intersection(a, b, v0, v1));
|
(a_pos ^ b_pos).then(|| push_intersection(a, b, v0, v1));
|
||||||
(b_pos ^ c_pos).then(|| push_intersection(b, c, v1, v2));
|
(b_pos ^ c_pos).then(|| push_intersection(b, c, v1, v2));
|
||||||
(c_pos ^ a_pos).then(|| push_intersection(c, a, v2, v0));
|
(c_pos ^ a_pos).then(|| push_intersection(c, a, v2, v0));
|
||||||
|
|
||||||
|
(n == 2).then_some(out)
|
||||||
}
|
}
|
||||||
|
@@ -98,15 +98,11 @@ impl Slicer {
|
|||||||
// model. Because all the faces are triangles, every triangle
|
// model. Because all the faces are triangles, every triangle
|
||||||
// intersection will return two points. These can then be
|
// intersection will return two points. These can then be
|
||||||
// interpreted as line segments making up a polygon.
|
// interpreted as line segments making up a polygon.
|
||||||
let intersections = self
|
let segments = self
|
||||||
.models
|
.models
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.flat_map(|(idx, mesh)| segments[idx].intersect_plane(mesh, height));
|
.flat_map(|(idx, mesh)| segments[idx].intersect_plane(mesh, height))
|
||||||
let segments = intersections
|
|
||||||
.chunks(2)
|
|
||||||
.into_iter()
|
|
||||||
.map(|mut x| (x.next().unwrap(), x.next().unwrap()))
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Creates a new encoded for this layer. Because printers can
|
// Creates a new encoded for this layer. Because printers can
|
||||||
@@ -126,26 +122,49 @@ impl Slicer {
|
|||||||
let yf = y as f32;
|
let yf = y as f32;
|
||||||
let mut intersections = segments
|
let mut intersections = segments
|
||||||
.iter()
|
.iter()
|
||||||
|
.map(|x| (x.0[0], x.0[1], x.1))
|
||||||
// Filtering to only consider segments with one point
|
// Filtering to only consider segments with one point
|
||||||
// above the current row and one point below.
|
// above the current row and one point below.
|
||||||
.filter(|&(a, b)| ((a.y > yf) ^ (b.y > yf)))
|
.filter(|&(a, b, _)| ((a.y > yf) ^ (b.y > yf)))
|
||||||
.map(|(a, b)| {
|
.map(|(a, b, facing)| {
|
||||||
// Get the x position of the line segment at this y
|
// Get the x position of the line segment at this y
|
||||||
let t = (yf - a.y) / (b.y - a.y);
|
let t = (yf - a.y) / (b.y - a.y);
|
||||||
a.x + t * (b.x - a.x)
|
(a.x + t * (b.x - a.x), facing)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Sort all these intersections for run-length encoding
|
// Sort all these intersections for run-length encoding
|
||||||
intersections.sort_by_key(|&x| OrderedFloat(x));
|
intersections.sort_by_key(|&(x, _)| OrderedFloat(x));
|
||||||
|
|
||||||
|
// In order to avoid creating a cavity in the model when
|
||||||
|
// there is an intersection either by the same mesh or
|
||||||
|
// another mesh, these intersections are removed. This is
|
||||||
|
// done by looking at the direction each line segment is
|
||||||
|
// facing. For example, <- <- -> -> would be reduced to <- ->.
|
||||||
|
let mut i = 1;
|
||||||
|
let mut ignore = 0;
|
||||||
|
while i < intersections.len() {
|
||||||
|
let (_, last_facing) = intersections[i - 1];
|
||||||
|
let (_, facing) = intersections[i];
|
||||||
|
|
||||||
|
if facing == last_facing {
|
||||||
|
intersections.remove(i);
|
||||||
|
ignore += 1;
|
||||||
|
} else if ignore > 0 {
|
||||||
|
intersections.remove(i);
|
||||||
|
ignore -= 1;
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Convert the intersections into runs of white pixels to be
|
// Convert the intersections into runs of white pixels to be
|
||||||
// encoded into the layer
|
// encoded into the layer
|
||||||
for span in intersections.chunks_exact(2) {
|
for span in intersections.chunks_exact(2) {
|
||||||
let y_offset = (self.slice_config.platform_resolution.x * y) as u64;
|
let y_offset = (self.slice_config.platform_resolution.x * y) as u64;
|
||||||
|
|
||||||
let a = span[0].round() as u64;
|
let a = span[0].0.round() as u64;
|
||||||
let b = span[1].round() as u64;
|
let b = span[1].0.round() as u64;
|
||||||
|
|
||||||
let start = a + y_offset;
|
let start = a + y_offset;
|
||||||
let end = b + y_offset;
|
let end = b + y_offset;
|
||||||
|
Reference in New Issue
Block a user