Skip to main content

geometry_algorithm/
make.rs

1//! Construct a Point / Box / Segment from a coordinate slice.
2//!
3//! Mirrors `boost::geometry::make<P>(values...)` and its
4//! `make<Box>` / `make<Segment>` siblings from
5//! `boost/geometry/algorithms/make.hpp`. The counterpart of
6//! [`crate::assign::assign_values`]: same contract but
7//! returns a fresh value instead of writing into an existing one. The
8//! Boost overloads are variadic; the Rust port takes a slice for
9//! arity-agnostic ergonomics (see [`crate::assign`] for the
10//! trade-off).
11
12use geometry_model::{Box, Segment};
13use geometry_trait::PointMut;
14
15use crate::assign::assign_values;
16
17/// Construct a [`PointMut`] from a slice of coordinates.
18///
19/// Mirrors `boost::geometry::make<Point>(v0, v1, ...)` from
20/// `boost/geometry/algorithms/make.hpp`.
21///
22/// # Panics
23///
24/// Same as [`crate::assign::assign_values`]: panics if
25/// `values.len() < P::DIM`.
26#[must_use]
27pub fn make_point<P>(values: &[P::Scalar]) -> P
28where
29    P: PointMut + Default,
30{
31    let mut p = P::default();
32    assign_values(&mut p, values);
33    p
34}
35
36/// Construct a [`Box`] from two corner coordinate slices.
37///
38/// The `min_coords` slice fills the minimum corner and `max_coords`
39/// the maximum corner. Mirrors `boost::geometry::make<Box>(...)` from
40/// `boost/geometry/algorithms/make.hpp`.
41///
42/// # Panics
43///
44/// Panics if either slice is shorter than `P::DIM` (see
45/// [`make_point`]).
46#[must_use]
47pub fn make_box<P>(min_coords: &[P::Scalar], max_coords: &[P::Scalar]) -> Box<P>
48where
49    P: PointMut + Default,
50{
51    Box::from_corners(make_point(min_coords), make_point(max_coords))
52}
53
54/// Construct a [`Segment`] from two endpoint coordinate slices.
55///
56/// Mirrors `boost::geometry::make<Segment>(...)` from
57/// `boost/geometry/algorithms/make.hpp`.
58///
59/// # Panics
60///
61/// Panics if either slice is shorter than `P::DIM` (see
62/// [`make_point`]).
63#[must_use]
64pub fn make_segment<P>(start_coords: &[P::Scalar], end_coords: &[P::Scalar]) -> Segment<P>
65where
66    P: PointMut + Default,
67{
68    Segment::new(make_point(start_coords), make_point(end_coords))
69}
70
71#[cfg(test)]
72#[allow(
73    clippy::float_cmp,
74    reason = "Constructed coordinates are read back as exact literals."
75)]
76mod tests {
77    //! Reference behaviour from
78    //! `boost/geometry/test/algorithms/make.cpp:39-74` — a slice is
79    //! materialised into a fresh point / box / segment.
80
81    use super::{make_box, make_point, make_segment};
82    use geometry_cs::Cartesian;
83    use geometry_model::{Box, Point2D, Point3D, Segment};
84    use geometry_trait::Point as _;
85
86    type Pt2 = Point2D<f64, Cartesian>;
87    type Pt3 = Point3D<f64, Cartesian>;
88
89    // make.cpp:39-40
90    #[test]
91    fn point2_from_slice() {
92        let p: Pt2 = make_point(&[123.0, 456.0]);
93        assert_eq!(p.get::<0>(), 123.0);
94        assert_eq!(p.get::<1>(), 456.0);
95    }
96
97    // make.cpp:47-49
98    #[test]
99    fn point3_from_slice() {
100        let p: Pt3 = make_point(&[123.0, 456.0, 789.0]);
101        assert_eq!(p.get::<0>(), 123.0);
102        assert_eq!(p.get::<1>(), 456.0);
103        assert_eq!(p.get::<2>(), 789.0);
104    }
105
106    // make.cpp:57-60
107    #[test]
108    fn box_from_corners() {
109        let b: Box<Pt2> = make_box(&[123.0, 456.0], &[789.0, 1011.0]);
110        assert_eq!(b.min().get::<0>(), 123.0);
111        assert_eq!(b.min().get::<1>(), 456.0);
112        assert_eq!(b.max().get::<0>(), 789.0);
113        assert_eq!(b.max().get::<1>(), 1011.0);
114    }
115
116    #[test]
117    fn segment_from_endpoints() {
118        let s: Segment<Pt2> = make_segment(&[0.0, 0.0], &[3.0, 4.0]);
119        assert_eq!(s.start().get::<0>(), 0.0);
120        assert_eq!(s.end().get::<0>(), 3.0);
121    }
122}