Skip to main content

geometry_model/
macros.rs

1//! Declarative literal macros for `model::Point`, `model::Linestring`,
2//! and `model::Polygon`.
3//!
4//! These mirror the literal-style polygon construction shown in
5//! `specs/rust-port-proposal.md` §5 — they give callers a compact way
6//! to write geometry values from nested tuple syntax, without standing
7//! up a builder type.
8//!
9//! Boost.Geometry has no direct equivalent (C++ initialiser-list syntax
10//! covers that role), so the surface is Rust-specific ergonomics over
11//! the existing model types.
12//!
13//! All three macros are `#[macro_export]`ed at the crate root.
14
15/// Construct a [`crate::Point2D`] or [`crate::Point3D`]
16/// from a tuple literal.
17///
18/// The `(x, y)` form expands to `Point2D::new(x, y)`; the `(x, y, z)`
19/// form expands to `Point3D::new(x, y, z)`. The coordinate type and
20/// coordinate system are inferred from context, so a binding annotated
21/// `Point2D<f64, Cartesian>` will pick up `f64` for the expressions.
22///
23/// # Examples
24///
25/// ```
26/// use geometry_cs::Cartesian;
27/// use geometry_model::{point, Point2D, Point3D};
28/// use geometry_trait::Point as _;
29///
30/// let p: Point2D<f64, Cartesian> = point!((1.0, 2.0));
31/// assert_eq!(p.get::<0>(), 1.0);
32///
33/// let q: Point3D<f64, Cartesian> = point!((1.0, 2.0, 3.0));
34/// assert_eq!(q.get::<2>(), 3.0);
35/// ```
36#[macro_export]
37macro_rules! point {
38    (($x:expr, $y:expr)) => {
39        $crate::Point2D::new($x, $y)
40    };
41    (($x:expr, $y:expr, $z:expr)) => {
42        $crate::Point3D::new($x, $y, $z)
43    };
44}
45
46/// Construct a [`crate::Linestring`] from a comma-separated
47/// list of `(x, y)` tuples.
48///
49/// Each tuple is expanded through [`point!`] and the resulting points
50/// are collected into a `Vec`, then wrapped via
51/// [`Linestring::from_vec`](crate::Linestring::from_vec).
52///
53/// # Examples
54///
55/// ```
56/// use geometry_cs::Cartesian;
57/// use geometry_model::{linestring, Linestring, Point2D};
58/// use geometry_trait::Linestring as _;
59///
60/// let ls: Linestring<Point2D<f64, Cartesian>> =
61///     linestring![(0.0, 0.0), (1.0, 1.0), (2.0, 0.0)];
62/// assert_eq!(ls.points().count(), 3);
63/// ```
64#[macro_export]
65macro_rules! linestring {
66    [ $( ( $($pt:expr),+ $(,)? ) ),* $(,)? ] => {{
67        let mut __ls = $crate::Linestring::new();
68        $( __ls.push($crate::point!(( $($pt),+ ))); )*
69        __ls
70    }};
71}
72
73/// Construct a [`crate::Polygon`] from a bracketed list whose
74/// first element is the outer ring and the rest are holes (interior
75/// rings).
76///
77/// Each ring is written as a bracketed, comma-separated list of `(x, y)`
78/// tuples; tuples expand through [`point!`].
79///
80/// # Examples
81///
82/// ```
83/// use geometry_cs::Cartesian;
84/// use geometry_model::{polygon, Point2D, Polygon};
85/// use geometry_trait::{Polygon as _, Ring as _};
86///
87/// let p: Polygon<Point2D<f64, Cartesian>> = polygon![
88///     [(0.0, 0.0), (4.0, 0.0), (4.0, 3.0), (0.0, 3.0), (0.0, 0.0)]
89/// ];
90/// assert_eq!(p.exterior().points().count(), 5);
91/// assert_eq!(p.interiors().count(), 0);
92/// ```
93///
94/// ## Polygons with holes
95///
96/// The outer ring comes first, then zero or more inner rings (holes)
97/// each as their own bracketed list:
98///
99/// ```
100/// use geometry_cs::Cartesian;
101/// use geometry_model::{polygon, Point2D, Polygon};
102/// use geometry_trait::{Polygon as _, Ring as _};
103///
104/// let p: Polygon<Point2D<f64, Cartesian>> = polygon![
105///     // Outer ring: a 5×5 square.
106///     [(0.0, 0.0), (5.0, 0.0), (5.0, 5.0), (0.0, 5.0), (0.0, 0.0)],
107///     // Hole: a 1×1 square inside it.
108///     [(1.0, 1.0), (2.0, 1.0), (2.0, 2.0), (1.0, 2.0), (1.0, 1.0)],
109/// ];
110/// assert_eq!(p.exterior().points().count(), 5);
111/// assert_eq!(p.interiors().count(), 1);
112/// assert_eq!(p.interiors().next().unwrap().points().count(), 5);
113/// ```
114///
115/// Mirrors the C++ "polygon with hole" example in
116/// `boost/geometry/doc/src/examples/quick_start.cpp` lines 121-129,
117/// rebuilt against the Rust [`crate::Polygon`] constructor.
118#[macro_export]
119macro_rules! polygon {
120    [ [ $( ( $($outer_pt:expr),+ $(,)? ) ),* $(,)? ]
121      $(, [ $( ( $($inner_pt:expr),+ $(,)? ) ),* $(,)? ] )*
122      $(,)?
123    ] => {{
124        let mut __outer = $crate::Ring::new();
125        $( __outer.push($crate::point!(( $($outer_pt),+ ))); )*
126        let mut __poly = $crate::Polygon::new(__outer);
127        $(
128            {
129                let mut __hole = $crate::Ring::new();
130                $( __hole.push($crate::point!(( $($inner_pt),+ ))); )*
131                __poly.inners.push(__hole);
132            }
133        )*
134        __poly
135    }};
136}