microcad_core/geo3d/
reflect.rs

1// Copyright © 2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Reflect and mirror 3D geometries.
5//!
6//! `Mirror` duplicates and keep the original geometry, where is `Reflect` only transform the original geometry.
7
8use super::*;
9use cgmath::InnerSpace;
10
11/// Reflects a 2D geometry along a line.
12pub trait Reflect3D<T = Self> {
13    /// Mirror a 2D geometry.
14    fn reflect_3d(&self, plane: &Plane) -> T;
15}
16
17/// Mirrors a 2D geometry, keeping the original (in contrast to `reflect`).
18pub trait Mirror3D<T = Self>: Reflect3D<T> + Into<Geometry3D> {
19    /// Mirror operation.
20    fn mirror_3d(self, plane: &Plane) -> Geometries3D {
21        let orig: Geometry3D = self.into();
22        let refl: Geometry3D = orig.reflect_3d(plane);
23        Geometries3D::new(vec![orig, refl])
24    }
25}
26
27impl Reflect3D for crate::Vec3 {
28    fn reflect_3d(&self, plane: &Plane) -> Self {
29        let n = plane.n.normalize(); // Ensure the normal is unit length
30        let v = *self - plane.p; // Vector from plane point to the point
31        let dist = v.dot(n); // Signed distance from plane
32        *self - 2.0 * dist * n // Reflect across the plane
33    }
34}
35
36impl Reflect3D for cgmath::Vector3<f32> {
37    fn reflect_3d(&self, plane: &Plane) -> Self {
38        let n: cgmath::Vector3<f32> = plane.n.normalize().cast().expect("Valid cast"); // Ensure the normal is unit length
39        let p: cgmath::Vector3<f32> = plane.p.cast().expect("Valid cast");
40        let v = *self - p; // Vector from plane point to the point
41        let dist = v.dot(n); // Signed distance from plane
42        *self - 2.0 * dist * n // Reflect across the plane        
43    }
44}
45
46impl Reflect3D for TriangleMesh {
47    fn reflect_3d(&self, plane: &Plane) -> Self {
48        Self {
49            positions: self
50                .positions
51                .iter()
52                .map(|pos| pos.reflect_3d(plane))
53                .collect(),
54            normals: self.normals.clone(), // Flip normals here?
55            triangle_indices: self
56                .triangle_indices
57                .iter()
58                .map(|tri| tri.flipped())
59                .collect(),
60        }
61    }
62}
63
64impl Mirror3D for TriangleMesh {}
65
66impl Reflect3D for Geometries3D {
67    fn reflect_3d(&self, plane: &Plane) -> Self {
68        Self::from_iter(
69            self.iter()
70                .map(|geometry| std::rc::Rc::new(geometry.as_ref().reflect_3d(plane))),
71        )
72    }
73}
74
75impl Mirror3D for Geometries3D {}
76
77impl Reflect3D for Geometry3D {
78    fn reflect_3d(&self, plane: &Plane) -> Self {
79        match &self {
80            Geometry3D::Mesh(triangle_mesh) => triangle_mesh.reflect_3d(plane).into(),
81            Geometry3D::Manifold(manifold) => TriangleMesh::from(manifold.to_mesh())
82                .reflect_3d(plane)
83                .into(),
84            Geometry3D::Collection(collection) => collection.reflect_3d(plane).into(),
85        }
86    }
87}
88
89impl Mirror3D for Geometry3D {}