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}