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}