Skip to main content

geometry_algorithm/
equals.rs

1//! `equals(&a, &b)` — see
2//! `boost/geometry/algorithms/equals.hpp`.
3//!
4//! Cartesian-only in v1. The per-pair strategy is selected by the
5//! tag-keyed [`geometry_strategy::EqualsPairStrategy`] picker on
6//! `(A::Kind, B::Kind)`, so any concept-adapted foreign type resolves
7//! through the same path as the equivalent `geometry-model` value.
8
9use geometry_strategy::{EqualsPairStrategy, EqualsStrategy};
10use geometry_trait::Geometry;
11
12/// `true` iff `a` and `b` describe the same point set.
13///
14/// Mirrors `boost::geometry::equals(a, b)` from
15/// `boost/geometry/algorithms/equals.hpp`. Polygon equality is
16/// up-to-rotation and traversal direction; vertex-order normalisation
17/// happens inside the strategy kernel.
18#[inline]
19#[must_use]
20pub fn equals<A, B>(a: &A, b: &B) -> bool
21where
22    A: Geometry,
23    B: Geometry,
24    A::Kind: EqualsPairStrategy<B::Kind>,
25    <A::Kind as EqualsPairStrategy<B::Kind>>::S: EqualsStrategy<A, B>,
26{
27    <<A::Kind as EqualsPairStrategy<B::Kind>>::S as Default>::default().equals(a, b)
28}
29
30#[cfg(test)]
31mod tests {
32    use super::equals;
33    use geometry_cs::Cartesian;
34    use geometry_model::{Point2D, Polygon, polygon};
35
36    type P = Point2D<f64, Cartesian>;
37
38    fn pt(x: f64, y: f64) -> P {
39        Point2D::new(x, y)
40    }
41
42    #[test]
43    fn equals_same_point() {
44        assert!(equals(&pt(1.0, 2.0), &pt(1.0, 2.0)));
45        assert!(!equals(&pt(1.0, 2.0), &pt(1.0, 2.1)));
46    }
47
48    #[test]
49    fn equals_polygon_rotated_and_reversed() {
50        let a: Polygon<P> = polygon![[(0.0, 0.0), (4.0, 0.0), (4.0, 4.0), (0.0, 4.0), (0.0, 0.0)]];
51        let b: Polygon<P> = polygon![[(4.0, 4.0), (0.0, 4.0), (0.0, 0.0), (4.0, 0.0), (4.0, 4.0)]];
52        assert!(equals(&a, &b));
53    }
54
55    /// Point equality compares all three ordinates in 3D — the `2 =>`
56    /// arm. Points equal in x,y but differing in z are not equal.
57    #[test]
58    fn equals_point_in_three_dimensions() {
59        use geometry_model::Point3D;
60        type P3 = Point3D<f64, Cartesian>;
61        let a = P3::new(1.0, 2.0, 3.0);
62        assert!(equals(&a, &P3::new(1.0, 2.0, 3.0)));
63        assert!(!equals(&a, &P3::new(1.0, 2.0, 9.0)));
64    }
65
66    /// Two polygons with matching holes (given in a different order) are
67    /// equal — the interior-ring permutation match with a `break`.
68    #[test]
69    fn equals_polygon_with_holes_matched_by_permutation() {
70        let a: Polygon<P> = polygon![
71            [
72                (0.0, 0.0),
73                (10.0, 0.0),
74                (10.0, 10.0),
75                (0.0, 10.0),
76                (0.0, 0.0)
77            ],
78            [(1.0, 1.0), (2.0, 1.0), (2.0, 2.0), (1.0, 1.0)],
79            [(5.0, 5.0), (6.0, 5.0), (6.0, 6.0), (5.0, 5.0)]
80        ];
81        // Same polygon, holes listed in the opposite order.
82        let b: Polygon<P> = polygon![
83            [
84                (0.0, 0.0),
85                (10.0, 0.0),
86                (10.0, 10.0),
87                (0.0, 10.0),
88                (0.0, 0.0)
89            ],
90            [(5.0, 5.0), (6.0, 5.0), (6.0, 6.0), (5.0, 5.0)],
91            [(1.0, 1.0), (2.0, 1.0), (2.0, 2.0), (1.0, 1.0)]
92        ];
93        assert!(equals(&a, &b));
94    }
95
96    /// Polygons whose exteriors match but whose hole *counts* differ are
97    /// not equal (the count-mismatch short-circuit).
98    #[test]
99    fn polygon_not_equals_on_different_hole_count() {
100        let a: Polygon<P> = polygon![
101            [
102                (0.0, 0.0),
103                (10.0, 0.0),
104                (10.0, 10.0),
105                (0.0, 10.0),
106                (0.0, 0.0)
107            ],
108            [(1.0, 1.0), (2.0, 1.0), (2.0, 2.0), (1.0, 1.0)]
109        ];
110        let b: Polygon<P> = polygon![[
111            (0.0, 0.0),
112            (10.0, 0.0),
113            (10.0, 10.0),
114            (0.0, 10.0),
115            (0.0, 0.0)
116        ]];
117        assert!(!equals(&a, &b));
118    }
119
120    /// Polygons with the same hole count but a hole that has no match are
121    /// not equal (the `if !found { return false }` arm).
122    #[test]
123    fn polygon_not_equals_when_a_hole_has_no_match() {
124        let a: Polygon<P> = polygon![
125            [
126                (0.0, 0.0),
127                (10.0, 0.0),
128                (10.0, 10.0),
129                (0.0, 10.0),
130                (0.0, 0.0)
131            ],
132            [(1.0, 1.0), (2.0, 1.0), (2.0, 2.0), (1.0, 1.0)]
133        ];
134        let b: Polygon<P> = polygon![
135            [
136                (0.0, 0.0),
137                (10.0, 0.0),
138                (10.0, 10.0),
139                (0.0, 10.0),
140                (0.0, 0.0)
141            ],
142            [(7.0, 7.0), (8.0, 7.0), (8.0, 8.0), (7.0, 7.0)]
143        ];
144        assert!(!equals(&a, &b));
145    }
146
147    /// Polygons whose exterior rings differ in vertex count are not equal
148    /// (the `av.len() != bv.len()` short-circuit in `rings_equal`).
149    #[test]
150    fn polygon_not_equals_on_different_vertex_count() {
151        let a: Polygon<P> = polygon![[(0.0, 0.0), (4.0, 0.0), (4.0, 4.0), (0.0, 0.0)]]; // triangle
152        let b: Polygon<P> = polygon![[(0.0, 0.0), (4.0, 0.0), (4.0, 4.0), (0.0, 4.0), (0.0, 0.0)]]; // square
153        assert!(!equals(&a, &b));
154    }
155}