Fixed mesh self intersection issue

omg this has been such a long standing issue im actually crying rn
This commit is contained in:
Connor Slade
2024-07-26 01:12:45 -04:00
parent 48aba5a422
commit dd45005c3e
5 changed files with 59 additions and 23 deletions

View File

@@ -2,4 +2,4 @@
A work in progress FOSS slicer for resin printers.
![mslicer_BOg0d3xB6g](https://github.com/user-attachments/assets/aecc299c-07e9-4449-82d2-60777378f870)
![mslicer_BOg0d3xB6g](https://github.com/user-attachments/assets/ce2f1d99-cce8-4006-8c4f-43aecb63e53b)

View File

@@ -1,9 +1,9 @@
# Todo
- [ ] Verify slicer on more models
- [ ] Preprocess mesh to remove self-intersections?
- [ ] Separate each surface from a mesh before slicing to fix the self intersection problem?
- [ ] Integrate remote send into ui
- [x] Preprocess mesh to remove self-intersections?
- [x] Separate each surface from a mesh before slicing to fix the self intersection problem?
- [x] Integrate remote send into ui
- [x] Allow slicing multiple modals at once
- [x] Fix translation in slicer (its currently moved by pixels not mm)
- [x] Allow rotating modals
@@ -26,7 +26,7 @@
- [x] Clamp camera rotations
- [x] Fix slicing rotated objects
- [ ] 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)
- [ ] Don't crash when slicing an object outside of bounds
- [x] Add support for loading .obj files
@@ -48,7 +48,6 @@
- [x] Use egui_dock to get a more clean look
- [x] Align to bed button
- [x] Define all crate versions in workspace toml
- [ ] MQTT commands use refs to strings
- [ ] Ask for filename when sending to printer
- [ ] Printer scanning (UDP broadcast)
- [ ] Verify printer capabilities before uploading / printing

View File

@@ -62,6 +62,10 @@ impl Mesh {
self.faces().get(index).unwrap()
}
pub fn normal(&self, index: usize) -> &Pos {
self.normals().get(index).unwrap()
}
pub fn vertex_count(&self) -> usize {
self.vertices().len()
}
@@ -138,6 +142,11 @@ impl Mesh {
(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.
pub fn inv_transform(&self, pos: &Pos) -> Pos {
(self.inv_transformation_matrix * pos.push(1.0)).xyz()

View File

@@ -53,7 +53,8 @@ impl Segments {
}
/// 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 layer = (height - self.start_height) / self.layer_height;
@@ -62,7 +63,10 @@ impl Segments {
}
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
@@ -90,8 +94,7 @@ fn intersect_triangle(
points: &[Vector3<f32>],
face: usize,
height: f32,
out: &mut Vec<Vector3<f32>>,
) {
) -> Option<[Vector3<f32>; 2]> {
// Get all the vertices of the face
let face = mesh.face(face);
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_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
// plane. t is how far along the line the intersection is and intersections,
// 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 t = a / (a - b);
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...
@@ -119,4 +126,6 @@ fn intersect_triangle(
(a_pos ^ b_pos).then(|| push_intersection(a, b, v0, v1));
(b_pos ^ c_pos).then(|| push_intersection(b, c, v1, v2));
(c_pos ^ a_pos).then(|| push_intersection(c, a, v2, v0));
(n == 2).then_some(out)
}

View File

@@ -98,15 +98,11 @@ impl Slicer {
// model. Because all the faces are triangles, every triangle
// intersection will return two points. These can then be
// interpreted as line segments making up a polygon.
let intersections = self
let segments = self
.models
.iter()
.enumerate()
.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()))
.flat_map(|(idx, mesh)| segments[idx].intersect_plane(mesh, height))
.collect::<Vec<_>>();
// Creates a new encoded for this layer. Because printers can
@@ -126,26 +122,49 @@ impl Slicer {
let yf = y as f32;
let mut intersections = segments
.iter()
.map(|x| (x.0[0], x.0[1], x.1))
// Filtering to only consider segments with one point
// above the current row and one point below.
.filter(|&(a, b)| ((a.y > yf) ^ (b.y > yf)))
.map(|(a, b)| {
.filter(|&(a, b, _)| ((a.y > yf) ^ (b.y > yf)))
.map(|(a, b, facing)| {
// Get the x position of the line segment at this 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<_>>();
// 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
// encoded into the layer
for span in intersections.chunks_exact(2) {
let y_offset = (self.slice_config.platform_resolution.x * y) as u64;
let a = span[0].round() as u64;
let b = span[1].round() as u64;
let a = span[0].0.round() as u64;
let b = span[1].0.round() as u64;
let start = a + y_offset;
let end = b + y_offset;