refactor!(argus-core): update find_intersection method
Don't separate the intersection finding code into a different trait, as it can be bundled with interpolation method.
This commit is contained in:
parent
3a9623b99b
commit
475d32c533
5 changed files with 42 additions and 28 deletions
|
|
@ -264,7 +264,7 @@ impl<T> Signal<T> {
|
||||||
pub fn sync_with_intersection<Interp>(&self, other: &Signal<T>) -> Option<Vec<Duration>>
|
pub fn sync_with_intersection<Interp>(&self, other: &Signal<T>) -> Option<Vec<Duration>>
|
||||||
where
|
where
|
||||||
T: PartialOrd + Clone,
|
T: PartialOrd + Clone,
|
||||||
Interp: FindIntersectionMethod<T>,
|
Interp: InterpolationMethod<T>,
|
||||||
{
|
{
|
||||||
use core::cmp::Ordering::*;
|
use core::cmp::Ordering::*;
|
||||||
let sync_points: Vec<&Duration> = self.sync_points(other)?.into_iter().collect();
|
let sync_points: Vec<&Duration> = self.sync_points(other)?.into_iter().collect();
|
||||||
|
|
@ -308,7 +308,8 @@ impl<T> Signal<T> {
|
||||||
.interpolate_at::<Interp>(*t)
|
.interpolate_at::<Interp>(*t)
|
||||||
.map(|value| Sample { time: *t, value }),
|
.map(|value| Sample { time: *t, value }),
|
||||||
};
|
};
|
||||||
let intersect = Interp::find_intersection(&a, &b);
|
let intersect = Interp::find_intersection(&a, &b)
|
||||||
|
.unwrap_or_else(|| panic!("unable to find intersection for crossing signals"));
|
||||||
return_points.push(intersect.time);
|
return_points.push(intersect.time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ use std::cmp::Ordering;
|
||||||
|
|
||||||
use super::interpolation::Linear;
|
use super::interpolation::Linear;
|
||||||
use super::traits::SignalPartialOrd;
|
use super::traits::SignalPartialOrd;
|
||||||
use super::{FindIntersectionMethod, InterpolationMethod, Signal};
|
use super::{InterpolationMethod, Signal};
|
||||||
|
|
||||||
impl<T> SignalPartialOrd for Signal<T>
|
impl<T> SignalPartialOrd for Signal<T>
|
||||||
where
|
where
|
||||||
T: PartialOrd + Clone,
|
T: PartialOrd + Clone,
|
||||||
Linear: InterpolationMethod<T> + FindIntersectionMethod<T>,
|
Linear: InterpolationMethod<T>,
|
||||||
{
|
{
|
||||||
fn signal_cmp<F>(&self, other: &Self, op: F) -> Option<Signal<bool>>
|
fn signal_cmp<F>(&self, other: &Self, op: F) -> Option<Signal<bool>>
|
||||||
where
|
where
|
||||||
|
|
@ -35,7 +35,7 @@ where
|
||||||
impl<T> Signal<T>
|
impl<T> Signal<T>
|
||||||
where
|
where
|
||||||
T: PartialOrd + Clone,
|
T: PartialOrd + Clone,
|
||||||
Linear: InterpolationMethod<T> + FindIntersectionMethod<T>,
|
Linear: InterpolationMethod<T>,
|
||||||
{
|
{
|
||||||
/// Compute the time-wise min of two signals
|
/// Compute the time-wise min of two signals
|
||||||
pub fn min(&self, other: &Self) -> Self {
|
pub fn min(&self, other: &Self) -> Self {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
//! Interpolation methods
|
//! Interpolation methods
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use super::utils::Neighborhood;
|
use super::utils::Neighborhood;
|
||||||
use super::{FindIntersectionMethod, InterpolationMethod, Sample};
|
use super::{InterpolationMethod, Sample};
|
||||||
|
|
||||||
/// Constant interpolation.
|
/// Constant interpolation.
|
||||||
///
|
///
|
||||||
|
|
@ -20,6 +21,11 @@ impl<T: Clone> InterpolationMethod<T> for Constant {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_intersection(_a: &Neighborhood<T>, _b: &Neighborhood<T>) -> Option<Sample<T>> {
|
||||||
|
// The signals must be either constant or colinear. Thus, return None.
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Nearest interpolation.
|
/// Nearest interpolation.
|
||||||
|
|
@ -41,6 +47,12 @@ impl<T: Clone> InterpolationMethod<T> for Nearest {
|
||||||
Some(b.value.clone())
|
Some(b.value.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_intersection(_a: &Neighborhood<T>, _b: &Neighborhood<T>) -> Option<Sample<T>> {
|
||||||
|
// For the same reason as Constant interpolation, the signals must be either parallel or
|
||||||
|
// colinear.
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Linear interpolation.
|
/// Linear interpolation.
|
||||||
|
|
@ -58,10 +70,8 @@ impl InterpolationMethod<bool> for Linear {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl FindIntersectionMethod<bool> for Linear {
|
fn find_intersection(a: &Neighborhood<bool>, b: &Neighborhood<bool>) -> Option<Sample<bool>> {
|
||||||
fn find_intersection(a: &Neighborhood<bool>, b: &Neighborhood<bool>) -> Sample<bool> {
|
|
||||||
let Sample { time: ta1, value: ya1 } = a.first.unwrap();
|
let Sample { time: ta1, value: ya1 } = a.first.unwrap();
|
||||||
let Sample { time: ta2, value: ya2 } = a.second.unwrap();
|
let Sample { time: ta2, value: ya2 } = a.second.unwrap();
|
||||||
let Sample { time: tb1, value: yb1 } = b.first.unwrap();
|
let Sample { time: tb1, value: yb1 } = b.first.unwrap();
|
||||||
|
|
@ -73,27 +83,31 @@ impl FindIntersectionMethod<bool> for Linear {
|
||||||
if left_cmp.is_eq() {
|
if left_cmp.is_eq() {
|
||||||
// They already intersect, so we return the inner time-point
|
// They already intersect, so we return the inner time-point
|
||||||
if ta1 < tb1 {
|
if ta1 < tb1 {
|
||||||
Sample { time: tb1, value: yb1 }
|
Some(Sample { time: tb1, value: yb1 })
|
||||||
} else {
|
} else {
|
||||||
Sample { time: ta1, value: ya1 }
|
Some(Sample { time: ta1, value: ya1 })
|
||||||
}
|
}
|
||||||
} else if right_cmp.is_eq() {
|
} else if right_cmp.is_eq() {
|
||||||
// They intersect at the end, so we return the outer time-point, as that is
|
// They intersect at the end, so we return the outer time-point, as that is
|
||||||
// when they become equal.
|
// when they become equal.
|
||||||
if ta2 < tb2 {
|
if ta2 < tb2 {
|
||||||
Sample { time: tb2, value: yb2 }
|
Some(Sample { time: tb2, value: yb2 })
|
||||||
} else {
|
} else {
|
||||||
Sample { time: ta2, value: ya2 }
|
Some(Sample { time: ta2, value: ya2 })
|
||||||
}
|
}
|
||||||
} else {
|
} else if let (Ordering::Less, Ordering::Greater) | (Ordering::Greater, Ordering::Less) = (left_cmp, right_cmp)
|
||||||
|
{
|
||||||
// The switched, so the one that switched earlier will intersect with the
|
// The switched, so the one that switched earlier will intersect with the
|
||||||
// other.
|
// other.
|
||||||
// So, we find the one that has a lower time point, i.e., the inner one.
|
// So, we find the one that has a lower time point, i.e., the inner one.
|
||||||
if ta2 < tb2 {
|
if ta2 < tb2 {
|
||||||
Sample { time: ta2, value: ya2 }
|
Some(Sample { time: ta2, value: ya2 })
|
||||||
} else {
|
} else {
|
||||||
Sample { time: tb2, value: yb2 }
|
Some(Sample { time: tb2, value: yb2 })
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// The lines must be parallel.
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -130,10 +144,8 @@ macro_rules! interpolate_for_num {
|
||||||
|
|
||||||
cast(val)
|
cast(val)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl FindIntersectionMethod<$ty> for Linear {
|
fn find_intersection(a: &Neighborhood<$ty>, b: &Neighborhood<$ty>) -> Option<Sample<$ty>> {
|
||||||
fn find_intersection(a: &Neighborhood<$ty>, b: &Neighborhood<$ty>) -> Sample<$ty> {
|
|
||||||
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line
|
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line
|
||||||
use num_traits::cast;
|
use num_traits::cast;
|
||||||
|
|
||||||
|
|
@ -153,13 +165,18 @@ macro_rules! interpolate_for_num {
|
||||||
let y4: f64 = cast(y4).unwrap_or_else(|| panic!("unable to cast {:?} to f64", y4));
|
let y4: f64 = cast(y4).unwrap_or_else(|| panic!("unable to cast {:?} to f64", y4));
|
||||||
|
|
||||||
let denom = ((t1 - t2) * (y3 - y4)) - ((y1 - y2) * (t3 - t4));
|
let denom = ((t1 - t2) * (y3 - y4)) - ((y1 - y2) * (t3 - t4));
|
||||||
|
if denom.abs() <= 1e-10 {
|
||||||
|
// The lines may be parallel or coincident.
|
||||||
|
// We just return None
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let t_top = (((t1 * y2) - (y1 * t2)) * (t3 - t4)) - ((t1 - t2) * (t3 * y4 - y3 * t4));
|
let t_top = (((t1 * y2) - (y1 * t2)) * (t3 - t4)) - ((t1 - t2) * (t3 * y4 - y3 * t4));
|
||||||
let y_top = (((t1 * y2) - (y1 * t2)) * (y3 - y4)) - ((y1 - y2) * (t3 * y4 - y3 * t4));
|
let y_top = (((t1 * y2) - (y1 * t2)) * (y3 - y4)) - ((y1 - y2) * (t3 * y4 - y3 * t4));
|
||||||
|
|
||||||
let t = Duration::from_secs_f64(t_top / denom);
|
let t = Duration::from_secs_f64(t_top / denom);
|
||||||
let y: $ty = num_traits::cast(y_top / denom).unwrap();
|
let y: $ty = num_traits::cast(y_top / denom).unwrap();
|
||||||
Sample { time: t, value: y }
|
Some(Sample { time: t, value: y })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use super::interpolation::Linear;
|
use super::interpolation::Linear;
|
||||||
use super::{FindIntersectionMethod, InterpolationMethod, SignalAbs};
|
use super::{InterpolationMethod, SignalAbs};
|
||||||
use crate::signals::Signal;
|
use crate::signals::Signal;
|
||||||
|
|
||||||
impl<T> core::ops::Neg for Signal<T>
|
impl<T> core::ops::Neg for Signal<T>
|
||||||
|
|
@ -98,7 +98,7 @@ impl<T> core::ops::Sub<&Signal<T>> for &Signal<T>
|
||||||
where
|
where
|
||||||
for<'a, 'b> &'a T: core::ops::Sub<&'b T, Output = T>,
|
for<'a, 'b> &'a T: core::ops::Sub<&'b T, Output = T>,
|
||||||
T: Clone + PartialOrd,
|
T: Clone + PartialOrd,
|
||||||
Linear: InterpolationMethod<T> + FindIntersectionMethod<T>,
|
Linear: InterpolationMethod<T>,
|
||||||
{
|
{
|
||||||
type Output = Signal<T>;
|
type Output = Signal<T>;
|
||||||
|
|
||||||
|
|
@ -133,7 +133,7 @@ impl<T> core::ops::Sub<&Signal<T>> for Signal<T>
|
||||||
where
|
where
|
||||||
for<'a, 'b> &'a T: core::ops::Sub<&'b T, Output = T>,
|
for<'a, 'b> &'a T: core::ops::Sub<&'b T, Output = T>,
|
||||||
T: Clone + PartialOrd,
|
T: Clone + PartialOrd,
|
||||||
Linear: InterpolationMethod<T> + FindIntersectionMethod<T>,
|
Linear: InterpolationMethod<T>,
|
||||||
{
|
{
|
||||||
type Output = Signal<T>;
|
type Output = Signal<T>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,10 @@ pub trait InterpolationMethod<T> {
|
||||||
/// Returns `None` if it isn't possible to interpolate at the given time using the
|
/// Returns `None` if it isn't possible to interpolate at the given time using the
|
||||||
/// given samples.
|
/// given samples.
|
||||||
fn at(a: &Sample<T>, b: &Sample<T>, time: Duration) -> Option<T>;
|
fn at(a: &Sample<T>, b: &Sample<T>, time: Duration) -> Option<T>;
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait implemented by interpolation strategies that allow finding the intersection of
|
|
||||||
/// two signal segments defined by start and end samples (see [`Neighborhood`]).
|
|
||||||
pub trait FindIntersectionMethod<T>: InterpolationMethod<T> {
|
|
||||||
/// Given two signals with two sample points each, find the intersection of the two
|
/// Given two signals with two sample points each, find the intersection of the two
|
||||||
/// lines.
|
/// lines.
|
||||||
fn find_intersection(a: &Neighborhood<T>, b: &Neighborhood<T>) -> Sample<T>;
|
fn find_intersection(a: &Neighborhood<T>, b: &Neighborhood<T>) -> Option<Sample<T>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simple trait to be used as a trait object for [`Signal<T>`] types.
|
/// Simple trait to be used as a trait object for [`Signal<T>`] types.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue