use super::{SampleDerivative, SampleTwoDerivatives};
use crate::common_traits::{HasTangent, Sum, VectorSpace, WithDerivative, WithTwoDerivatives};
use crate::curve::{
adaptors::{
ChainCurve, ConstantCurve, ContinuationCurve, CurveReparamCurve, ForeverCurve, GraphCurve,
LinearReparamCurve, PingPongCurve, RepeatCurve, ReverseCurve, ZipCurve,
},
Curve,
};
impl<T> SampleDerivative<T> for ConstantCurve<T>
where
T: HasTangent + Clone,
{
fn sample_with_derivative_unchecked(&self, _t: f32) -> WithDerivative<T> {
WithDerivative {
value: self.value.clone(),
derivative: VectorSpace::ZERO,
}
}
}
impl<T> SampleTwoDerivatives<T> for ConstantCurve<T>
where
T: HasTangent + Clone,
{
fn sample_with_two_derivatives_unchecked(&self, _t: f32) -> WithTwoDerivatives<T> {
WithTwoDerivatives {
value: self.value.clone(),
derivative: VectorSpace::ZERO,
second_derivative: VectorSpace::ZERO,
}
}
}
impl<T, C, D> SampleDerivative<T> for ChainCurve<T, C, D>
where
T: HasTangent,
C: SampleDerivative<T>,
D: SampleDerivative<T>,
{
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
if t > self.first.domain().end() {
self.second.sample_with_derivative_unchecked(
t - self.first.domain().end() + self.second.domain().start(),
)
} else {
self.first.sample_with_derivative_unchecked(t)
}
}
}
impl<T, C, D> SampleTwoDerivatives<T> for ChainCurve<T, C, D>
where
T: HasTangent,
C: SampleTwoDerivatives<T>,
D: SampleTwoDerivatives<T>,
{
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
if t > self.first.domain().end() {
self.second.sample_with_two_derivatives_unchecked(
t - self.first.domain().end() + self.second.domain().start(),
)
} else {
self.first.sample_with_two_derivatives_unchecked(t)
}
}
}
impl<T, C, D> SampleDerivative<T> for ContinuationCurve<T, C, D>
where
T: VectorSpace,
C: SampleDerivative<T>,
D: SampleDerivative<T>,
{
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
if t > self.first.domain().end() {
let mut output = self.second.sample_with_derivative_unchecked(
t - self.first.domain().end() + self.second.domain().start(),
);
output.value = output.value + self.offset;
output
} else {
self.first.sample_with_derivative_unchecked(t)
}
}
}
impl<T, C, D> SampleTwoDerivatives<T> for ContinuationCurve<T, C, D>
where
T: VectorSpace,
C: SampleTwoDerivatives<T>,
D: SampleTwoDerivatives<T>,
{
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
if t > self.first.domain().end() {
let mut output = self.second.sample_with_two_derivatives_unchecked(
t - self.first.domain().end() + self.second.domain().start(),
);
output.value = output.value + self.offset;
output
} else {
self.first.sample_with_two_derivatives_unchecked(t)
}
}
}
impl<T, C> SampleDerivative<T> for RepeatCurve<T, C>
where
T: HasTangent,
C: SampleDerivative<T>,
{
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
let t = self.base_curve_sample_time(t);
self.curve.sample_with_derivative_unchecked(t)
}
}
impl<T, C> SampleTwoDerivatives<T> for RepeatCurve<T, C>
where
T: HasTangent,
C: SampleTwoDerivatives<T>,
{
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
let t = self.base_curve_sample_time(t);
self.curve.sample_with_two_derivatives_unchecked(t)
}
}
impl<T, C> SampleDerivative<T> for ForeverCurve<T, C>
where
T: HasTangent,
C: SampleDerivative<T>,
{
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
let t = self.base_curve_sample_time(t);
self.curve.sample_with_derivative_unchecked(t)
}
}
impl<T, C> SampleTwoDerivatives<T> for ForeverCurve<T, C>
where
T: HasTangent,
C: SampleTwoDerivatives<T>,
{
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
let t = self.base_curve_sample_time(t);
self.curve.sample_with_two_derivatives_unchecked(t)
}
}
impl<T, C> SampleDerivative<T> for PingPongCurve<T, C>
where
T: HasTangent,
C: SampleDerivative<T>,
{
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
if t > self.curve.domain().end() {
let t = self.curve.domain().end() * 2.0 - t;
let mut output = self.curve.sample_with_derivative_unchecked(t);
output.derivative = -output.derivative;
output
} else {
self.curve.sample_with_derivative_unchecked(t)
}
}
}
impl<T, C> SampleTwoDerivatives<T> for PingPongCurve<T, C>
where
T: HasTangent,
C: SampleTwoDerivatives<T>,
{
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
if t > self.curve.domain().end() {
let t = self.curve.domain().end() * 2.0 - t;
let mut output = self.curve.sample_with_two_derivatives_unchecked(t);
output.derivative = -output.derivative;
output
} else {
self.curve.sample_with_two_derivatives_unchecked(t)
}
}
}
impl<S, T, C, D> SampleDerivative<(S, T)> for ZipCurve<S, T, C, D>
where
S: HasTangent,
T: HasTangent,
C: SampleDerivative<S>,
D: SampleDerivative<T>,
{
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<(S, T)> {
let first_output = self.first.sample_with_derivative_unchecked(t);
let second_output = self.second.sample_with_derivative_unchecked(t);
WithDerivative {
value: (first_output.value, second_output.value),
derivative: Sum(first_output.derivative, second_output.derivative),
}
}
}
impl<S, T, C, D> SampleTwoDerivatives<(S, T)> for ZipCurve<S, T, C, D>
where
S: HasTangent,
T: HasTangent,
C: SampleTwoDerivatives<S>,
D: SampleTwoDerivatives<T>,
{
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<(S, T)> {
let first_output = self.first.sample_with_two_derivatives_unchecked(t);
let second_output = self.second.sample_with_two_derivatives_unchecked(t);
WithTwoDerivatives {
value: (first_output.value, second_output.value),
derivative: Sum(first_output.derivative, second_output.derivative),
second_derivative: Sum(
first_output.second_derivative,
second_output.second_derivative,
),
}
}
}
impl<T, C> SampleDerivative<(f32, T)> for GraphCurve<T, C>
where
T: HasTangent,
C: SampleDerivative<T>,
{
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<(f32, T)> {
let output = self.base.sample_with_derivative_unchecked(t);
WithDerivative {
value: (t, output.value),
derivative: Sum(1.0, output.derivative),
}
}
}
impl<T, C> SampleTwoDerivatives<(f32, T)> for GraphCurve<T, C>
where
T: HasTangent,
C: SampleTwoDerivatives<T>,
{
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<(f32, T)> {
let output = self.base.sample_with_two_derivatives_unchecked(t);
WithTwoDerivatives {
value: (t, output.value),
derivative: Sum(1.0, output.derivative),
second_derivative: Sum(0.0, output.second_derivative),
}
}
}
impl<T, C> SampleDerivative<T> for ReverseCurve<T, C>
where
T: HasTangent,
C: SampleDerivative<T>,
{
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
let mut output = self
.curve
.sample_with_derivative_unchecked(self.domain().end() - (t - self.domain().start()));
output.derivative = -output.derivative;
output
}
}
impl<T, C> SampleTwoDerivatives<T> for ReverseCurve<T, C>
where
T: HasTangent,
C: SampleTwoDerivatives<T>,
{
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
let mut output = self.curve.sample_with_two_derivatives_unchecked(
self.domain().end() - (t - self.domain().start()),
);
output.derivative = -output.derivative;
output
}
}
impl<T, C, D> SampleDerivative<T> for CurveReparamCurve<T, C, D>
where
T: HasTangent,
C: SampleDerivative<T>,
D: SampleDerivative<f32>,
{
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
let reparam_output = self.reparam_curve.sample_with_derivative_unchecked(t);
let mut output = self
.base
.sample_with_derivative_unchecked(reparam_output.value);
output.derivative = output.derivative * reparam_output.derivative;
output
}
}
impl<T, C, D> SampleTwoDerivatives<T> for CurveReparamCurve<T, C, D>
where
T: HasTangent,
C: SampleTwoDerivatives<T>,
D: SampleTwoDerivatives<f32>,
{
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
let reparam_output = self.reparam_curve.sample_with_two_derivatives_unchecked(t);
let mut output = self
.base
.sample_with_two_derivatives_unchecked(reparam_output.value);
output.second_derivative = (output.second_derivative
* (reparam_output.derivative * reparam_output.derivative))
+ (output.derivative * reparam_output.second_derivative);
output.derivative = output.derivative * reparam_output.derivative;
output
}
}
impl<T, C> SampleDerivative<T> for LinearReparamCurve<T, C>
where
T: HasTangent,
C: SampleDerivative<T>,
{
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
let g = self.new_domain.linear_map_to(self.base.domain()).unwrap();
let g_derivative = self.base.domain().length() / self.new_domain.length();
let mut output = self.base.sample_with_derivative_unchecked(g(t));
output.derivative = output.derivative * g_derivative;
output
}
}
impl<T, C> SampleTwoDerivatives<T> for LinearReparamCurve<T, C>
where
T: HasTangent,
C: SampleTwoDerivatives<T>,
{
fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
let g = self.new_domain.linear_map_to(self.base.domain()).unwrap();
let g_derivative = self.base.domain().length() / self.new_domain.length();
let mut output = self.base.sample_with_two_derivatives_unchecked(g(t));
output.second_derivative = output.second_derivative * (g_derivative * g_derivative);
output.derivative = output.derivative * g_derivative;
output
}
}
#[cfg(test)]
mod tests {
use approx::assert_abs_diff_eq;
use super::*;
use crate::cubic_splines::{CubicBezier, CubicCardinalSpline, CubicCurve, CubicGenerator};
use crate::curve::{Curve, CurveExt, Interval};
use crate::{vec2, Vec2, Vec3};
fn test_curve() -> CubicCurve<Vec2> {
let control_pts = [[
vec2(0.0, 0.0),
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(1.0, 1.0),
]];
CubicBezier::new(control_pts).to_curve().unwrap()
}
fn other_test_curve() -> CubicCurve<Vec2> {
let control_pts = [
vec2(1.0, 1.0),
vec2(2.0, 1.0),
vec2(2.0, 0.0),
vec2(1.0, 0.0),
];
CubicCardinalSpline::new(0.5, control_pts)
.to_curve()
.unwrap()
}
fn reparam_curve() -> CubicCurve<f32> {
let control_pts = [[0.0, 0.25, 0.75, 1.0]];
CubicBezier::new(control_pts).to_curve().unwrap()
}
#[test]
fn constant_curve() {
let curve = ConstantCurve::new(Interval::UNIT, Vec3::new(0.2, 1.5, -2.6));
let jet = curve.sample_with_derivative(0.5).unwrap();
assert_abs_diff_eq!(jet.derivative, Vec3::ZERO);
}
#[test]
fn chain_curve() {
let curve1 = test_curve();
let curve2 = other_test_curve();
let curve = curve1.by_ref().chain(&curve2).unwrap();
let jet = curve.sample_with_derivative(0.65).unwrap();
let true_jet = curve1.sample_with_derivative(0.65).unwrap();
assert_abs_diff_eq!(jet.value, true_jet.value);
assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
let jet = curve.sample_with_derivative(1.1).unwrap();
let true_jet = curve2.sample_with_derivative(0.1).unwrap();
assert_abs_diff_eq!(jet.value, true_jet.value);
assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
}
#[test]
fn continuation_curve() {
let curve1 = test_curve();
let curve2 = other_test_curve();
let curve = curve1.by_ref().chain_continue(&curve2).unwrap();
let jet = curve.sample_with_derivative(0.99).unwrap();
let true_jet = curve1.sample_with_derivative(0.99).unwrap();
assert_abs_diff_eq!(jet.value, true_jet.value);
assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
let jet = curve.sample_with_derivative(1.3).unwrap();
let true_jet = curve2.sample_with_derivative(0.3).unwrap();
assert_abs_diff_eq!(jet.value, true_jet.value);
assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
}
#[test]
fn repeat_curve() {
let curve1 = test_curve();
let curve = curve1.by_ref().repeat(3).unwrap();
let jet = curve.sample_with_derivative(0.73).unwrap();
let true_jet = curve1.sample_with_derivative(0.73).unwrap();
assert_abs_diff_eq!(jet.value, true_jet.value);
assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
let jet = curve.sample_with_derivative(3.5).unwrap();
let true_jet = curve1.sample_with_derivative(0.5).unwrap();
assert_abs_diff_eq!(jet.value, true_jet.value);
assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
}
#[test]
fn forever_curve() {
let curve1 = test_curve();
let curve = curve1.by_ref().forever().unwrap();
let jet = curve.sample_with_derivative(0.12).unwrap();
let true_jet = curve1.sample_with_derivative(0.12).unwrap();
assert_abs_diff_eq!(jet.value, true_jet.value);
assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
let jet = curve.sample_with_derivative(500.5).unwrap();
let true_jet = curve1.sample_with_derivative(0.5).unwrap();
assert_abs_diff_eq!(jet.value, true_jet.value);
assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
}
#[test]
fn ping_pong_curve() {
let curve1 = test_curve();
let curve = curve1.by_ref().ping_pong().unwrap();
let jet = curve.sample_with_derivative(0.99).unwrap();
let comparison_jet = curve1.sample_with_derivative(0.99).unwrap();
assert_abs_diff_eq!(jet.value, comparison_jet.value);
assert_abs_diff_eq!(jet.derivative, comparison_jet.derivative);
let jet = curve.sample_with_derivative(1.3).unwrap();
let comparison_jet = curve1.sample_with_derivative(0.7).unwrap();
assert_abs_diff_eq!(jet.value, comparison_jet.value);
assert_abs_diff_eq!(jet.derivative, -comparison_jet.derivative, epsilon = 1.0e-5);
}
#[test]
fn zip_curve() {
let curve1 = test_curve();
let curve2 = other_test_curve();
let curve = curve1.by_ref().zip(&curve2).unwrap();
let jet = curve.sample_with_derivative(0.7).unwrap();
let comparison_jet1 = curve1.sample_with_derivative(0.7).unwrap();
let comparison_jet2 = curve2.sample_with_derivative(0.7).unwrap();
assert_abs_diff_eq!(jet.value.0, comparison_jet1.value);
assert_abs_diff_eq!(jet.value.1, comparison_jet2.value);
let Sum(derivative1, derivative2) = jet.derivative;
assert_abs_diff_eq!(derivative1, comparison_jet1.derivative);
assert_abs_diff_eq!(derivative2, comparison_jet2.derivative);
}
#[test]
fn graph_curve() {
let curve1 = test_curve();
let curve = curve1.by_ref().graph();
let jet = curve.sample_with_derivative(0.25).unwrap();
let comparison_jet = curve1.sample_with_derivative(0.25).unwrap();
assert_abs_diff_eq!(jet.value.0, 0.25);
assert_abs_diff_eq!(jet.value.1, comparison_jet.value);
let Sum(one, derivative) = jet.derivative;
assert_abs_diff_eq!(one, 1.0);
assert_abs_diff_eq!(derivative, comparison_jet.derivative);
}
#[test]
fn reverse_curve() {
let curve1 = test_curve();
let curve = curve1.by_ref().reverse().unwrap();
let jet = curve.sample_with_derivative(0.23).unwrap();
let comparison_jet = curve1.sample_with_derivative(0.77).unwrap();
assert_abs_diff_eq!(jet.value, comparison_jet.value);
assert_abs_diff_eq!(jet.derivative, -comparison_jet.derivative);
}
#[test]
fn curve_reparam_curve() {
let reparam_curve = reparam_curve();
let reparam_jet = reparam_curve.sample_with_derivative(0.6).unwrap();
let curve1 = test_curve();
let curve = curve1.by_ref().reparametrize_by_curve(&reparam_curve);
let jet = curve.sample_with_derivative(0.6).unwrap();
let base_jet = curve1
.sample_with_derivative(reparam_curve.sample(0.6).unwrap())
.unwrap();
assert_abs_diff_eq!(jet.value, base_jet.value);
assert_abs_diff_eq!(jet.derivative, base_jet.derivative * reparam_jet.derivative);
}
#[test]
fn linear_reparam_curve() {
let curve1 = test_curve();
let curve = curve1
.by_ref()
.reparametrize_linear(Interval::new(0.0, 0.5).unwrap())
.unwrap();
let jet = curve.sample_with_derivative(0.23).unwrap();
let comparison_jet = curve1.sample_with_derivative(0.46).unwrap();
assert_abs_diff_eq!(jet.value, comparison_jet.value);
assert_abs_diff_eq!(jet.derivative, comparison_jet.derivative * 2.0);
}
}