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>>
|
||||
where
|
||||
T: PartialOrd + Clone,
|
||||
Interp: FindIntersectionMethod<T>,
|
||||
Interp: InterpolationMethod<T>,
|
||||
{
|
||||
use core::cmp::Ordering::*;
|
||||
let sync_points: Vec<&Duration> = self.sync_points(other)?.into_iter().collect();
|
||||
|
|
@ -308,7 +308,8 @@ impl<T> Signal<T> {
|
|||
.interpolate_at::<Interp>(*t)
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ use std::cmp::Ordering;
|
|||
|
||||
use super::interpolation::Linear;
|
||||
use super::traits::SignalPartialOrd;
|
||||
use super::{FindIntersectionMethod, InterpolationMethod, Signal};
|
||||
use super::{InterpolationMethod, Signal};
|
||||
|
||||
impl<T> SignalPartialOrd for Signal<T>
|
||||
where
|
||||
T: PartialOrd + Clone,
|
||||
Linear: InterpolationMethod<T> + FindIntersectionMethod<T>,
|
||||
Linear: InterpolationMethod<T>,
|
||||
{
|
||||
fn signal_cmp<F>(&self, other: &Self, op: F) -> Option<Signal<bool>>
|
||||
where
|
||||
|
|
@ -35,7 +35,7 @@ where
|
|||
impl<T> Signal<T>
|
||||
where
|
||||
T: PartialOrd + Clone,
|
||||
Linear: InterpolationMethod<T> + FindIntersectionMethod<T>,
|
||||
Linear: InterpolationMethod<T>,
|
||||
{
|
||||
/// Compute the time-wise min of two signals
|
||||
pub fn min(&self, other: &Self) -> Self {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
//! Interpolation methods
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::time::Duration;
|
||||
|
||||
use super::utils::Neighborhood;
|
||||
use super::{FindIntersectionMethod, InterpolationMethod, Sample};
|
||||
use super::{InterpolationMethod, Sample};
|
||||
|
||||
/// Constant interpolation.
|
||||
///
|
||||
|
|
@ -20,6 +21,11 @@ impl<T: Clone> InterpolationMethod<T> for Constant {
|
|||
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.
|
||||
|
|
@ -41,6 +47,12 @@ impl<T: Clone> InterpolationMethod<T> for Nearest {
|
|||
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.
|
||||
|
|
@ -58,10 +70,8 @@ impl InterpolationMethod<bool> for Linear {
|
|||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FindIntersectionMethod<bool> for Linear {
|
||||
fn find_intersection(a: &Neighborhood<bool>, b: &Neighborhood<bool>) -> Sample<bool> {
|
||||
fn find_intersection(a: &Neighborhood<bool>, b: &Neighborhood<bool>) -> Option<Sample<bool>> {
|
||||
let Sample { time: ta1, value: ya1 } = a.first.unwrap();
|
||||
let Sample { time: ta2, value: ya2 } = a.second.unwrap();
|
||||
let Sample { time: tb1, value: yb1 } = b.first.unwrap();
|
||||
|
|
@ -73,27 +83,31 @@ impl FindIntersectionMethod<bool> for Linear {
|
|||
if left_cmp.is_eq() {
|
||||
// They already intersect, so we return the inner time-point
|
||||
if ta1 < tb1 {
|
||||
Sample { time: tb1, value: yb1 }
|
||||
Some(Sample { time: tb1, value: yb1 })
|
||||
} else {
|
||||
Sample { time: ta1, value: ya1 }
|
||||
Some(Sample { time: ta1, value: ya1 })
|
||||
}
|
||||
} else if right_cmp.is_eq() {
|
||||
// They intersect at the end, so we return the outer time-point, as that is
|
||||
// when they become equal.
|
||||
if ta2 < tb2 {
|
||||
Sample { time: tb2, value: yb2 }
|
||||
Some(Sample { time: tb2, value: yb2 })
|
||||
} 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
|
||||
// other.
|
||||
// So, we find the one that has a lower time point, i.e., the inner one.
|
||||
if ta2 < tb2 {
|
||||
Sample { time: ta2, value: ya2 }
|
||||
Some(Sample { time: ta2, value: ya2 })
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
|
||||
impl FindIntersectionMethod<$ty> for Linear {
|
||||
fn find_intersection(a: &Neighborhood<$ty>, b: &Neighborhood<$ty>) -> Sample<$ty> {
|
||||
fn find_intersection(a: &Neighborhood<$ty>, b: &Neighborhood<$ty>) -> Option<Sample<$ty>> {
|
||||
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line
|
||||
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 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 y_top = (((t1 * y2) - (y1 * t2)) * (y3 - y4)) - ((y1 - y2) * (t3 * y4 - y3 * t4));
|
||||
|
||||
let t = Duration::from_secs_f64(t_top / denom);
|
||||
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::{FindIntersectionMethod, InterpolationMethod, SignalAbs};
|
||||
use super::{InterpolationMethod, SignalAbs};
|
||||
use crate::signals::Signal;
|
||||
|
||||
impl<T> core::ops::Neg for Signal<T>
|
||||
|
|
@ -98,7 +98,7 @@ impl<T> core::ops::Sub<&Signal<T>> for &Signal<T>
|
|||
where
|
||||
for<'a, 'b> &'a T: core::ops::Sub<&'b T, Output = T>,
|
||||
T: Clone + PartialOrd,
|
||||
Linear: InterpolationMethod<T> + FindIntersectionMethod<T>,
|
||||
Linear: InterpolationMethod<T>,
|
||||
{
|
||||
type Output = Signal<T>;
|
||||
|
||||
|
|
@ -133,7 +133,7 @@ impl<T> core::ops::Sub<&Signal<T>> for Signal<T>
|
|||
where
|
||||
for<'a, 'b> &'a T: core::ops::Sub<&'b T, Output = T>,
|
||||
T: Clone + PartialOrd,
|
||||
Linear: InterpolationMethod<T> + FindIntersectionMethod<T>,
|
||||
Linear: InterpolationMethod<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
|
||||
/// given samples.
|
||||
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
|
||||
/// 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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue