1use nalgebra::{Point3, Vector3};
8
9#[derive(Debug, Clone, Copy, Default)]
12pub struct CoordinateShift {
13 pub x: f64,
15 pub y: f64,
17 pub z: f64,
19}
20
21impl CoordinateShift {
22 #[inline]
24 pub fn new(x: f64, y: f64, z: f64) -> Self {
25 Self { x, y, z }
26 }
27
28 #[inline]
30 pub fn from_point(point: Point3<f64>) -> Self {
31 Self {
32 x: point.x,
33 y: point.y,
34 z: point.z,
35 }
36 }
37
38 #[inline]
40 pub fn is_significant(&self) -> bool {
41 const THRESHOLD: f64 = 10000.0; self.x.abs() > THRESHOLD || self.y.abs() > THRESHOLD || self.z.abs() > THRESHOLD
43 }
44
45 #[inline]
47 pub fn is_zero(&self) -> bool {
48 self.x == 0.0 && self.y == 0.0 && self.z == 0.0
49 }
50}
51
52#[derive(Debug, Clone)]
54pub struct Mesh {
55 pub positions: Vec<f32>,
57 pub normals: Vec<f32>,
59 pub indices: Vec<u32>,
61}
62
63#[derive(Debug, Clone)]
67pub struct SubMesh {
68 pub geometry_id: u32,
70 pub mesh: Mesh,
72}
73
74impl SubMesh {
75 pub fn new(geometry_id: u32, mesh: Mesh) -> Self {
77 Self { geometry_id, mesh }
78 }
79}
80
81#[derive(Debug, Clone, Default)]
83pub struct SubMeshCollection {
84 pub sub_meshes: Vec<SubMesh>,
85}
86
87impl SubMeshCollection {
88 pub fn new() -> Self {
90 Self { sub_meshes: Vec::new() }
91 }
92
93 pub fn add(&mut self, geometry_id: u32, mesh: Mesh) {
95 if !mesh.is_empty() {
96 self.sub_meshes.push(SubMesh::new(geometry_id, mesh));
97 }
98 }
99
100 pub fn is_empty(&self) -> bool {
102 self.sub_meshes.is_empty()
103 }
104
105 pub fn len(&self) -> usize {
107 self.sub_meshes.len()
108 }
109
110 pub fn into_combined_mesh(self) -> Mesh {
112 let mut combined = Mesh::new();
113 for sub in self.sub_meshes {
114 combined.merge(&sub.mesh);
115 }
116 combined
117 }
118
119 pub fn iter(&self) -> impl Iterator<Item = &SubMesh> {
121 self.sub_meshes.iter()
122 }
123}
124
125impl Mesh {
126 pub fn new() -> Self {
128 Self {
129 positions: Vec::new(),
130 normals: Vec::new(),
131 indices: Vec::new(),
132 }
133 }
134
135 pub fn with_capacity(vertex_count: usize, index_count: usize) -> Self {
137 Self {
138 positions: Vec::with_capacity(vertex_count * 3),
139 normals: Vec::with_capacity(vertex_count * 3),
140 indices: Vec::with_capacity(index_count),
141 }
142 }
143
144 pub fn from_triangle(v0: &Point3<f64>, v1: &Point3<f64>, v2: &Point3<f64>, normal: &Vector3<f64>) -> Self {
146 let mut mesh = Self::with_capacity(3, 3);
147 mesh.positions = vec![
148 v0.x as f32, v0.y as f32, v0.z as f32,
149 v1.x as f32, v1.y as f32, v1.z as f32,
150 v2.x as f32, v2.y as f32, v2.z as f32,
151 ];
152 mesh.normals = vec![
153 normal.x as f32, normal.y as f32, normal.z as f32,
154 normal.x as f32, normal.y as f32, normal.z as f32,
155 normal.x as f32, normal.y as f32, normal.z as f32,
156 ];
157 mesh.indices = vec![0, 1, 2];
158 mesh
159 }
160
161 #[inline]
163 pub fn add_vertex(&mut self, position: Point3<f64>, normal: Vector3<f64>) {
164 self.positions.push(position.x as f32);
165 self.positions.push(position.y as f32);
166 self.positions.push(position.z as f32);
167
168 self.normals.push(normal.x as f32);
169 self.normals.push(normal.y as f32);
170 self.normals.push(normal.z as f32);
171 }
172
173 #[inline]
186 pub fn add_vertex_with_shift(
187 &mut self,
188 position: Point3<f64>,
189 normal: Vector3<f64>,
190 shift: &CoordinateShift,
191 ) {
192 let shifted_x = position.x - shift.x;
195 let shifted_y = position.y - shift.y;
196 let shifted_z = position.z - shift.z;
197
198 self.positions.push(shifted_x as f32);
199 self.positions.push(shifted_y as f32);
200 self.positions.push(shifted_z as f32);
201
202 self.normals.push(normal.x as f32);
203 self.normals.push(normal.y as f32);
204 self.normals.push(normal.z as f32);
205 }
206
207 #[inline]
210 pub fn apply_shift(&mut self, shift: &CoordinateShift) {
211 if shift.is_zero() {
212 return;
213 }
214 for chunk in self.positions.chunks_exact_mut(3) {
215 chunk[0] = (chunk[0] as f64 - shift.x) as f32;
217 chunk[1] = (chunk[1] as f64 - shift.y) as f32;
218 chunk[2] = (chunk[2] as f64 - shift.z) as f32;
219 }
220 }
221
222 #[inline]
224 pub fn add_triangle(&mut self, i0: u32, i1: u32, i2: u32) {
225 self.indices.push(i0);
226 self.indices.push(i1);
227 self.indices.push(i2);
228 }
229
230 #[inline]
232 pub fn merge(&mut self, other: &Mesh) {
233 if other.is_empty() {
234 return;
235 }
236
237 let vertex_offset = (self.positions.len() / 3) as u32;
238
239 self.positions.reserve(other.positions.len());
241 self.normals.reserve(other.normals.len());
242 self.indices.reserve(other.indices.len());
243
244 self.positions.extend_from_slice(&other.positions);
245 self.normals.extend_from_slice(&other.normals);
246
247 self.indices
249 .extend(other.indices.iter().map(|&i| i + vertex_offset));
250 }
251
252 #[inline]
254 pub fn merge_all(&mut self, meshes: &[Mesh]) {
255 let total_positions: usize = meshes.iter().map(|m| m.positions.len()).sum();
257 let total_indices: usize = meshes.iter().map(|m| m.indices.len()).sum();
258
259 self.positions.reserve(total_positions);
261 self.normals.reserve(total_positions);
262 self.indices.reserve(total_indices);
263
264 for mesh in meshes {
266 if !mesh.is_empty() {
267 let vertex_offset = (self.positions.len() / 3) as u32;
268 self.positions.extend_from_slice(&mesh.positions);
269 self.normals.extend_from_slice(&mesh.normals);
270 self.indices
271 .extend(mesh.indices.iter().map(|&i| i + vertex_offset));
272 }
273 }
274 }
275
276 #[inline]
278 pub fn vertex_count(&self) -> usize {
279 self.positions.len() / 3
280 }
281
282 #[inline]
284 pub fn triangle_count(&self) -> usize {
285 self.indices.len() / 3
286 }
287
288 #[inline]
290 pub fn is_empty(&self) -> bool {
291 self.positions.is_empty()
292 }
293
294 #[inline]
296 pub fn bounds(&self) -> (Point3<f32>, Point3<f32>) {
297 if self.is_empty() {
298 return (Point3::origin(), Point3::origin());
299 }
300
301 let mut min = Point3::new(f32::MAX, f32::MAX, f32::MAX);
302 let mut max = Point3::new(f32::MIN, f32::MIN, f32::MIN);
303
304 self.positions.chunks_exact(3).for_each(|chunk| {
306 let (x, y, z) = (chunk[0], chunk[1], chunk[2]);
307 min.x = min.x.min(x);
308 min.y = min.y.min(y);
309 min.z = min.z.min(z);
310 max.x = max.x.max(x);
311 max.y = max.y.max(y);
312 max.z = max.z.max(z);
313 });
314
315 (min, max)
316 }
317
318 #[inline]
321 pub fn centroid_f64(&self) -> Point3<f64> {
322 if self.is_empty() {
323 return Point3::origin();
324 }
325
326 let mut sum = Point3::new(0.0f64, 0.0f64, 0.0f64);
327 let count = self.positions.len() / 3;
328
329 self.positions.chunks_exact(3).for_each(|chunk| {
330 sum.x += chunk[0] as f64;
331 sum.y += chunk[1] as f64;
332 sum.z += chunk[2] as f64;
333 });
334
335 Point3::new(
336 sum.x / count as f64,
337 sum.y / count as f64,
338 sum.z / count as f64,
339 )
340 }
341
342 #[inline]
344 pub fn clear(&mut self) {
345 self.positions.clear();
346 self.normals.clear();
347 self.indices.clear();
348 }
349
350 pub fn filter_stretched_triangles(&mut self, max_edge_length: f32) -> usize {
363 if self.is_empty() {
364 return 0;
365 }
366
367 let max_edge_sq = max_edge_length * max_edge_length;
368 let mut valid_indices = Vec::new();
369 let mut removed_count = 0;
370
371 for i in (0..self.indices.len()).step_by(3) {
373 if i + 2 >= self.indices.len() {
374 break;
375 }
376 let i0 = self.indices[i] as usize;
377 let i1 = self.indices[i + 1] as usize;
378 let i2 = self.indices[i + 2] as usize;
379
380 if i0 * 3 + 2 >= self.positions.len()
381 || i1 * 3 + 2 >= self.positions.len()
382 || i2 * 3 + 2 >= self.positions.len()
383 {
384 removed_count += 1;
386 continue;
387 }
388
389 let p0 = (
390 self.positions[i0 * 3],
391 self.positions[i0 * 3 + 1],
392 self.positions[i0 * 3 + 2],
393 );
394 let p1 = (
395 self.positions[i1 * 3],
396 self.positions[i1 * 3 + 1],
397 self.positions[i1 * 3 + 2],
398 );
399 let p2 = (
400 self.positions[i2 * 3],
401 self.positions[i2 * 3 + 1],
402 self.positions[i2 * 3 + 2],
403 );
404
405 let edge01_sq = (p1.0 - p0.0).powi(2) + (p1.1 - p0.1).powi(2) + (p1.2 - p0.2).powi(2);
407 let edge12_sq = (p2.0 - p1.0).powi(2) + (p2.1 - p1.1).powi(2) + (p2.2 - p1.2).powi(2);
408 let edge20_sq = (p0.0 - p2.0).powi(2) + (p0.1 - p2.1).powi(2) + (p0.2 - p2.2).powi(2);
409
410 if edge01_sq <= max_edge_sq && edge12_sq <= max_edge_sq && edge20_sq <= max_edge_sq {
412 valid_indices.push(self.indices[i]);
414 valid_indices.push(self.indices[i + 1]);
415 valid_indices.push(self.indices[i + 2]);
416 } else {
417 removed_count += 1;
419 }
420 }
421
422 self.indices = valid_indices;
423 removed_count
424 }
425}
426
427impl Default for Mesh {
428 fn default() -> Self {
429 Self::new()
430 }
431}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436
437 #[test]
438 fn test_mesh_creation() {
439 let mesh = Mesh::new();
440 assert!(mesh.is_empty());
441 assert_eq!(mesh.vertex_count(), 0);
442 assert_eq!(mesh.triangle_count(), 0);
443 }
444
445 #[test]
446 fn test_add_vertex() {
447 let mut mesh = Mesh::new();
448 mesh.add_vertex(Point3::new(1.0, 2.0, 3.0), Vector3::new(0.0, 0.0, 1.0));
449 assert_eq!(mesh.vertex_count(), 1);
450 assert_eq!(mesh.positions, vec![1.0, 2.0, 3.0]);
451 assert_eq!(mesh.normals, vec![0.0, 0.0, 1.0]);
452 }
453
454 #[test]
455 fn test_merge() {
456 let mut mesh1 = Mesh::new();
457 mesh1.add_vertex(Point3::new(0.0, 0.0, 0.0), Vector3::z());
458 mesh1.add_triangle(0, 1, 2);
459
460 let mut mesh2 = Mesh::new();
461 mesh2.add_vertex(Point3::new(1.0, 1.0, 1.0), Vector3::y());
462 mesh2.add_triangle(0, 1, 2);
463
464 mesh1.merge(&mesh2);
465 assert_eq!(mesh1.vertex_count(), 2);
466 assert_eq!(mesh1.triangle_count(), 2);
467 }
468
469 #[test]
470 fn test_coordinate_shift_creation() {
471 let shift = CoordinateShift::new(500000.0, 5000000.0, 100.0);
472 assert!(shift.is_significant());
473 assert!(!shift.is_zero());
474
475 let zero_shift = CoordinateShift::default();
476 assert!(!zero_shift.is_significant());
477 assert!(zero_shift.is_zero());
478 }
479
480 #[test]
481 fn test_add_vertex_with_shift_preserves_precision() {
482 let mut mesh = Mesh::new();
487
488 let p1 = Point3::new(2679012.123456, 1247892.654321, 432.111);
490 let p2 = Point3::new(2679012.223456, 1247892.754321, 432.211);
491
492 let shift = CoordinateShift::new(2679012.0, 1247892.0, 432.0);
494
495 mesh.add_vertex_with_shift(p1, Vector3::z(), &shift);
496 mesh.add_vertex_with_shift(p2, Vector3::z(), &shift);
497
498 assert!((mesh.positions[0] - 0.123456).abs() < 0.0001); assert!((mesh.positions[1] - 0.654321).abs() < 0.0001); assert!((mesh.positions[2] - 0.111).abs() < 0.0001); assert!((mesh.positions[3] - 0.223456).abs() < 0.0001); assert!((mesh.positions[4] - 0.754321).abs() < 0.0001); assert!((mesh.positions[5] - 0.211).abs() < 0.0001); let dx = mesh.positions[3] - mesh.positions[0];
510 let dy = mesh.positions[4] - mesh.positions[1];
511 let dz = mesh.positions[5] - mesh.positions[2];
512
513 assert!((dx - 0.1).abs() < 0.0001);
515 assert!((dy - 0.1).abs() < 0.0001);
516 assert!((dz - 0.1).abs() < 0.0001);
517 }
518
519 #[test]
520 fn test_apply_shift_to_existing_mesh() {
521 let mut mesh = Mesh::new();
522
523 mesh.positions = vec![
525 500000.0, 5000000.0, 0.0,
526 500010.0, 5000010.0, 10.0,
527 ];
528 mesh.normals = vec![0.0, 0.0, 1.0, 0.0, 0.0, 1.0];
529
530 let shift = CoordinateShift::new(500000.0, 5000000.0, 0.0);
532 mesh.apply_shift(&shift);
533
534 assert!((mesh.positions[0] - 0.0).abs() < 0.001);
536 assert!((mesh.positions[1] - 0.0).abs() < 0.001);
537 assert!((mesh.positions[3] - 10.0).abs() < 0.001);
538 assert!((mesh.positions[4] - 10.0).abs() < 0.001);
539 }
540
541 #[test]
542 fn test_centroid_f64() {
543 let mut mesh = Mesh::new();
544 mesh.positions = vec![
545 0.0, 0.0, 0.0,
546 10.0, 10.0, 10.0,
547 20.0, 20.0, 20.0,
548 ];
549 mesh.normals = vec![0.0; 9];
550
551 let centroid = mesh.centroid_f64();
552 assert!((centroid.x - 10.0).abs() < 0.001);
553 assert!((centroid.y - 10.0).abs() < 0.001);
554 assert!((centroid.z - 10.0).abs() < 0.001);
555 }
556
557 #[test]
558 fn test_precision_comparison_shifted_vs_unshifted() {
559 let base_x = 2679012.0;
564 let base_y = 1247892.0;
565 let offset = 0.001; let p1 = Point3::new(base_x, base_y, 0.0);
568 let p2 = Point3::new(base_x + offset, base_y, 0.0);
569
570 let p1_f32_direct = (p1.x as f32, p1.y as f32);
572 let p2_f32_direct = (p2.x as f32, p2.y as f32);
573 let diff_direct = p2_f32_direct.0 - p1_f32_direct.0;
574
575 let shift = CoordinateShift::new(base_x, base_y, 0.0);
577 let p1_shifted = ((p1.x - shift.x) as f32, (p1.y - shift.y) as f32);
578 let p2_shifted = ((p2.x - shift.x) as f32, (p2.y - shift.y) as f32);
579 let diff_shifted = p2_shifted.0 - p1_shifted.0;
580
581 println!("Direct f32 difference (should be ~0.001): {}", diff_direct);
582 println!("Shifted f32 difference (should be ~0.001): {}", diff_shifted);
583
584 let error_direct = (diff_direct - offset as f32).abs();
586 let error_shifted = (diff_shifted - offset as f32).abs();
587
588 println!("Error without shift: {}m", error_direct);
589 println!("Error with shift: {}m", error_shifted);
590
591 assert!(
594 error_shifted < error_direct || error_shifted < 0.0001,
595 "Shifted precision should be better than direct conversion"
596 );
597 }
598}