iron_shapes/
concepts.rs

1// SPDX-FileCopyrightText: 2022 Thomas Kramer
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5//! Abstraction concepts over geometric primitives.
6//!
7//! # References
8//! * Inspired by [the Boost polygon library](https://www.boost.org/doc/libs/1_78_0/libs/polygon/doc/gtl_design_overview.htm),
9//! [archived](https://web.archive.org/web/20220524201016/https://www.boost.org/doc/libs/1_78_0/libs/polygon/doc/gtl_design_overview.htm)
10//! * and ["Geometry Template Library for STL-like 2D Operations"](https://www.boost.org/doc/libs/1_44_0/libs/polygon/doc/GTL_boostcon2009.pdf),
11//! [archived](https://web.archive.org/web/20220524201159/https://www.boost.org/doc/libs/1_44_0/libs/polygon/doc/GTL_boostcon2009.pdf)
12
13use crate::isotropy::*;
14use num_traits::{Float, Num, Signed};
15
16/// Define a type used for coordinates.
17pub trait CoordinateBase {
18    /// Base coordinate type used for x and y coordinates.
19    type Coord: Num + Signed + Copy + PartialEq + PartialOrd;
20}
21
22impl<T> CoordinateBase for T
23where
24    T: Num + Signed + Copy + PartialEq + PartialOrd,
25{
26    type Coord = T;
27}
28
29/// Define the coordinate concept.
30/// This will be used to parametrize all other geometric concepts.
31pub trait CoordinateConcept: CoordinateBase {
32    /// Type used for area. This is typically a floating point number or a rational number.
33    type Area: Num + Signed + Copy + PartialEq + PartialOrd;
34    /// Type used for area which can be expressed without fractions. The datatype usually has a bigger range than `Coord` to avoid overflows during multiplications.
35    /// For example when using `i32` as `Coord`, a `i64` is recommended as area type.
36    type ManhattanArea: Num + Copy + PartialEq + PartialOrd;
37    /// Type for unsigned area.
38    type UnsignedArea: Num + Copy + PartialEq + PartialOrd;
39    /// Type for difference between coordinates. Typically the same type as `Coord` when `Coord` is signed.
40    type CoordinateDifference: Num + Signed + Copy + PartialEq + From<Self::Coord> + PartialOrd;
41    /// Type for distances. Typically a floating point type because distances cannot be represented in integers nor rationals.
42    type CoordinateDistance: Num
43        + Copy
44        + PartialEq
45        + From<Self::CoordinateDifference>
46        + Float
47        + PartialOrd;
48}
49
50impl CoordinateConcept for i32 {
51    type Area = f64;
52    type ManhattanArea = i64;
53    type UnsignedArea = f64;
54    type CoordinateDifference = i32;
55    type CoordinateDistance = f64;
56}
57
58impl CoordinateConcept for f32 {
59    type Area = f32;
60    type ManhattanArea = f32;
61    type UnsignedArea = f32;
62    type CoordinateDifference = f32;
63    type CoordinateDistance = f32;
64}
65
66impl CoordinateConcept for f64 {
67    type Area = f64;
68    type ManhattanArea = f64;
69    type UnsignedArea = f64;
70    type CoordinateDifference = f64;
71    type CoordinateDistance = f64;
72}
73
74// macro_rules! crd {
75//  ($t:ty) => {<<Self as $t>::Coord as CoordinateConcept>}
76// }
77
78/// Basic traits to get and set Kartesian coordinates of a point in the two-dimensional plane.
79pub trait PointBase<C: CoordinateBase> {
80    /// Construct a new point.
81    fn new(x: C::Coord, y: C::Coord) -> Self;
82
83    /// Get a coordinate value.
84    fn get(&self, orient: Orientation2D) -> C::Coord;
85
86    /// Set a coordinate value.
87    fn set(&mut self, orient: Orientation2D, value: C::Coord);
88
89    /// Get the x-coordinate value.
90    fn x(&self) -> C::Coord {
91        self.get(Orientation2D::Horizontal)
92    }
93
94    /// Get the y-coordinate value.
95    fn y(&self) -> C::Coord {
96        self.get(Orientation2D::Vertical)
97    }
98}
99
100/// Concept of a point in the Euclidean plane.
101pub trait PointConcept<C: CoordinateConcept>: PointBase<C> {
102    /// Compute the x or y component of the vector from the point to the `other` point.
103    fn projected_distance(&self, other: &Self, orient: Orientation2D) -> C::CoordinateDifference {
104        let a: C::CoordinateDifference = self.get(orient).into();
105        let b: C::CoordinateDifference = other.get(orient).into();
106        b - a
107    }
108
109    /// Compute the 1-norm of the vector pointing from the point to the other.
110    fn manhattan_distance(&self, other: &Self) -> C::CoordinateDifference {
111        self.projected_distance(other, Orientation2D::Horizontal)
112            + self.projected_distance(other, Orientation2D::Vertical)
113    }
114
115    /// Squared euclidean distance.
116    fn distance_squared(&self, other: &Self) -> C::CoordinateDistance {
117        let d_horiz: C::CoordinateDistance = self
118            .projected_distance(other, Orientation2D::Horizontal)
119            .into();
120        let d_vert: C::CoordinateDistance = self
121            .projected_distance(other, Orientation2D::Vertical)
122            .into();
123
124        d_horiz * d_horiz + d_vert * d_vert
125    }
126
127    /// Euclidean distance, i.e. 2-norm of the vector from the point to the other.
128    fn euclidian_distance(&self, other: &Self) -> C::CoordinateDistance {
129        self.distance_squared(other).sqrt()
130    }
131}
132
133/// A polygon edge.
134pub trait Segment<C: CoordinateConcept> {
135    /// Type used to represent the end points of the segment.
136    type Point: PointConcept<C>;
137
138    /// Get the start (LOW) or end (HIGH) point of the segment.
139    fn get_point(&self, dir: Direction1D) -> Self::Point;
140
141    /// Shortcut to get the 'low' end of the segment.
142    fn start(&self) -> Self::Point {
143        self.get_point(Direction1D::Low)
144    }
145
146    /// Shortcut to get the 'high' end of the segment.
147    fn end(&self) -> Self::Point {
148        self.get_point(Direction1D::High)
149    }
150}
151
152/// Convert a polygon into an iterator over polygon segments.
153pub trait IntoSegments<C: CoordinateConcept> {
154    /// Type which represents the segments.
155    type Segment: Segment<C>;
156    /// Iterator over segments.
157    type SegmentIter: Iterator<Item = Self::Segment>;
158
159    /// Iterate over segments/edges of a polygon.
160    fn into_segments(self) -> Self::SegmentIter;
161}
162
163/// Loop over points/vertices.
164pub trait IntoPoints<C: CoordinateConcept> {
165    /// Type of the points.
166    type Point: PointConcept<C>;
167    /// Iterator over points.
168    type PointIter: Iterator<Item = Self::Point>;
169
170    /// Iterate over points.
171    fn into_points(self) -> Self::PointIter;
172}
173
174/// Describe an interval of coordinates by a start value and an end value.
175pub trait Interval<Coord>
176where
177    Coord: Copy,
178{
179    /// Get the low or high end.
180    fn get(&self, d: Direction1D) -> Coord;
181
182    /// Get the low end.
183    fn start(&self) -> Coord {
184        self.get(Direction1D::Low)
185    }
186
187    /// Get the high end.
188    fn end(&self) -> Coord {
189        self.get(Direction1D::High)
190    }
191}
192
193/// Concept of an axis-aligned rectangle.
194pub trait Rectangle<C: CoordinateConcept>: Polygon90<C> {
195    /// Type used for representing a one-dimensional interval.
196    type Interval: Interval<C::Coord>;
197
198    /// Get the interval which is spanned by the rectangle in the given orientation.
199    fn get(&self, orientation: Orientation2D) -> Self::Interval;
200
201    // fn lower_left(&self) -> <Self as PolygonSet>::Point;
202    // fn upper_right(&self) -> <Self as PolygonSet>::Point;
203    //
204    // fn lower_right(&self) -> <Self as PolygonSet>::Point {
205    //     let ll = self.lower_left();
206    //     let ur = self.upper_right();
207    //
208    //     <Self as PolygonSet>::Point::new(ur.x(), ll.y())
209    // }
210}
211
212/// Concept of a polygon with axis-aligned edges. The polygon consists of a single closed loop of vertices, i.e. has no holes.
213pub trait Polygon90<C: CoordinateConcept>: Polygon<C> + Polygon90WithHoles<C> {
214    /// Iterator over alternating x/y coordinates of the points. Starts with an x coordinate.
215    type CompactIterator: Iterator<Item = C::Coord>;
216
217    /// Iterate over alternating x/y coordinates of the polygon vertices. Start with an x coordinate.
218    fn compact_iter(&self) -> Self::CompactIterator;
219
220    // /// Set the points from a compact iterator.
221    // fn set_from_compact(&mut self, iter: Self::CompactIterator) {
222    //     self.set(iter);
223    //     todo!();
224    // }
225}
226
227/// Concept of a polygon which consists of a single closed loop of vertices.
228pub trait Polygon<C: CoordinateConcept>: PolygonWithHoles<C> + IntoPoints<C> {
229    /// Set points from an iterator.
230    fn set(&mut self, iter: impl Iterator<Item = <Self as PolygonSet<C>>::Point>);
231}
232
233/// Concept of a polygon with axis-aligned edges and holes.
234pub trait Polygon90WithHoles<C: CoordinateConcept>: PolygonWithHoles<C> + Polygon90Set<C> {}
235
236/// Concept of a polygon with holes.
237pub trait PolygonWithHoles<C: CoordinateConcept>: PolygonSet<C> {
238    /// Get the number of holes.
239    fn num_holes(&self) -> usize;
240}
241
242/// Set of multiple polygons with axis-aligned edges.
243pub trait Polygon90Set<C: CoordinateConcept>: PolygonSet<C> {}
244
245/// Set of multiple polygons edges.
246pub trait PolygonSet<C: CoordinateConcept>: IntoSegments<C> {
247    /// Point type used for the vertices.
248    type Point: PointConcept<C>;
249    /// Type used for the polygon segments.
250    type Segment: Segment<C, Point = Self::Point>;
251    // type Polygon: PolygonWithHoles;
252    // type PolygonIter: Iterator<Item=Self::Polygon>;
253    /// Iterator over all points.
254    type AllPoints: Iterator<Item = Self::Point>;
255
256    /// Get number of polygons.
257    fn num_polygons(&self) -> usize;
258
259    /// Add the point `p` to all vertices.
260    fn convolved(self, p: &Self::Point) -> Self;
261
262    /// Add the point `p` to all vertices.
263    fn convolve(&mut self, p: &Self::Point);
264
265    /// Multiply all vertices with a factor.
266    fn scaled(self, scale: C::Coord) -> Self;
267
268    /// Multiply all vertices with a factor.
269    fn scale(&mut self, scale: C::Coord);
270
271    /// Iterate over all vertices.
272    fn all_points(&self) -> Self::AllPoints;
273}