Skip to main content

geometry_model/
linestring.rs

1//! Default `model::Linestring<P>` — an ordered sequence of >= 2 points
2//! stored in a `Vec<P>`.
3//!
4//! Mirrors `boost::geometry::model::linestring<Point, Container,
5//! Allocator>` declared in `boost/geometry/geometries/linestring.hpp:54-95`,
6//! together with the trait specialisation the same header provides
7//! under `namespace traits` (`tag`) at
8//! `boost/geometry/geometries/linestring.hpp:103-113`. The Rust port
9//! collapses that specialisation into the two trait impls below
10//! ([`Geometry`], [`LinestringTrait`]), keyed off the generic point
11//! parameter `P`.
12//!
13//! Boost derives `model::linestring` from `std::vector<Point>` so any
14//! `boost::range` algorithm sees the underlying storage directly; the
15//! Rust port stores a `Vec<P>` and surfaces it through the `points()`
16//! iterator the [`LinestringTrait`] concept requires.
17
18use alloc::vec::Vec;
19
20use geometry_tag::LinestringTag;
21use geometry_trait::{Geometry, Linestring as LinestringTrait, Point as PointTrait};
22
23/// Default Linestring — a `Vec<P>` newtype.
24///
25/// Mirrors `boost::geometry::model::linestring<Point>` from
26/// `boost/geometry/geometries/linestring.hpp:54-95`. The
27/// `#[repr(transparent)]` annotation guarantees the in-memory layout
28/// matches the underlying `Vec<P>`, so callers can safely transmute
29/// between the two when interfacing with code that already operates
30/// on `Vec<P>` directly — the same invariant Boost relies on by
31/// inheriting publicly from `std::vector<Point>`.
32#[derive(Debug, Clone, PartialEq)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34#[repr(transparent)]
35pub struct Linestring<P: PointTrait>(pub Vec<P>);
36
37impl<P: PointTrait> Linestring<P> {
38    /// Construct an empty linestring.
39    ///
40    /// Mirrors the default `linestring()` constructor at
41    /// `boost/geometry/geometries/linestring.hpp:68-70`.
42    #[must_use]
43    pub const fn new() -> Self {
44        Self(Vec::new())
45    }
46
47    /// Wrap an existing `Vec<P>` as a linestring without copying.
48    ///
49    /// Rust analogue of constructing `boost::geometry::model::linestring`
50    /// from an iterator range
51    /// (`boost/geometry/geometries/linestring.hpp:73-76`); the `Vec`
52    /// is moved in rather than copied element-by-element.
53    #[must_use]
54    pub const fn from_vec(v: Vec<P>) -> Self {
55        Self(v)
56    }
57
58    /// Append a point to the back of the linestring.
59    ///
60    /// Plays the role of `std::vector::push_back` on the inherited
61    /// `base_type` in `boost/geometry/geometries/linestring.hpp:60-64`.
62    pub fn push(&mut self, p: P) {
63        self.0.push(p);
64    }
65}
66
67impl<P: PointTrait> Default for Linestring<P> {
68    #[inline]
69    fn default() -> Self {
70        Self::new()
71    }
72}
73
74/// Tag this concrete type as a Linestring. Mirrors the
75/// `traits::tag<model::linestring<...>>` specialisation at
76/// `boost/geometry/geometries/linestring.hpp:103-113`.
77impl<P: PointTrait> Geometry for Linestring<P> {
78    type Kind = LinestringTag;
79    type Point = P;
80}
81
82/// Wire the Linestring concept up. The `points()` iterator hands back
83/// `self.0.iter()` — `slice::Iter` is already
84/// `ExactSizeIterator + Clone`, so no adapter is needed. Plays the
85/// role of `boost::begin(ls)` / `boost::end(ls)` on
86/// `model::linestring` (`boost/geometry/geometries/linestring.hpp:60-64`).
87impl<P: PointTrait> LinestringTrait for Linestring<P> {
88    fn points(&self) -> impl ExactSizeIterator<Item = &P> + Clone {
89        self.0.iter()
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    //! Iteration + tag-resolution + `check_linestring` witness for
96    //! [`Linestring`]. Mirrors
97    //! `boost/geometry/test/core/tag.cpp` (the linestring-tag arm)
98    //! and the iteration smoke tests in
99    //! `boost/geometry/test/geometries/linestring.cpp`.
100
101    use super::Linestring;
102    use crate::point::Point2D;
103    use alloc::vec;
104    use alloc::vec::Vec;
105    use geometry_cs::Cartesian;
106    use geometry_tag::LinestringTag;
107    use geometry_trait::{Geometry, Linestring as LinestringTrait, Point as _, check_linestring};
108
109    #[test]
110    fn linestring_iterates_in_declared_order() {
111        let mut ls = Linestring::<Point2D<f64, Cartesian>>::new();
112        ls.push(Point2D::new(0.0, 0.0));
113        ls.push(Point2D::new(1.0, 1.0));
114        ls.push(Point2D::new(2.0, 0.0));
115        assert_eq!(ls.points().count(), 3);
116        let xs: Vec<u64> = ls.points().map(|p| p.get::<0>().to_bits()).collect();
117        assert_eq!(
118            xs,
119            vec![0.0_f64.to_bits(), 1.0_f64.to_bits(), 2.0_f64.to_bits()]
120        );
121    }
122
123    #[test]
124    fn linestring_iterator_reports_exact_size() {
125        let ls = Linestring::<Point2D<f64, Cartesian>>::from_vec(vec![
126            Point2D::new(0.0, 0.0),
127            Point2D::new(1.0, 1.0),
128        ]);
129        let it = ls.points();
130        assert_eq!(it.len(), 2);
131    }
132
133    #[test]
134    fn linestring_kind_is_linestring_tag() {
135        fn k<T: Geometry<Kind = LinestringTag>>() {}
136        k::<Linestring<Point2D<f64, Cartesian>>>();
137    }
138
139    #[test]
140    fn linestring_satisfies_concept() {
141        check_linestring::<Linestring<Point2D<f64, Cartesian>>>();
142    }
143}