use geometry_strategy::{CentroidStrategy, CentroidStrategyForKind};
use geometry_trait::Geometry;
#[inline]
#[must_use]
pub fn centroid<G>(
g: &G,
) -> <<G::Kind as CentroidStrategyForKind>::S as CentroidStrategy<G>>::Output
where
G: Geometry,
G::Kind: CentroidStrategyForKind,
<G::Kind as CentroidStrategyForKind>::S: CentroidStrategy<G>,
{
<<G::Kind as CentroidStrategyForKind>::S as Default>::default().centroid(g)
}
#[inline]
#[must_use]
#[allow(
clippy::needless_pass_by_value,
reason = "Strategies are ZST/small Copy configuration objects; by-value matches the user-facing call shape."
)]
pub fn centroid_with<G, S>(g: &G, s: S) -> S::Output
where
G: Geometry,
S: CentroidStrategy<G>,
{
s.centroid(g)
}
#[cfg(test)]
mod tests {
#![allow(
clippy::float_cmp,
reason = "centroids are compared with an explicit absolute tolerance, not `==`"
)]
use super::centroid;
use geometry_cs::Cartesian;
use geometry_model::{Box, MultiPoint, Point2D, Polygon, Ring, Segment, linestring, polygon};
use geometry_trait::Point as _;
type Pt = Point2D<f64, Cartesian>;
fn close(got: Pt, x: f64, y: f64, tol: f64) -> bool {
(got.get::<0>() - x).abs() < tol && (got.get::<1>() - y).abs() < tol
}
#[test]
fn polygon_10x10_square() {
let pg: Polygon<Pt> = polygon![[(0., 0.), (0., 10.), (10., 10.), (10., 0.), (0., 0.)]];
let c = centroid(&pg);
assert!(close(c, 5.0, 5.0, 1e-9));
}
#[test]
fn ring_unit_square_shift() {
let r: Ring<Pt> = Ring::from_vec(vec![
Pt::new(1., 1.),
Pt::new(1., 2.),
Pt::new(2., 2.),
Pt::new(2., 1.),
Pt::new(1., 1.),
]);
let c = centroid(&r);
assert!(close(c, 1.5, 1.5, 1e-9));
}
#[test]
fn linestring_diagonal() {
let ls = linestring![(1., 1.), (2., 2.), (3., 3.)];
let c = centroid(&ls);
assert!(close(c, 2.0, 2.0, 1e-9));
}
#[test]
fn segment_midpoint() {
let s = Segment::new(Pt::new(1., 1.), Pt::new(3., 3.));
let c = centroid(&s);
assert!(close(c, 2.0, 2.0, 1e-12));
}
#[test]
fn box_centroid() {
let b: Box<Pt> = Box::from_corners(Pt::new(1., 2.), Pt::new(3., 4.));
let c = centroid(&b);
assert!(close(c, 2.0, 3.0, 1e-12));
}
#[test]
fn multipoint_mean() {
let mp: MultiPoint<Pt> =
MultiPoint::from_vec(vec![Pt::new(0., 0.), Pt::new(2., 0.), Pt::new(0., 2.)]);
let c = centroid(&mp);
assert!(close(c, 2.0 / 3.0, 2.0 / 3.0, 1e-9));
}
}