use core::ops::{Add, AddAssign};
use num_traits::float::FloatCore;
use vqm::{Matrix2x2, Vector2};
pub type Anchor2df32 = Anchor2d<f32>;
pub type Anchor2df64 = Anchor2d<f64>;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Anchor2d<T> {
pub pos: Vector2<T>,
pub distance: T,
}
impl<T> Default for Anchor2d<T>
where
T: FloatCore,
{
fn default() -> Self {
Self::new(T::zero(), T::zero())
}
}
impl<T> Anchor2d<T>
where
T: FloatCore,
{
#[must_use]
pub fn new(x: T, y: T) -> Self {
Anchor2d { pos: Vector2 { x, y }, distance: T::zero() }
}
}
#[must_use]
pub fn trilaterate_2d<T>(anchors: &[Anchor2d<T>]) -> Option<Vector2<T>>
where
T: Default + FloatCore + Add<T, Output = T> + AddAssign + vqm::Matrix2x2Math,
{
if anchors.len() < 3 {
return None;
}
let mut base_idx = 0;
let mut min_distance = anchors[0].distance;
for (ii, anchor) in anchors.iter().enumerate().skip(1) {
if anchor.distance < min_distance {
min_distance = anchor.distance;
base_idx = ii;
}
}
let base = anchors[base_idx];
let base_norm_squared = base.pos.norm_squared();
let base_distance_squared = base.distance * base.distance;
let mut ata_00 = T::zero();
let mut ata_01 = T::zero();
let mut ata_11 = T::zero();
let mut atb = Vector2::default();
for (ii, anchor) in anchors.iter().enumerate() {
if ii == base_idx {
continue;
}
let a_row = (base.pos - anchor.pos) * (T::one() + T::one());
ata_00 += a_row.x * a_row.x;
ata_01 += a_row.x * a_row.y;
ata_11 += a_row.y * a_row.y;
let b_row = (anchor.distance * anchor.distance - base_distance_squared)
- (anchor.pos.norm_squared() - base_norm_squared);
atb += a_row * b_row;
}
let m = Matrix2x2::new([
ata_00, ata_01, ata_01, ata_11, ]);
let (adjugate, determinant) = m.adjugate();
if determinant.abs() < T::min_positive_value() { None } else { Some((adjugate * atb) / determinant) }
}
#[cfg(test)]
mod tests {
use super::*;
fn _is_normal<T: Sized + Send + Sync + Unpin>() {}
fn is_full<T: Sized + Send + Sync + Unpin + Copy + Clone + Default + PartialEq>() {}
#[test]
fn normal_types() {
is_full::<Anchor2d<f32>>();
}
}