use crate::Precision;
use nalgebra::{distance_squared, Point};
fn proj_dist2<const D: usize>(
start: &Point<Precision, D>,
end: &Point<Precision, D>,
p: &Point<Precision, D>,
length_sq: Precision,
) -> Precision {
let lensq_along = (p - start).dot(&(end - start));
let proj;
if lensq_along <= 0.0 {
proj = start;
} else if lensq_along >= length_sq {
proj = end;
} else {
return distance_squared(p, &(start + lensq_along / length_sq * (end - start)));
}
distance_squared(p, proj)
}
fn rdp_keep_inner<const D: usize>(
line: &[Point<Precision, D>],
epsilon_sq: Precision,
offset: usize,
) -> Vec<usize> {
if line.len() <= 2 {
return vec![];
}
let first = line.first().unwrap();
let last = line.last().unwrap();
let length_sq = distance_squared(first, last);
let mut greatest_dist2 = (0, -Precision::INFINITY);
for (idx, point) in line.iter().enumerate().skip(1).take(line.len() - 2) {
let d2 = proj_dist2(first, last, point, length_sq);
if d2 > greatest_dist2.1 {
greatest_dist2 = (idx, d2)
}
}
let mut to_keep = vec![];
if greatest_dist2.1 > epsilon_sq {
if greatest_dist2.0 > 1 {
to_keep.append(&mut rdp_keep_inner(
&line[0..=greatest_dist2.0],
epsilon_sq,
offset,
));
}
to_keep.push(greatest_dist2.0);
if greatest_dist2.0 < line.len() - 2 {
to_keep.append(&mut rdp_keep_inner(
&line[greatest_dist2.0..line.len()],
epsilon_sq,
offset + greatest_dist2.0,
));
}
}
to_keep
}
pub fn rdp_keep<const D: usize>(line: &[Point<Precision, D>], epsilon: Precision) -> Vec<usize> {
let epsilon_sq = epsilon * epsilon;
let mut out = vec![0];
out.append(&mut rdp_keep_inner(line, epsilon_sq, 0));
out.push(line.len() - 1);
out
}
pub fn rdp_reduce<const D: usize>(
line: &[Point<Precision, D>],
epsilon: Precision,
) -> Vec<Point<Precision, D>> {
let kept = rdp_keep(line, epsilon);
kept.into_iter().map(|idx| line[idx]).collect()
}