1use crate::{Mesh, RecognitionError, RecognitionResult};
4use std::fmt::Write;
5
6#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct DebugObjExport {
14 pub obj: String,
16 pub mtl: String,
18}
19
20const COLORS: &[[f64; 3]] = &[
21 [0.894, 0.102, 0.110],
22 [0.216, 0.494, 0.722],
23 [0.302, 0.686, 0.290],
24 [0.596, 0.306, 0.639],
25 [1.000, 0.498, 0.000],
26 [1.000, 1.000, 0.200],
27 [0.651, 0.337, 0.157],
28 [0.969, 0.506, 0.749],
29];
30
31pub fn export_debug_obj(
38 mesh: &Mesh,
39 result: &RecognitionResult,
40) -> Result<DebugObjExport, RecognitionError> {
41 validate_mesh_indices(mesh)?;
42
43 let mut owner = vec![None; mesh.triangles.len()];
46 for (region_index, region) in result.regions.iter().enumerate() {
47 for &triangle in ®ion.triangle_indices {
48 claim(&mut owner, triangle, region_index, "recognized region")?;
49 }
50 }
51 let unresolved_owner = result.regions.len();
52 for &triangle in &result.unresolved_triangles {
53 claim(
54 &mut owner,
55 triangle,
56 unresolved_owner,
57 "unresolved partition",
58 )?;
59 }
60
61 let mut obj = String::new();
62 obj.push_str("# cadmesh-analytic recognition debug export\n");
63 obj.push_str("mtllib analytic_regions.mtl\n");
64 obj.push_str("o analytic_recognition_debug\n");
65 for vertex in &mesh.vertices {
66 writeln!(
67 &mut obj,
68 "v {:.17} {:.17} {:.17}",
69 vertex.x, vertex.y, vertex.z
70 )
71 .unwrap();
72 }
73
74 let mut mtl = String::from("# cadmesh-analytic deterministic debug palette\n");
75 for (region_index, region) in result.regions.iter().enumerate() {
76 let material = format!("region_{region_index:03}");
77 let group = format!(
78 "region_{region_index:03}_{}",
79 region.surface.surface_type().name()
80 );
81 let color = COLORS[region_index % COLORS.len()];
82 write_material(&mut mtl, &material, color);
83 writeln!(&mut obj, "\ng {group}\nusemtl {material}").unwrap();
84 writeln!(
85 &mut obj,
86 "# orientation={} triangles={} rms_error={:.17} max_error={:.17}",
87 region.orientation,
88 region.triangle_indices.len(),
89 region.metrics.rms_error,
90 region.metrics.max_error
91 )
92 .unwrap();
93 write_faces(&mut obj, mesh, ®ion.triangle_indices);
94 }
95
96 if !result.unresolved_triangles.is_empty() {
97 write_material(&mut mtl, "unresolved", [0.55, 0.55, 0.55]);
98 obj.push_str("\ng unresolved\nusemtl unresolved\n");
99 write_faces(&mut obj, mesh, &result.unresolved_triangles);
100 }
101
102 let unassigned: Vec<_> = owner
103 .iter()
104 .enumerate()
105 .filter_map(|(triangle, assigned)| assigned.is_none().then_some(triangle))
106 .collect();
107 if !unassigned.is_empty() {
108 write_material(&mut mtl, "unassigned", [0.08, 0.08, 0.08]);
109 obj.push_str("\ng unassigned\nusemtl unassigned\n");
110 write_faces(&mut obj, mesh, &unassigned);
111 }
112
113 Ok(DebugObjExport { obj, mtl })
114}
115
116fn validate_mesh_indices(mesh: &Mesh) -> Result<(), RecognitionError> {
117 if mesh.vertices.iter().any(|vertex| !vertex.is_finite()) {
118 return Err(RecognitionError::InvalidMesh(
119 "debug export requires finite vertex coordinates".into(),
120 ));
121 }
122 for (triangle_index, triangle) in mesh.triangles.iter().enumerate() {
123 for &vertex in triangle {
124 if vertex as usize >= mesh.vertices.len() {
125 return Err(RecognitionError::InvalidMesh(format!(
126 "triangle {triangle_index} references missing vertex {vertex}"
127 )));
128 }
129 }
130 }
131 Ok(())
132}
133
134fn claim(
135 owner: &mut [Option<usize>],
136 triangle: usize,
137 claimant: usize,
138 partition: &str,
139) -> Result<(), RecognitionError> {
140 let slot = owner.get_mut(triangle).ok_or_else(|| {
141 RecognitionError::InvalidSelection(format!(
142 "{partition} references missing triangle {triangle}"
143 ))
144 })?;
145 if let Some(previous) = *slot {
146 return Err(RecognitionError::InvalidSelection(format!(
147 "triangle {triangle} is assigned more than once (owners {previous} and {claimant})"
148 )));
149 }
150 *slot = Some(claimant);
151 Ok(())
152}
153
154fn write_faces(out: &mut String, mesh: &Mesh, triangle_indices: &[usize]) {
155 for &triangle_index in triangle_indices {
156 let triangle = mesh.triangles[triangle_index];
157 writeln!(
158 out,
159 "f {} {} {}",
160 triangle[0] + 1,
161 triangle[1] + 1,
162 triangle[2] + 1
163 )
164 .unwrap();
165 }
166}
167
168fn write_material(out: &mut String, name: &str, color: [f64; 3]) {
169 writeln!(
170 out,
171 "\nnewmtl {name}\nKd {:.3} {:.3} {:.3}\nKa 0.000 0.000 0.000\nd 1.000",
172 color[0], color[1], color[2]
173 )
174 .unwrap();
175}
176
177