microcad_core/geo2d/
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 2D 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 Reflect2D<T = Self> {
13    /// Mirror a 2D geometry.
14    fn reflect_2d(&self, l: &Line) -> T;
15}
16
17/// Mirrors a 2D geometry, keeping the original (in contrast to `reflect`).
18pub trait Mirror2D<T = Self>: Reflect2D<T> + Into<Geometry2D> {
19    /// Mirror operation.
20    fn mirror_2d(self, l: &Line) -> Geometries2D {
21        let orig: Geometry2D = self.into();
22        let refl: Geometry2D = orig.reflect_2d(l);
23        Geometries2D::new(vec![orig, refl])
24    }
25}
26
27impl Reflect2D for Point {
28    fn reflect_2d(&self, l: &Line) -> Self {
29        let d = l.vec();
30        let t = ((self.x() - l.0.x()) * d.x + (self.y() - l.0.y()) * d.y) / d.magnitude();
31        let q = 2.0 * (Vec2::from(l.0.0.x_y()) + t * d);
32        Self::new(q.x - self.x(), q.y - self.y())
33    }
34}
35
36impl Reflect2D for geo::Coord {
37    fn reflect_2d(&self, l: &Line) -> Self {
38        Point::from(self.x_y()).reflect_2d(l).into()
39    }
40}
41
42impl Reflect2D for LineString {
43    fn reflect_2d(&self, l: &Line) -> Self {
44        Self::new(self.0.iter().map(|c| c.reflect_2d(l)).rev().collect())
45    }
46}
47
48impl Mirror2D for LineString {}
49
50impl Reflect2D for MultiLineString {
51    fn reflect_2d(&self, l: &Line) -> Self {
52        Self::new(
53            self.0
54                .iter()
55                .map(|line_string| line_string.reflect_2d(l))
56                .collect(),
57        )
58    }
59}
60
61impl Mirror2D for MultiLineString {}
62
63impl Reflect2D for Polygon {
64    fn reflect_2d(&self, l: &Line) -> Self {
65        Self::new(
66            self.exterior().reflect_2d(l),
67            self.interiors()
68                .iter()
69                .map(|interior| interior.reflect_2d(l))
70                .collect(),
71        )
72    }
73}
74
75impl Mirror2D for Polygon {}
76
77impl Reflect2D for MultiPolygon {
78    fn reflect_2d(&self, l: &Line) -> Self {
79        Self::new(self.0.iter().map(|polygon| polygon.reflect_2d(l)).collect())
80    }
81}
82
83impl Mirror2D for MultiPolygon {}
84
85impl Reflect2D<Polygon> for Rect {
86    fn reflect_2d(&self, l: &Line) -> Polygon {
87        self.to_polygon().reflect_2d(l)
88    }
89}
90
91impl Mirror2D<Polygon> for Rect {}
92
93impl Reflect2D for Line {
94    fn reflect_2d(&self, l: &Line) -> Self {
95        Self(self.1.reflect_2d(l), self.0.reflect_2d(l))
96    }
97}
98
99impl Mirror2D for Line {}
100
101impl Reflect2D for Geometries2D {
102    fn reflect_2d(&self, l: &Line) -> Self {
103        Self::from_iter(
104            self.iter()
105                .map(|geometry| std::rc::Rc::new(geometry.as_ref().reflect_2d(l))),
106        )
107    }
108}
109
110impl Mirror2D for Geometries2D {}
111
112impl Reflect2D for Geometry2D {
113    fn reflect_2d(&self, l: &Line) -> Self {
114        match &self {
115            Geometry2D::LineString(line_string) => line_string.reflect_2d(l).into(),
116            Geometry2D::MultiLineString(multi_line_string) => {
117                multi_line_string.reflect_2d(l).into()
118            }
119            Geometry2D::Polygon(polygon) => polygon.reflect_2d(l).into(),
120            Geometry2D::MultiPolygon(multi_polygon) => multi_polygon.reflect_2d(l).into(),
121            Geometry2D::Rect(rect) => rect.reflect_2d(l).into(),
122            Geometry2D::Line(line) => line.reflect_2d(l).into(),
123            Geometry2D::Collection(collection) => collection.reflect_2d(l).into(),
124        }
125    }
126}
127
128impl Mirror2D for Geometry2D {}