Skip to main content

geometry_model/
pointing_segment.rs

1//! Borrowing segment model whose indexed access forwards to two points.
2//!
3//! Mirrors `boost::geometry::model::pointing_segment<Point>` from
4//! `geometries/pointing_segment.hpp:39-72` and its indexed-access
5//! specializations at lines 91-137.
6
7use geometry_tag::SegmentTag;
8use geometry_trait::{Geometry, IndexedAccess, PointMut, Segment};
9
10/// A segment borrowing two mutable endpoints instead of storing copies.
11///
12/// Mirrors `model::pointing_segment` from
13/// `geometries/pointing_segment.hpp:39-72`. Boost stores nullable raw
14/// pointers because its segment iterator requires default construction. Rust
15/// represents only the valid, non-null state and uses the borrow checker to
16/// prevent the endpoints from outliving their source.
17#[derive(Debug)]
18pub struct PointingSegment<'a, P: PointMut> {
19    start: &'a mut P,
20    end: &'a mut P,
21}
22
23impl<'a, P: PointMut> PointingSegment<'a, P> {
24    /// Borrow two endpoints as a segment.
25    ///
26    /// Mirrors `pointing_segment(p1, p2)` from
27    /// `geometries/pointing_segment.hpp:67-71` while eliminating the C++
28    /// model's nullable default state.
29    #[inline]
30    #[must_use]
31    pub const fn new(start: &'a mut P, end: &'a mut P) -> Self {
32        Self { start, end }
33    }
34
35    /// Borrow the first endpoint.
36    #[inline]
37    #[must_use]
38    pub const fn start(&self) -> &P {
39        self.start
40    }
41
42    /// Borrow the second endpoint.
43    #[inline]
44    #[must_use]
45    pub const fn end(&self) -> &P {
46        self.end
47    }
48}
49
50impl<P: PointMut> Geometry for PointingSegment<'_, P> {
51    type Kind = SegmentTag;
52    type Point = P;
53}
54
55impl<P: PointMut> IndexedAccess for PointingSegment<'_, P> {
56    #[inline]
57    fn get_indexed<const I: usize, const D: usize>(&self) -> P::Scalar {
58        match I {
59            0 => self.start.get::<D>(),
60            1 => self.end.get::<D>(),
61            _ => panic!("PointingSegment::get_indexed: endpoint index {I} is out of range"),
62        }
63    }
64
65    #[inline]
66    fn set_indexed<const I: usize, const D: usize>(&mut self, value: P::Scalar) {
67        match I {
68            0 => self.start.set::<D>(value),
69            1 => self.end.set::<D>(value),
70            _ => panic!("PointingSegment::set_indexed: endpoint index {I} is out of range"),
71        }
72    }
73}
74
75impl<P: PointMut> Segment for PointingSegment<'_, P> {}
76
77#[cfg(test)]
78mod tests {
79    //! An endpoint index past the second must fail loudly: the two-way
80    //! branch form silently aliased every out-of-range `I` onto `end`.
81
82    use geometry_cs::Cartesian;
83    use geometry_trait::IndexedAccess as _;
84
85    use super::PointingSegment;
86    use crate::Point2D;
87
88    type P = Point2D<f64, Cartesian>;
89
90    #[test]
91    #[should_panic(expected = "endpoint index 2 is out of range")]
92    fn reading_a_third_endpoint_panics_instead_of_aliasing_the_end() {
93        let mut start = P::new(1.0, 2.0);
94        let mut end = P::new(3.0, 4.0);
95        let segment = PointingSegment::new(&mut start, &mut end);
96        let _ = segment.get_indexed::<2, 0>();
97    }
98
99    #[test]
100    #[should_panic(expected = "endpoint index 2 is out of range")]
101    fn writing_a_third_endpoint_panics_instead_of_aliasing_the_end() {
102        let mut start = P::new(1.0, 2.0);
103        let mut end = P::new(3.0, 4.0);
104        let mut segment = PointingSegment::new(&mut start, &mut end);
105        segment.set_indexed::<2, 0>(9.0);
106    }
107}