1#![allow(clippy::manual_strip)]
6#[allow(unused_imports)]
7use super::functions::*;
8#[allow(unused_imports)]
9use super::functions_2::*;
10use std::collections::HashMap;
11
12pub struct StlTriangle {
14 pub normal: [f32; 3],
16 pub v0: [f32; 3],
18 pub v1: [f32; 3],
20 pub v2: [f32; 3],
22}
23pub struct StlMesh {
25 pub triangles: Vec<StlTriangle>,
27 pub name: String,
29}
30impl StlMesh {
31 pub fn new(name: &str) -> Self {
33 StlMesh {
34 triangles: Vec::new(),
35 name: name.to_string(),
36 }
37 }
38 pub fn add_triangle(&mut self, v0: [f32; 3], v1: [f32; 3], v2: [f32; 3]) {
40 let normal = compute_normal(v0, v1, v2);
41 self.triangles.push(StlTriangle { normal, v0, v1, v2 });
42 }
43 pub fn to_binary_bytes(&self) -> Vec<u8> {
48 let n = self.triangles.len();
49 let mut buf = Vec::with_capacity(84 + n * 50);
50 let mut header = [0u8; 80];
51 let name_bytes = self.name.as_bytes();
52 let copy_len = name_bytes.len().min(80);
53 header[..copy_len].copy_from_slice(&name_bytes[..copy_len]);
54 buf.extend_from_slice(&header);
55 buf.extend_from_slice(&(n as u32).to_le_bytes());
56 for tri in &self.triangles {
57 for &f in &tri.normal {
58 buf.extend_from_slice(&f.to_le_bytes());
59 }
60 for &f in &tri.v0 {
61 buf.extend_from_slice(&f.to_le_bytes());
62 }
63 for &f in &tri.v1 {
64 buf.extend_from_slice(&f.to_le_bytes());
65 }
66 for &f in &tri.v2 {
67 buf.extend_from_slice(&f.to_le_bytes());
68 }
69 buf.extend_from_slice(&0u16.to_le_bytes());
70 }
71 buf
72 }
73 pub fn from_binary_bytes(data: &[u8]) -> Result<Self, String> {
75 if data.len() < 84 {
76 return Err(format!(
77 "Binary STL too short: {} bytes (need at least 84)",
78 data.len()
79 ));
80 }
81 let header_bytes = &data[..80];
82 let name = String::from_utf8_lossy(header_bytes)
83 .trim_end_matches('\0')
84 .trim()
85 .to_string();
86 let n = u32::from_le_bytes([data[80], data[81], data[82], data[83]]) as usize;
87 let expected_len = 84 + n * 50;
88 if data.len() < expected_len {
89 return Err(format!(
90 "Binary STL too short for {} triangles: got {} bytes, need {}",
91 n,
92 data.len(),
93 expected_len
94 ));
95 }
96 let mut triangles = Vec::with_capacity(n);
97 for i in 0..n {
98 let base = 84 + i * 50;
99 let normal = read_f32x3(data, base)?;
100 let v0 = read_f32x3(data, base + 12)?;
101 let v1 = read_f32x3(data, base + 24)?;
102 let v2 = read_f32x3(data, base + 36)?;
103 triangles.push(StlTriangle { normal, v0, v1, v2 });
104 }
105 Ok(StlMesh { triangles, name })
106 }
107 pub fn to_ascii(&self) -> String {
109 let mut s = String::new();
110 s.push_str(&format!("solid {}\n", self.name));
111 for tri in &self.triangles {
112 s.push_str(&format!(
113 " facet normal {:.6e} {:.6e} {:.6e}\n",
114 tri.normal[0], tri.normal[1], tri.normal[2]
115 ));
116 s.push_str(" outer loop\n");
117 s.push_str(&format!(
118 " vertex {:.6e} {:.6e} {:.6e}\n",
119 tri.v0[0], tri.v0[1], tri.v0[2]
120 ));
121 s.push_str(&format!(
122 " vertex {:.6e} {:.6e} {:.6e}\n",
123 tri.v1[0], tri.v1[1], tri.v1[2]
124 ));
125 s.push_str(&format!(
126 " vertex {:.6e} {:.6e} {:.6e}\n",
127 tri.v2[0], tri.v2[1], tri.v2[2]
128 ));
129 s.push_str(" endloop\n");
130 s.push_str(" endfacet\n");
131 }
132 s.push_str(&format!("endsolid {}\n", self.name));
133 s
134 }
135 pub fn from_ascii(s: &str) -> Result<Self, String> {
137 let mut lines = s.lines().peekable();
138 let first = lines
139 .next()
140 .ok_or_else(|| "Empty STL string".to_string())?
141 .trim();
142 let name = if first.starts_with("solid") {
143 first[5..].trim().to_string()
144 } else {
145 return Err(format!("Expected 'solid', got: {first}"));
146 };
147 let mut triangles = Vec::new();
148 while let Some(l) = lines.next() {
149 let line = l.trim().to_string();
150 if line.starts_with("endsolid") {
151 break;
152 }
153 if line.is_empty() {
154 continue;
155 }
156 if !line.starts_with("facet normal") {
157 return Err(format!("Expected 'facet normal', got: {line}"));
158 }
159 let normal = parse_vec3_from_line(&line, "facet normal")?;
160 let outer = lines
161 .next()
162 .ok_or_else(|| "Unexpected EOF after facet normal".to_string())?
163 .trim()
164 .to_string();
165 if !outer.starts_with("outer loop") {
166 return Err(format!("Expected 'outer loop', got: {outer}"));
167 }
168 let v0 = parse_vertex_line(
169 lines
170 .next()
171 .ok_or_else(|| "Unexpected EOF reading vertex 0".to_string())?
172 .trim(),
173 )?;
174 let v1 = parse_vertex_line(
175 lines
176 .next()
177 .ok_or_else(|| "Unexpected EOF reading vertex 1".to_string())?
178 .trim(),
179 )?;
180 let v2 = parse_vertex_line(
181 lines
182 .next()
183 .ok_or_else(|| "Unexpected EOF reading vertex 2".to_string())?
184 .trim(),
185 )?;
186 let _endloop = lines
187 .next()
188 .ok_or_else(|| "Unexpected EOF reading endloop".to_string())?;
189 let _endfacet = lines
190 .next()
191 .ok_or_else(|| "Unexpected EOF reading endfacet".to_string())?;
192 triangles.push(StlTriangle { normal, v0, v1, v2 });
193 }
194 Ok(StlMesh { triangles, name })
195 }
196 pub fn surface_area(&self) -> f32 {
198 self.triangles.iter().map(triangle_area).sum()
199 }
200 pub fn bounding_box(&self) -> ([f32; 3], [f32; 3]) {
204 if self.triangles.is_empty() {
205 return ([0.0; 3], [0.0; 3]);
206 }
207 let mut lo = [f32::MAX; 3];
208 let mut hi = [f32::MIN; 3];
209 for tri in &self.triangles {
210 for v in [&tri.v0, &tri.v1, &tri.v2] {
211 for k in 0..3 {
212 if v[k] < lo[k] {
213 lo[k] = v[k];
214 }
215 if v[k] > hi[k] {
216 hi[k] = v[k];
217 }
218 }
219 }
220 }
221 (lo, hi)
222 }
223 pub fn is_watertight(&self) -> bool {
225 let mut edge_counts: HashMap<EdgeKey, usize> = HashMap::new();
226 for tri in &self.triangles {
227 for &(a, b) in &[(tri.v0, tri.v1), (tri.v1, tri.v2), (tri.v2, tri.v0)] {
228 let key = EdgeKey::new(a, b);
229 *edge_counts.entry(key).or_insert(0) += 1;
230 }
231 }
232 edge_counts.values().all(|&c| c == 2)
233 }
234}
235#[derive(Debug, Clone, Default)]
237pub struct StlValidationReport {
238 pub boundary_edge_count: usize,
240 pub is_watertight: bool,
242 pub is_manifold: bool,
244 pub degenerate_count: usize,
246}
247#[allow(dead_code)]
249#[derive(Debug, Clone)]
250pub struct TriangleMesh {
251 pub positions: Vec<[f32; 3]>,
253 pub normals: Vec<[f32; 3]>,
255 pub indices: Vec<[usize; 3]>,
257}
258#[derive(Debug, Clone, Copy, Default)]
262pub struct StlColor {
263 pub r: u8,
265 pub g: u8,
267 pub b: u8,
269}
270impl StlColor {
271 pub fn new(r: u8, g: u8, b: u8) -> Self {
273 Self {
274 r: r & 0x1F,
275 g: g & 0x1F,
276 b: b & 0x1F,
277 }
278 }
279 pub fn encode(&self) -> u16 {
281 0x8000 | ((self.r as u16) << 10) | ((self.g as u16) << 5) | (self.b as u16)
282 }
283 pub fn decode(attr: u16) -> Option<Self> {
286 if attr & 0x8000 == 0 {
287 return None;
288 }
289 Some(Self {
290 r: ((attr >> 10) & 0x1F) as u8,
291 g: ((attr >> 5) & 0x1F) as u8,
292 b: (attr & 0x1F) as u8,
293 })
294 }
295 pub fn to_normalized(&self) -> [f32; 3] {
297 [
298 self.r as f32 / 31.0,
299 self.g as f32 / 31.0,
300 self.b as f32 / 31.0,
301 ]
302 }
303}
304#[allow(dead_code)]
306#[derive(Debug, Clone)]
307pub struct StlQualityMetrics {
308 pub max_aspect_ratio: f32,
310 pub min_area: f32,
312 pub max_area: f32,
314 pub degenerate_fraction: f32,
316 pub has_bad_geometry: bool,
318}
319#[allow(dead_code)]
321#[derive(Debug, Clone)]
322pub struct StlValidation {
323 pub degenerate_count: usize,
325 pub non_manifold_edges: usize,
327 pub is_watertight: bool,
329 pub nan_inf_count: usize,
331 pub normals_valid: bool,
333}
334#[derive(PartialEq, Eq, Hash)]
336pub(super) struct EdgeKey {
337 pub(super) a: [u32; 3],
338 pub(super) b: [u32; 3],
339}
340impl EdgeKey {
341 pub(super) fn new(p: [f32; 3], q: [f32; 3]) -> Self {
342 let pa = p.map(f32::to_bits);
343 let qa = q.map(f32::to_bits);
344 if pa <= qa {
345 EdgeKey { a: pa, b: qa }
346 } else {
347 EdgeKey { a: qa, b: pa }
348 }
349 }
350}
351#[allow(dead_code)]
353#[derive(Debug, Clone)]
354pub struct StlStatistics {
355 pub triangle_count: usize,
357 pub surface_area: f32,
359 pub bb_min: [f32; 3],
361 pub bb_max: [f32; 3],
363 pub bb_size: [f32; 3],
365 pub avg_triangle_area: f32,
367 pub min_triangle_area: f32,
369 pub max_triangle_area: f32,
371 pub avg_edge_length: f32,
373 pub approx_unique_vertices: usize,
375}
376#[allow(dead_code)]
378#[derive(Debug, Clone)]
379pub struct WeldedMesh {
380 pub vertices: Vec<[f32; 3]>,
382 pub triangles: Vec<[usize; 3]>,
384}