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 let i0 = self.indices[i] as usize;
374 let i1 = self.indices[i + 1] as usize;
375 let i2 = self.indices[i + 2] as usize;
376
377 if i0 * 3 + 2 >= self.positions.len()
378 || i1 * 3 + 2 >= self.positions.len()
379 || i2 * 3 + 2 >= self.positions.len()
380 {
381 removed_count += 1;
383 continue;
384 }
385
386 let p0 = (
387 self.positions[i0 * 3],
388 self.positions[i0 * 3 + 1],
389 self.positions[i0 * 3 + 2],
390 );
391 let p1 = (
392 self.positions[i1 * 3],
393 self.positions[i1 * 3 + 1],
394 self.positions[i1 * 3 + 2],
395 );
396 let p2 = (
397 self.positions[i2 * 3],
398 self.positions[i2 * 3 + 1],
399 self.positions[i2 * 3 + 2],
400 );
401
402 let edge01_sq = (p1.0 - p0.0).powi(2) + (p1.1 - p0.1).powi(2) + (p1.2 - p0.2).powi(2);
404 let edge12_sq = (p2.0 - p1.0).powi(2) + (p2.1 - p1.1).powi(2) + (p2.2 - p1.2).powi(2);
405 let edge20_sq = (p0.0 - p2.0).powi(2) + (p0.1 - p2.1).powi(2) + (p0.2 - p2.2).powi(2);
406
407 if edge01_sq <= max_edge_sq && edge12_sq <= max_edge_sq && edge20_sq <= max_edge_sq {
409 valid_indices.push(self.indices[i]);
411 valid_indices.push(self.indices[i + 1]);
412 valid_indices.push(self.indices[i + 2]);
413 } else {
414 removed_count += 1;
416 }
417 }
418
419 self.indices = valid_indices;
420 removed_count
421 }
422}
423
424impl Default for Mesh {
425 fn default() -> Self {
426 Self::new()
427 }
428}
429
430#[cfg(test)]
431mod tests {
432 use super::*;
433
434 #[test]
435 fn test_mesh_creation() {
436 let mesh = Mesh::new();
437 assert!(mesh.is_empty());
438 assert_eq!(mesh.vertex_count(), 0);
439 assert_eq!(mesh.triangle_count(), 0);
440 }
441
442 #[test]
443 fn test_add_vertex() {
444 let mut mesh = Mesh::new();
445 mesh.add_vertex(Point3::new(1.0, 2.0, 3.0), Vector3::new(0.0, 0.0, 1.0));
446 assert_eq!(mesh.vertex_count(), 1);
447 assert_eq!(mesh.positions, vec![1.0, 2.0, 3.0]);
448 assert_eq!(mesh.normals, vec![0.0, 0.0, 1.0]);
449 }
450
451 #[test]
452 fn test_merge() {
453 let mut mesh1 = Mesh::new();
454 mesh1.add_vertex(Point3::new(0.0, 0.0, 0.0), Vector3::z());
455 mesh1.add_triangle(0, 1, 2);
456
457 let mut mesh2 = Mesh::new();
458 mesh2.add_vertex(Point3::new(1.0, 1.0, 1.0), Vector3::y());
459 mesh2.add_triangle(0, 1, 2);
460
461 mesh1.merge(&mesh2);
462 assert_eq!(mesh1.vertex_count(), 2);
463 assert_eq!(mesh1.triangle_count(), 2);
464 }
465
466 #[test]
467 fn test_coordinate_shift_creation() {
468 let shift = CoordinateShift::new(500000.0, 5000000.0, 100.0);
469 assert!(shift.is_significant());
470 assert!(!shift.is_zero());
471
472 let zero_shift = CoordinateShift::default();
473 assert!(!zero_shift.is_significant());
474 assert!(zero_shift.is_zero());
475 }
476
477 #[test]
478 fn test_add_vertex_with_shift_preserves_precision() {
479 let mut mesh = Mesh::new();
484
485 let p1 = Point3::new(2679012.123456, 1247892.654321, 432.111);
487 let p2 = Point3::new(2679012.223456, 1247892.754321, 432.211);
488
489 let shift = CoordinateShift::new(2679012.0, 1247892.0, 432.0);
491
492 mesh.add_vertex_with_shift(p1, Vector3::z(), &shift);
493 mesh.add_vertex_with_shift(p2, Vector3::z(), &shift);
494
495 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];
507 let dy = mesh.positions[4] - mesh.positions[1];
508 let dz = mesh.positions[5] - mesh.positions[2];
509
510 assert!((dx - 0.1).abs() < 0.0001);
512 assert!((dy - 0.1).abs() < 0.0001);
513 assert!((dz - 0.1).abs() < 0.0001);
514 }
515
516 #[test]
517 fn test_apply_shift_to_existing_mesh() {
518 let mut mesh = Mesh::new();
519
520 mesh.positions = vec![
522 500000.0, 5000000.0, 0.0,
523 500010.0, 5000010.0, 10.0,
524 ];
525 mesh.normals = vec![0.0, 0.0, 1.0, 0.0, 0.0, 1.0];
526
527 let shift = CoordinateShift::new(500000.0, 5000000.0, 0.0);
529 mesh.apply_shift(&shift);
530
531 assert!((mesh.positions[0] - 0.0).abs() < 0.001);
533 assert!((mesh.positions[1] - 0.0).abs() < 0.001);
534 assert!((mesh.positions[3] - 10.0).abs() < 0.001);
535 assert!((mesh.positions[4] - 10.0).abs() < 0.001);
536 }
537
538 #[test]
539 fn test_centroid_f64() {
540 let mut mesh = Mesh::new();
541 mesh.positions = vec![
542 0.0, 0.0, 0.0,
543 10.0, 10.0, 10.0,
544 20.0, 20.0, 20.0,
545 ];
546 mesh.normals = vec![0.0; 9];
547
548 let centroid = mesh.centroid_f64();
549 assert!((centroid.x - 10.0).abs() < 0.001);
550 assert!((centroid.y - 10.0).abs() < 0.001);
551 assert!((centroid.z - 10.0).abs() < 0.001);
552 }
553
554 #[test]
555 fn test_precision_comparison_shifted_vs_unshifted() {
556 let base_x = 2679012.0;
561 let base_y = 1247892.0;
562 let offset = 0.001; let p1 = Point3::new(base_x, base_y, 0.0);
565 let p2 = Point3::new(base_x + offset, base_y, 0.0);
566
567 let p1_f32_direct = (p1.x as f32, p1.y as f32);
569 let p2_f32_direct = (p2.x as f32, p2.y as f32);
570 let diff_direct = p2_f32_direct.0 - p1_f32_direct.0;
571
572 let shift = CoordinateShift::new(base_x, base_y, 0.0);
574 let p1_shifted = ((p1.x - shift.x) as f32, (p1.y - shift.y) as f32);
575 let p2_shifted = ((p2.x - shift.x) as f32, (p2.y - shift.y) as f32);
576 let diff_shifted = p2_shifted.0 - p1_shifted.0;
577
578 println!("Direct f32 difference (should be ~0.001): {}", diff_direct);
579 println!("Shifted f32 difference (should be ~0.001): {}", diff_shifted);
580
581 let error_direct = (diff_direct - offset as f32).abs();
583 let error_shifted = (diff_shifted - offset as f32).abs();
584
585 println!("Error without shift: {}m", error_direct);
586 println!("Error with shift: {}m", error_shifted);
587
588 assert!(
591 error_shifted < error_direct || error_shifted < 0.0001,
592 "Shifted precision should be better than direct conversion"
593 );
594 }
595}