Skip to main content

geometry_algorithm/
expand.rs

1//! Expand a box to include another geometry.
2//!
3//! Mirrors `boost::geometry::expand` from
4//! `boost/geometry/algorithms/expand.hpp` and the component-wise Cartesian
5//! implementation in `algorithms/detail/expand/{point,box}.hpp`.
6
7use geometry_model::Box as ModelBox;
8use geometry_strategy::{EnvelopeStrategy, EnvelopeStrategyForKind};
9use geometry_trait::{Box as BoxTrait, Geometry, IndexedAccess, PointMut, corner, fold_dims};
10
11/// Expand `bounds` so it contains `geometry`'s envelope.
12///
13/// Mirrors `boost::geometry::expand(box, geometry)` from
14/// `algorithms/detail/expand/interface.hpp:115-132`. The geometry kind is
15/// resolved through the same public envelope strategy used by
16/// [`crate::envelope()`], then each minimum/maximum coordinate widens the
17/// caller-supplied box in place.
18///
19/// The current envelope strategy family is Cartesian; spherical and
20/// geographic antimeridian-aware expansion will become available when those
21/// envelope strategies are ported.
22#[inline]
23pub fn expand<B, G, P>(bounds: &mut B, geometry: &G)
24where
25    B: BoxTrait<Point = P>,
26    G: Geometry<Point = P>,
27    P: PointMut + Default,
28    G::Kind: EnvelopeStrategyForKind,
29    <G::Kind as EnvelopeStrategyForKind>::S: EnvelopeStrategy<G, Output = ModelBox<P>> + Default,
30{
31    expand_with(
32        bounds,
33        geometry,
34        <<G::Kind as EnvelopeStrategyForKind>::S as Default>::default(),
35    );
36}
37
38/// Expand `bounds` using an explicitly supplied envelope strategy.
39///
40/// Mirrors the strategy overload of `boost::geometry::expand` from
41/// `algorithms/detail/expand/interface.hpp:135-153`. Returning an envelope
42/// from the strategy keeps the mutation generic over any writable Box concept
43/// implementation.
44#[inline]
45#[allow(
46    clippy::needless_pass_by_value,
47    reason = "envelope strategies are zero-sized or small Copy values, matching the crate's other _with entry points"
48)]
49pub fn expand_with<B, G, P, S>(bounds: &mut B, geometry: &G, strategy: S)
50where
51    B: BoxTrait<Point = P>,
52    G: Geometry<Point = P>,
53    P: PointMut + Default,
54    S: EnvelopeStrategy<G, Output = ModelBox<P>>,
55{
56    let incoming = strategy.envelope(geometry);
57
58    fold_dims((), incoming.min(), |(), _, dimension| match dimension {
59        0 => expand_dimension::<B, P, 0>(bounds, &incoming),
60        1 => expand_dimension::<B, P, 1>(bounds, &incoming),
61        2 => expand_dimension::<B, P, 2>(bounds, &incoming),
62        3 => expand_dimension::<B, P, 3>(bounds, &incoming),
63        _ => unreachable!("fold_dims limits point dimensions to MAX_DIM"),
64    });
65}
66
67#[inline]
68fn expand_dimension<B, P, const D: usize>(bounds: &mut B, incoming: &ModelBox<P>)
69where
70    B: BoxTrait<Point = P>,
71    P: PointMut,
72{
73    let incoming_min = incoming.get_indexed::<{ corner::MIN }, D>();
74    let incoming_max = incoming.get_indexed::<{ corner::MAX }, D>();
75    let current_min = bounds.get_indexed::<{ corner::MIN }, D>();
76    let current_max = bounds.get_indexed::<{ corner::MAX }, D>();
77
78    if incoming_min < current_min {
79        bounds.set_indexed::<{ corner::MIN }, D>(incoming_min);
80    }
81    if incoming_max > current_max {
82        bounds.set_indexed::<{ corner::MAX }, D>(incoming_max);
83    }
84}