From 0926f043fea1e961de7029ba8a8017e44680a4ee Mon Sep 17 00:00:00 2001 From: qthree Date: Mon, 16 Mar 2026 20:09:35 +0700 Subject: [PATCH 1/2] fix From> to Mesh geo::Polygon::new() ensures LineString is closed: pushes copy of first() to the end but geo_poly_to_csg_polys ignores duplicate Coord at the end of LineString conflicting edges cause panic in spade crate (triangulation) --- src/mesh/mod.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/mesh/mod.rs b/src/mesh/mod.rs index 9e43d3e..498a46c 100644 --- a/src/mesh/mod.rs +++ b/src/mesh/mod.rs @@ -797,6 +797,21 @@ impl CSG for Mesh { impl From> for Mesh { /// Convert a Sketch into a Mesh. fn from(sketch: Sketch) -> Self { + /// Helper function to convert a geo::LineString to a Vec + fn geo_line_string_to_vertices( + line_string: &geo::LineString, + ) -> Vec { + let mut vertices: Vec<_> = line_string + .coords_iter() + .map(|c| Vertex::new(Point3::new(c.x, c.y, 0.0), Vector3::z())) + .collect(); + // LineString can closed, which means first and last Coords are the same + // but crate::mesh::polygon::Polygon expects can't have conflicting edges so we need to remove last Vertex + if matches!((vertices.first(), vertices.last()), (Some(first), Some(last)) if first == last) { + vertices.pop(); + } + vertices + } /// Helper function to convert a geo::Polygon to a Vec fn geo_poly_to_csg_polys( poly2d: &GeoPolygon, @@ -805,11 +820,7 @@ impl From> for Mesh { let mut all_polygons = Vec::new(); // Handle the exterior ring - let outer_vertices_3d: Vec<_> = poly2d - .exterior() - .coords_iter() - .map(|c| Vertex::new(Point3::new(c.x, c.y, 0.0), Vector3::z())) - .collect(); + let outer_vertices_3d = geo_line_string_to_vertices(poly2d.exterior()); if outer_vertices_3d.len() >= 3 { all_polygons.push(Polygon::new(outer_vertices_3d, metadata.clone())); @@ -817,10 +828,7 @@ impl From> for Mesh { // Handle interior rings (holes) for ring in poly2d.interiors() { - let hole_vertices_3d: Vec<_> = ring - .coords_iter() - .map(|c| Vertex::new(Point3::new(c.x, c.y, 0.0), Vector3::z())) - .collect(); + let hole_vertices_3d =geo_line_string_to_vertices(ring); if hole_vertices_3d.len() >= 3 { all_polygons.push(Polygon::new(hole_vertices_3d, metadata.clone())); } From 7febc5fb1e4125ba62e52ca9769210958acd4ef1 Mon Sep 17 00:00:00 2001 From: qthree Date: Mon, 16 Mar 2026 20:45:58 +0700 Subject: [PATCH 2/2] apply review suggestions --- src/mesh/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/mod.rs b/src/mesh/mod.rs index 498a46c..ac303df 100644 --- a/src/mesh/mod.rs +++ b/src/mesh/mod.rs @@ -807,7 +807,7 @@ impl From> for Mesh { .collect(); // LineString can closed, which means first and last Coords are the same // but crate::mesh::polygon::Polygon expects can't have conflicting edges so we need to remove last Vertex - if matches!((vertices.first(), vertices.last()), (Some(first), Some(last)) if first == last) { + if vertices.first() == vertices.last() { vertices.pop(); } vertices @@ -828,7 +828,7 @@ impl From> for Mesh { // Handle interior rings (holes) for ring in poly2d.interiors() { - let hole_vertices_3d =geo_line_string_to_vertices(ring); + let hole_vertices_3d = geo_line_string_to_vertices(ring); if hole_vertices_3d.len() >= 3 { all_polygons.push(Polygon::new(hole_vertices_3d, metadata.clone())); }