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