u_nesting_cutting/
contour.rs1#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6use u_nesting_core::geometry::{Geometry, Geometry2DExt};
7use u_nesting_core::SolveResult;
8
9pub type ContourId = usize;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
15pub enum ContourType {
16 Exterior,
18 Interior,
20}
21
22#[derive(Debug, Clone)]
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25pub struct CutContour {
26 pub id: ContourId,
28 pub geometry_id: String,
30 pub instance: usize,
32 pub contour_type: ContourType,
34 pub vertices: Vec<(f64, f64)>,
36 pub perimeter: f64,
38 pub centroid: (f64, f64),
40}
41
42pub fn extract_contours<G: Geometry2DExt<Scalar = f64> + Geometry<Scalar = f64>>(
58 solve_result: &SolveResult<f64>,
59 geometries: &[G],
60) -> Vec<CutContour> {
61 let mut contours = Vec::new();
62 let mut next_id: ContourId = 0;
63
64 for placement in &solve_result.placements {
65 let geom = match geometries.iter().find(|g| *g.id() == placement.geometry_id) {
67 Some(g) => g,
68 None => continue,
69 };
70
71 let transform = placement.to_transform_2d();
73
74 let exterior_world = transform.transform_points(geom.outer_ring());
76 let exterior_perim = u_nesting_core::geom::polygon::perimeter(&exterior_world);
77 let exterior_centroid =
78 u_nesting_core::geom::polygon::centroid(&exterior_world).unwrap_or((0.0, 0.0));
79
80 contours.push(CutContour {
81 id: next_id,
82 geometry_id: placement.geometry_id.clone(),
83 instance: placement.instance,
84 contour_type: ContourType::Exterior,
85 vertices: exterior_world,
86 perimeter: exterior_perim,
87 centroid: exterior_centroid,
88 });
89 next_id += 1;
90
91 for hole in geom.holes() {
93 let hole_world = transform.transform_points(hole);
94 let hole_perim = u_nesting_core::geom::polygon::perimeter(&hole_world);
95 let hole_centroid =
96 u_nesting_core::geom::polygon::centroid(&hole_world).unwrap_or((0.0, 0.0));
97
98 contours.push(CutContour {
99 id: next_id,
100 geometry_id: placement.geometry_id.clone(),
101 instance: placement.instance,
102 contour_type: ContourType::Interior,
103 vertices: hole_world,
104 perimeter: hole_perim,
105 centroid: hole_centroid,
106 });
107 next_id += 1;
108 }
109 }
110
111 contours
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_contour_type_equality() {
120 assert_eq!(ContourType::Exterior, ContourType::Exterior);
121 assert_ne!(ContourType::Exterior, ContourType::Interior);
122 }
123
124 #[test]
125 fn test_cut_contour_creation() {
126 let contour = CutContour {
127 id: 0,
128 geometry_id: "rect1".to_string(),
129 instance: 0,
130 contour_type: ContourType::Exterior,
131 vertices: vec![(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0)],
132 perimeter: 40.0,
133 centroid: (5.0, 5.0),
134 };
135 assert_eq!(contour.id, 0);
136 assert_eq!(contour.contour_type, ContourType::Exterior);
137 assert_eq!(contour.vertices.len(), 4);
138 }
139}