use std::ops::{Add, Mul, Sub};
pub(super) trait MatchCoord:
Copy + PartialOrd + Sub<Output = Self> + Mul<Output = Self> + Add<Output = Self>
{
}
impl MatchCoord for f32 {}
impl MatchCoord for f64 {}
#[derive(Default)]
pub(super) struct MatchScratch<F = f64> {
candidates: Vec<(F, usize, usize)>,
used_point: Vec<bool>,
used_pred: Vec<bool>,
matches: Vec<(usize, usize)>,
}
impl<F> MatchScratch<F> {
pub(super) fn take_matches(&mut self) -> Vec<(usize, usize)> {
std::mem::take(&mut self.matches)
}
}
pub(super) fn greedy_unique_matches<'a, F: MatchCoord>(
points: &[(F, F)],
max_points: usize,
predicted: &[(usize, F, F)],
radius_sq: F,
scratch: &'a mut MatchScratch<F>,
) -> &'a [(usize, usize)] {
let n_points = points.len().min(max_points);
let candidates = &mut scratch.candidates;
candidates.clear();
for (pt_idx, &(cx, cy)) in points[..n_points].iter().enumerate() {
for (pred_idx, &(_cat_idx, px, py)) in predicted.iter().enumerate() {
let dx = cx - px;
let dy = cy - py;
let d2 = dx * dx + dy * dy;
if d2 <= radius_sq {
candidates.push((d2, pt_idx, pred_idx));
}
}
}
candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
let used_point = &mut scratch.used_point;
used_point.clear();
used_point.resize(n_points, false);
let used_pred = &mut scratch.used_pred;
used_pred.clear();
used_pred.resize(predicted.len(), false);
let matches = &mut scratch.matches;
matches.clear();
for &(_, pt_idx, pred_idx) in candidates.iter() {
if !used_point[pt_idx] && !used_pred[pred_idx] {
used_point[pt_idx] = true;
used_pred[pred_idx] = true;
matches.push((pt_idx, predicted[pred_idx].0));
}
}
matches
}