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}