Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ overflow-checks = false
inherits = "release"
opt-level = "z"


[dependencies]
# geometry
nalgebra = "0.33"
Expand Down Expand Up @@ -75,6 +76,10 @@ hershey = { version = "0.1.2", optional = true }

doc-image-embed = "0.2.1"

[dependencies.approx]
version = "^0.5"
default-features = false

# wasm
[target.'cfg(any(target_arch = "wasm32", target_arch = "wasm64"))'.dependencies]
getrandom = { version = "0.3", features = ["wasm_js"], optional = true }
Expand Down
6 changes: 1 addition & 5 deletions src/io/dxf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,7 @@ impl<S: Clone + Debug + Send + Sync> Mesh<S> {
let triangles = if poly.vertices.len() > 3 {
poly.triangulate()
} else {
vec![[
poly.vertices[0].clone(),
poly.vertices[1].clone(),
poly.vertices[2].clone(),
]]
vec![[poly.vertices[0], poly.vertices[1], poly.vertices[2]]]
};

for tri in triangles {
Expand Down
6 changes: 3 additions & 3 deletions src/io/obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,9 @@ impl<S: Clone + Debug + Send + Sync> Mesh<S> {
// Convert to triangles if more than 3 vertices
for i in 1..face_vertices.len() - 1 {
let triangle = vec![
face_vertices[0].clone(),
face_vertices[i].clone(),
face_vertices[i + 1].clone(),
face_vertices[0],
face_vertices[i],
face_vertices[i + 1],
];
polygons.push(Polygon::new(triangle, metadata.clone()));
}
Expand Down
4 changes: 0 additions & 4 deletions src/io/stl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@ use geo::CoordsIter;
use nalgebra::{Point3, Vector3};
use std::fmt::Debug;

#[cfg(any(feature = "stl-io", feature = "dxf-io"))]
use core2::io::Cursor;

#[cfg(feature = "stl-io")]
use stl_io;

impl<S: Clone + Debug + Send + Sync> Mesh<S> {
Expand Down Expand Up @@ -75,7 +73,6 @@ impl<S: Clone + Debug + Send + Sync> Mesh<S> {
/// # Ok(())
/// # }
/// ```
#[cfg(feature = "stl-io")]
pub fn to_stl_binary(&self, _name: &str) -> std::io::Result<Vec<u8>> {
use core2::io::Cursor;
use stl_io::{Normal, Triangle, Vertex, write_stl};
Expand Down Expand Up @@ -303,7 +300,6 @@ impl<S: Clone + Debug + Send + Sync> Sketch<S> {
/// # Ok(())
/// # }
/// ```
#[cfg(feature = "stl-io")]
pub fn to_stl_binary(&self, _name: &str) -> std::io::Result<Vec<u8>> {
use core2::io::Cursor;
use stl_io::{Normal, Triangle, Vertex, write_stl};
Expand Down
3 changes: 0 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,3 @@ compile_error!("Either 'delaunay' or 'earcut' feature must be specified, but not
not(any(feature = "f64", feature = "f32"))
))]
compile_error!("Either 'f64' or 'f32' feature must be specified, but not both");

#[cfg(test)]
mod tests;
17 changes: 10 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1131,11 +1131,14 @@ fn main() {
let _ = fs::write("stl/swept_tube.stl", tube.to_stl_ascii("swept_tube"));
}

let rect = Sketch::rectangle(100.0, 60.0, None);
let hilbert = rect.hilbert_curve(6, 2.0);
let hilbert_extruded = hilbert.extrude(10.0);
let _ = fs::write(
"stl/hilbert_extruded.stl",
hilbert_extruded.to_stl_ascii("hilbert"),
);
#[cfg(feature = "stl-io")]
{
let rect = Sketch::rectangle(100.0, 60.0, None);
let hilbert = rect.hilbert_curve(6, 2.0);
let hilbert_extruded = hilbert.extrude(10.0);
let _ = fs::write(
"stl/hilbert_extruded.stl",
hilbert_extruded.to_stl_ascii("hilbert"),
);
}
}
2 changes: 1 addition & 1 deletion src/mesh/bsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ impl<S: Clone + Send + Sync + Debug> Node<S> {
intersection_edges.extend(
crossing_points
.chunks_exact(2)
.map(|chunk| [chunk[0].clone(), chunk[1].clone()]),
.map(|chunk| [chunk[0], chunk[1]]),
);
},

Expand Down
6 changes: 3 additions & 3 deletions src/mesh/flatten_slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ impl<S: Clone + Debug + Send + Sync> Mesh<S> {
let dist_sq = (chain[0].pos - chain[n - 1].pos).norm_squared();
if dist_sq < EPSILON * EPSILON {
// Force them to be exactly the same, closing the line
chain[n - 1] = chain[0].clone();
chain[n - 1] = chain[0];
}

let polyline = LineString::new(
Expand Down Expand Up @@ -210,7 +210,7 @@ fn unify_intersection_edges(edges: &[[Vertex; 2]]) -> Vec<Vec<Vertex>> {
// Our chain starts with `edges[start_edge_idx]`. We can build a small function to “walk”:
// We'll store it in the direction edge[0] -> edge[1]
let e = &edges[start_edge_idx];
let mut chain = vec![e[0].clone(), e[1].clone()];
let mut chain = vec![e[0], e[1]];

// We walk "forward" from edge[1] if possible
extend_chain_forward(&mut chain, &adjacency, &mut visited, edges);
Expand Down Expand Up @@ -264,7 +264,7 @@ fn extend_chain_forward(

// Mark visited
visited[edge_idx] = true;
found_next = Some(next_vertex.clone());
found_next = Some(*next_vertex);
break;
}

Expand Down
5 changes: 1 addition & 4 deletions src/mesh/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,7 @@ impl<S: Clone + Send + Sync + Debug> Mesh<S> {
.flat_map(|poly| {
let sub_tris = poly.subdivide_triangles(levels);
sub_tris.into_iter().map(move |tri| {
Polygon::new(
vec![tri[0].clone(), tri[1].clone(), tri[2].clone()],
poly.metadata.clone(),
)
Polygon::new(vec![tri[0], tri[1], tri[2]], poly.metadata.clone())
})
})
.collect();
Expand Down
8 changes: 4 additions & 4 deletions src/mesh/plane.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,11 +383,11 @@ impl Plane {

// If current vertex is definitely not behind plane, it goes to split_front
if type_i != BACK {
split_front.push(vertex_i.clone());
split_front.push(*vertex_i);
}
// If current vertex is definitely not in front, it goes to split_back
if type_i != FRONT {
split_back.push(vertex_i.clone());
split_back.push(*vertex_i);
}

// If the edge between these two vertices crosses the plane,
Expand All @@ -399,7 +399,7 @@ impl Plane {
let intersection =
(self.offset() - normal.dot(&vertex_i.pos.coords)) / denom;
let vertex_new = vertex_i.interpolate(vertex_j, intersection);
split_front.push(vertex_new.clone());
split_front.push(vertex_new);
split_back.push(vertex_new);
}
}
Expand Down Expand Up @@ -509,7 +509,7 @@ fn test_plane_orientation() {
// Cycling the order of the vertices doesn't change the winding order of the shape,
// so it should not change the resulting plane's normal.
for cycle_rotation in 0..vertices.len() {
let mut vertices = vertices.clone();
let mut vertices = vertices;
vertices.rotate_right(cycle_rotation);
let plane = Plane::from_vertices(vertices.to_vec());

Expand Down
12 changes: 4 additions & 8 deletions src/mesh/polygon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,7 @@ impl<S: Clone + Send + Sync> Polygon<S> {
// Returning it directly avoids robustness problems with very thin
// triangles and makes the fast-path cheaper.
if self.vertices.len() == 3 {
return vec![[
self.vertices[0].clone(),
self.vertices[1].clone(),
self.vertices[2].clone(),
]];
return vec![[self.vertices[0], self.vertices[1], self.vertices[2]]];
}

let normal_3d = self.plane.normal().normalize();
Expand Down Expand Up @@ -418,9 +414,9 @@ pub fn subdivide_triangle(tri: [Vertex; 3]) -> Vec<[Vertex; 3]> {
let v20 = tri[2].interpolate(&tri[0], 0.5);

vec![
[tri[0].clone(), v01.clone(), v20.clone()],
[v01.clone(), tri[1].clone(), v12.clone()],
[v20.clone(), v12.clone(), tri[2].clone()],
[tri[0], v01, v20],
[v01, tri[1], v12],
[v20, v12, tri[2]],
[v01, v12, v20],
]
}
24 changes: 4 additions & 20 deletions src/mesh/shapes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,22 +342,14 @@ impl<S: Clone + Debug + Send + Sync> Mesh<S> {
if !bottom_degenerate {
// Bottom cap: a triangle fan from the bottom center to two consecutive points on the bottom ring.
polygons.push(Polygon::new(
vec![
start_v.clone(),
point(0.0, slice0, -1.0),
point(0.0, slice1, -1.0),
],
vec![start_v, point(0.0, slice0, -1.0), point(0.0, slice1, -1.0)],
metadata.clone(),
));
}
if !top_degenerate {
// Top cap: a triangle fan from the top center to two consecutive points on the top ring.
polygons.push(Polygon::new(
vec![
end_v.clone(),
point(1.0, slice1, 1.0),
point(1.0, slice0, 1.0),
],
vec![end_v, point(1.0, slice1, 1.0), point(1.0, slice0, 1.0)],
metadata.clone(),
));
}
Expand All @@ -368,21 +360,13 @@ impl<S: Clone + Debug + Send + Sync> Mesh<S> {
if bottom_degenerate {
// Bottom is a point (start_v); create a triangle from start_v to two consecutive points on the top ring.
polygons.push(Polygon::new(
vec![
start_v.clone(),
point(1.0, slice0, 0.0),
point(1.0, slice1, 0.0),
],
vec![start_v, point(1.0, slice0, 0.0), point(1.0, slice1, 0.0)],
metadata.clone(),
));
} else if top_degenerate {
// Top is a point (end_v); create a triangle from two consecutive points on the bottom ring to end_v.
polygons.push(Polygon::new(
vec![
point(0.0, slice1, 0.0),
point(0.0, slice0, 0.0),
end_v.clone(),
],
vec![point(0.0, slice1, 0.0), point(0.0, slice0, 0.0), end_v],
metadata.clone(),
));
} else {
Expand Down
28 changes: 28 additions & 0 deletions src/mesh/smoothing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,3 +395,31 @@ impl<S: Clone + Debug + Send + Sync> Mesh<S> {
Mesh::from_polygons(&filtered_polygons, self.metadata.clone())
}
}

#[cfg(test)]
mod test {
use nalgebra::Vector3;

use super::*;

#[test]
fn remove_poor_triangles() {
// Create a degenerate case by making a very thin triangle
let vertices = vec![
Vertex::new(Point3::new(0.0, 0.0, 0.0), Vector3::z()),
Vertex::new(Point3::new(1.0, 0.0, 0.0), Vector3::z()),
Vertex::new(Point3::new(0.5, 1e-8, 0.0), Vector3::z()), // Very thin triangle
];
let bad_polygon: Polygon<()> = Polygon::new(vertices, None);
let csg_with_bad = Mesh::from_polygons(&[bad_polygon], None);

// Remove poor quality triangles
let filtered = csg_with_bad.remove_poor_triangles(0.1);

// Should remove the poor quality triangle
assert!(
filtered.polygons.len() <= csg_with_bad.polygons.len(),
"Should remove or maintain triangle count"
);
}
}
Loading
Loading