use geometry_coords::CoordinateScalar;
use geometry_cs::{CartesianFamily, CoordinateSystem};
use geometry_model::{DynGeometry, DynKind, Linestring, Point, Segment};
use geometry_strategy::{
DefaultDistance, DefaultDistanceStrategy, DistanceStrategy, PointToSegment, Pythagoras,
};
use geometry_tag::SameAs;
use geometry_trait::Linestring as LinestringTrait;
use crate::distance::{distance, distance_with};
use crate::dyn_error::DynKindMismatch;
const SUPPORTED: &[&[DynKind]] = &[
&[DynKind::Point, DynKind::Point],
&[DynKind::Point, DynKind::LineString], &[DynKind::LineString, DynKind::Point], ];
#[allow(
clippy::match_same_arms,
reason = "The two point↔linestring arms differ by argument order; keeping them separate documents both directions."
)]
pub fn distance_dyn<S, Cs>(
a: &DynGeometry<S, Cs>,
b: &DynGeometry<S, Cs>,
) -> Result<S, DynKindMismatch>
where
S: CoordinateScalar,
Cs: CoordinateSystem + Copy,
Cs::Family: SameAs<CartesianFamily> + DefaultDistance<Cs::Family>,
DefaultDistanceStrategy<Point<S, 2, Cs>, Point<S, 2, Cs>>:
DistanceStrategy<Point<S, 2, Cs>, Point<S, 2, Cs>, Out = S> + Default,
PointToSegment<Pythagoras>:
DistanceStrategy<Point<S, 2, Cs>, Segment<Point<S, 2, Cs>>, Out = S>,
{
use DynGeometry::{LineString, Point as PointArm};
match (a, b) {
(PointArm(p), PointArm(q)) => Ok(distance(p, q)),
(PointArm(p), LineString(ls)) => Ok(point_to_linestring(p, ls)),
(LineString(ls), PointArm(p)) => Ok(point_to_linestring(p, ls)),
_ => Err(DynKindMismatch {
got: alloc::vec![a.kind(), b.kind()],
expected: SUPPORTED,
}),
}
}
fn point_to_linestring<S, Cs>(p: &Point<S, 2, Cs>, ls: &Linestring<Point<S, 2, Cs>>) -> S
where
S: CoordinateScalar,
Cs: CoordinateSystem + Copy,
Cs::Family: SameAs<CartesianFamily>,
PointToSegment<Pythagoras>:
DistanceStrategy<Point<S, 2, Cs>, Segment<Point<S, 2, Cs>>, Out = S>,
{
let strategy = PointToSegment::<Pythagoras>::default();
let mut best: Option<S> = None;
let mut prev: Option<Point<S, 2, Cs>> = None;
for pt in ls.points() {
if let Some(start) = prev {
let seg = Segment::new(start, *pt);
let d = distance_with(p, &seg, strategy);
best = Some(match best {
Some(m) if m <= d => m,
_ => d,
});
}
prev = Some(*pt);
}
best.unwrap_or_else(|| match ls.points().next() {
Some(v) => distance_with(p, &Segment::new(*v, *v), strategy),
None => S::ZERO,
})
}