Skip to main content

geometry_strategy/
buffer.rs

1//! Composable buffer strategies.
2//!
3//! Mirrors the five strategy roles accepted by Boost.Geometry's buffer
4//! interface: distance, side, join, end, and point. The strategy values are
5//! data-only and live below the overlay engine so algorithms can consume them
6//! without creating a dependency cycle.
7
8use geometry_cs::{CartesianFamily, CoordinateSystem, GeographicFamily, SphericalFamily, Spheroid};
9use geometry_trait::Geometry;
10
11/// Cartesian coordinate strategy bundle for buffer construction.
12///
13/// Mirrors `boost::geometry::strategies::buffer::cartesian<>` from
14/// `strategies/buffer/cartesian.hpp:24-31`.
15#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
16pub struct CartesianBuffer;
17
18/// Spherical coordinate strategy bundle for buffer construction.
19///
20/// Mirrors `boost::geometry::strategies::buffer::spherical` from
21/// `strategies/buffer/spherical.hpp:24-46`. Distances use the same unit as
22/// `radius`. The overlay consumer applies this bundle through a local tangent
23/// projection, an explicitly recorded approximation for local buffers.
24#[derive(Debug, Clone, Copy, PartialEq)]
25pub struct SphericalBuffer {
26    /// Sphere radius in the buffer distance unit.
27    pub radius: f64,
28}
29
30impl SphericalBuffer {
31    /// Unit-sphere strategy matching Boost's default radius.
32    pub const UNIT: Self = Self { radius: 1.0 };
33
34    /// Construct a spherical buffer strategy with an explicit radius.
35    #[must_use]
36    pub const fn new(radius: f64) -> Self {
37        Self { radius }
38    }
39}
40
41impl Default for SphericalBuffer {
42    fn default() -> Self {
43        Self::UNIT
44    }
45}
46
47/// Geographic coordinate strategy bundle for buffer construction.
48///
49/// Mirrors `boost::geometry::strategies::buffer::geographic` from
50/// `strategies/buffer/geographic.hpp:25-48`. The overlay consumer derives the
51/// local meridional and prime-vertical scales from this spheroid before using
52/// its Cartesian offset engine.
53#[derive(Debug, Clone, Copy, PartialEq)]
54pub struct GeographicBuffer {
55    /// Reference spheroid used to convert angular coordinates and metric
56    /// buffer distances.
57    pub spheroid: Spheroid,
58}
59
60impl GeographicBuffer {
61    /// WGS84 geographic buffer strategy.
62    pub const WGS84: Self = Self {
63        spheroid: Spheroid::WGS84,
64    };
65
66    /// Construct a geographic buffer strategy with an explicit spheroid.
67    #[must_use]
68    pub const fn new(spheroid: Spheroid) -> Self {
69        Self { spheroid }
70    }
71}
72
73impl Default for GeographicBuffer {
74    fn default() -> Self {
75        Self::WGS84
76    }
77}
78
79/// Select the default coordinate strategy bundle for a buffer input family.
80///
81/// Mirrors `strategies::buffer::services::default_strategy` from
82/// `strategies/buffer/services.hpp:24-40` and its Cartesian, spherical, and
83/// geographic specializations.
84pub trait DefaultBuffer<Family> {
85    /// Default coordinate strategy for this family.
86    type Strategy: Default;
87}
88
89impl DefaultBuffer<CartesianFamily> for CartesianFamily {
90    type Strategy = CartesianBuffer;
91}
92
93impl DefaultBuffer<SphericalFamily> for SphericalFamily {
94    type Strategy = SphericalBuffer;
95}
96
97impl DefaultBuffer<GeographicFamily> for GeographicFamily {
98    type Strategy = GeographicBuffer;
99}
100
101/// Coordinate strategy selected by the point coordinate-system family of `G`.
102pub type DefaultBufferStrategy<G> = <<<<G as Geometry>::Point as geometry_trait::Point>::Cs as CoordinateSystem>::Family as DefaultBuffer<<<<G as Geometry>::Point as geometry_trait::Point>::Cs as CoordinateSystem>::Family>>::Strategy;
103
104/// Signed offset distance policy.
105///
106/// Mirrors `strategy::buffer::distance_symmetric` and `distance_asymmetric`
107/// from `strategies/agnostic/buffer_distance_symmetric.hpp:46-104` and
108/// `buffer_distance_asymmetric.hpp:45-111`.
109#[derive(Debug, Clone, Copy, PartialEq)]
110pub enum BufferDistanceStrategy {
111    /// One signed distance on both sides.
112    Symmetric(f64),
113    /// Independent offsets on the left and right of a linear geometry.
114    Asymmetric {
115        /// Offset on the directed line's left side.
116        left: f64,
117        /// Offset on the directed line's right side.
118        right: f64,
119    },
120}
121
122/// Side-offset construction policy.
123///
124/// Mirrors `strategy::buffer::side_straight` from
125/// `strategies/cartesian/buffer_side_straight.hpp:46-136`.
126#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127pub enum BufferSideStrategy {
128    /// Offset each source segment by its perpendicular normal.
129    Straight,
130}
131
132/// Corner join policy.
133///
134/// Mirrors `strategy::buffer::join_round` and `join_miter` from
135/// `strategies/cartesian/buffer_join_round.hpp:57-164` and
136/// `buffer_join_miter.hpp:52-134`.
137#[derive(Debug, Clone, Copy, PartialEq)]
138pub enum BufferJoinStrategy {
139    /// Circular corner approximation.
140    Round {
141        /// Segment count for a complete circle.
142        points_per_circle: usize,
143    },
144    /// Offset-edge intersection capped to `limit × distance`.
145    Miter {
146        /// Maximum miter ratio.
147        limit: f64,
148    },
149}
150
151/// Linear endpoint policy.
152///
153/// Mirrors `strategy::buffer::end_round` and `end_flat` from
154/// `strategies/cartesian/buffer_end_round.hpp:55-173` and
155/// `buffer_end_flat.hpp:50-100`.
156#[derive(Debug, Clone, Copy, PartialEq)]
157pub enum BufferEndStrategy {
158    /// Semicircular endpoint approximation.
159    Round {
160        /// Segment count for a complete circle.
161        points_per_circle: usize,
162    },
163    /// End the buffer at the source endpoint without extension.
164    Flat,
165}
166
167/// Point-buffer policy.
168///
169/// Mirrors `strategy::buffer::point_circle` and `point_square` from
170/// `strategies/cartesian/buffer_point_circle.hpp:56-109` and
171/// `buffer_point_square.hpp:51-110`.
172#[derive(Debug, Clone, Copy, PartialEq, Eq)]
173pub enum BufferPointStrategy {
174    /// Regular-polygon circle approximation.
175    Circle {
176        /// Vertex count for a complete circle.
177        points_per_circle: usize,
178    },
179    /// Axis-aligned square centered on the point.
180    Square,
181}
182
183/// Complete strategy bundle accepted by `buffer_with`.
184///
185/// Mirrors the five explicit strategy arguments to
186/// `boost::geometry::buffer` from
187/// `algorithms/detail/buffer/interface.hpp:246-273`.
188#[derive(Debug, Clone, Copy, PartialEq)]
189pub struct BufferSettings {
190    /// Signed distance policy.
191    pub distance: BufferDistanceStrategy,
192    /// Side construction policy.
193    pub side: BufferSideStrategy,
194    /// Corner join policy.
195    pub join: BufferJoinStrategy,
196    /// Linear endpoint policy.
197    pub end: BufferEndStrategy,
198    /// Point construction policy.
199    pub point: BufferPointStrategy,
200}
201
202impl BufferSettings {
203    /// Conventional round buffer using one symmetric distance.
204    ///
205    /// Composes Boost's distance-symmetric, side-straight, join-round,
206    /// end-round, and point-circle strategies from the headers cited on the
207    /// corresponding enum types.
208    #[must_use]
209    pub const fn round(distance: f64, points_per_circle: usize) -> Self {
210        Self {
211            distance: BufferDistanceStrategy::Symmetric(distance),
212            side: BufferSideStrategy::Straight,
213            join: BufferJoinStrategy::Round { points_per_circle },
214            end: BufferEndStrategy::Round { points_per_circle },
215            point: BufferPointStrategy::Circle { points_per_circle },
216        }
217    }
218}
219
220impl Default for BufferSettings {
221    fn default() -> Self {
222        Self::round(1.0, 36)
223    }
224}