1use super::{SampleDerivative, SampleTwoDerivatives};
5use crate::common_traits::{HasTangent, Sum, VectorSpace, WithDerivative, WithTwoDerivatives};
6use crate::curve::{
7 adaptors::{
8 ChainCurve, ConstantCurve, ContinuationCurve, CurveReparamCurve, ForeverCurve, GraphCurve,
9 LinearReparamCurve, PingPongCurve, RepeatCurve, ReverseCurve, ZipCurve,
10 },
11 Curve,
12};
13
14impl<T> SampleDerivative<T> for ConstantCurve<T>
17where
18 T: HasTangent + Clone,
19{
20 fn sample_with_derivative_unchecked(&self, _t: f32) -> WithDerivative<T> {
21 WithDerivative {
22 value: self.value.clone(),
23 derivative: VectorSpace::ZERO,
24 }
25 }
26}
27
28impl<T> SampleTwoDerivatives<T> for ConstantCurve<T>
29where
30 T: HasTangent + Clone,
31{
32 fn sample_with_two_derivatives_unchecked(&self, _t: f32) -> WithTwoDerivatives<T> {
33 WithTwoDerivatives {
34 value: self.value.clone(),
35 derivative: VectorSpace::ZERO,
36 second_derivative: VectorSpace::ZERO,
37 }
38 }
39}
40
41impl<T, C, D> SampleDerivative<T> for ChainCurve<T, C, D>
44where
45 T: HasTangent,
46 C: SampleDerivative<T>,
47 D: SampleDerivative<T>,
48{
49 fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
50 if t > self.first.domain().end() {
51 self.second.sample_with_derivative_unchecked(
52 t - self.first.domain().end() + self.second.domain().start(),
54 )
55 } else {
56 self.first.sample_with_derivative_unchecked(t)
57 }
58 }
59}
60
61impl<T, C, D> SampleTwoDerivatives<T> for ChainCurve<T, C, D>
62where
63 T: HasTangent,
64 C: SampleTwoDerivatives<T>,
65 D: SampleTwoDerivatives<T>,
66{
67 fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
68 if t > self.first.domain().end() {
69 self.second.sample_with_two_derivatives_unchecked(
70 t - self.first.domain().end() + self.second.domain().start(),
72 )
73 } else {
74 self.first.sample_with_two_derivatives_unchecked(t)
75 }
76 }
77}
78
79impl<T, C, D> SampleDerivative<T> for ContinuationCurve<T, C, D>
82where
83 T: VectorSpace,
84 C: SampleDerivative<T>,
85 D: SampleDerivative<T>,
86{
87 fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
88 if t > self.first.domain().end() {
89 let mut output = self.second.sample_with_derivative_unchecked(
90 t - self.first.domain().end() + self.second.domain().start(),
92 );
93 output.value = output.value + self.offset;
94 output
95 } else {
96 self.first.sample_with_derivative_unchecked(t)
97 }
98 }
99}
100
101impl<T, C, D> SampleTwoDerivatives<T> for ContinuationCurve<T, C, D>
102where
103 T: VectorSpace,
104 C: SampleTwoDerivatives<T>,
105 D: SampleTwoDerivatives<T>,
106{
107 fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
108 if t > self.first.domain().end() {
109 let mut output = self.second.sample_with_two_derivatives_unchecked(
110 t - self.first.domain().end() + self.second.domain().start(),
112 );
113 output.value = output.value + self.offset;
114 output
115 } else {
116 self.first.sample_with_two_derivatives_unchecked(t)
117 }
118 }
119}
120
121impl<T, C> SampleDerivative<T> for RepeatCurve<T, C>
124where
125 T: HasTangent,
126 C: SampleDerivative<T>,
127{
128 fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
129 let t = self.base_curve_sample_time(t);
130 self.curve.sample_with_derivative_unchecked(t)
131 }
132}
133
134impl<T, C> SampleTwoDerivatives<T> for RepeatCurve<T, C>
135where
136 T: HasTangent,
137 C: SampleTwoDerivatives<T>,
138{
139 fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
140 let t = self.base_curve_sample_time(t);
141 self.curve.sample_with_two_derivatives_unchecked(t)
142 }
143}
144
145impl<T, C> SampleDerivative<T> for ForeverCurve<T, C>
148where
149 T: HasTangent,
150 C: SampleDerivative<T>,
151{
152 fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
153 let t = self.base_curve_sample_time(t);
154 self.curve.sample_with_derivative_unchecked(t)
155 }
156}
157
158impl<T, C> SampleTwoDerivatives<T> for ForeverCurve<T, C>
159where
160 T: HasTangent,
161 C: SampleTwoDerivatives<T>,
162{
163 fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
164 let t = self.base_curve_sample_time(t);
165 self.curve.sample_with_two_derivatives_unchecked(t)
166 }
167}
168
169impl<T, C> SampleDerivative<T> for PingPongCurve<T, C>
172where
173 T: HasTangent,
174 C: SampleDerivative<T>,
175{
176 fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
177 if t > self.curve.domain().end() {
178 let t = self.curve.domain().end() * 2.0 - t;
179 let mut output = self.curve.sample_with_derivative_unchecked(t);
182 output.derivative = -output.derivative;
183 output
184 } else {
185 self.curve.sample_with_derivative_unchecked(t)
186 }
187 }
188}
189
190impl<T, C> SampleTwoDerivatives<T> for PingPongCurve<T, C>
191where
192 T: HasTangent,
193 C: SampleTwoDerivatives<T>,
194{
195 fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
196 if t > self.curve.domain().end() {
197 let t = self.curve.domain().end() * 2.0 - t;
198 let mut output = self.curve.sample_with_two_derivatives_unchecked(t);
201 output.derivative = -output.derivative;
202 output
203 } else {
204 self.curve.sample_with_two_derivatives_unchecked(t)
205 }
206 }
207}
208
209impl<S, T, C, D> SampleDerivative<(S, T)> for ZipCurve<S, T, C, D>
212where
213 S: HasTangent,
214 T: HasTangent,
215 C: SampleDerivative<S>,
216 D: SampleDerivative<T>,
217{
218 fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<(S, T)> {
219 let first_output = self.first.sample_with_derivative_unchecked(t);
220 let second_output = self.second.sample_with_derivative_unchecked(t);
221 WithDerivative {
222 value: (first_output.value, second_output.value),
223 derivative: Sum(first_output.derivative, second_output.derivative),
224 }
225 }
226}
227
228impl<S, T, C, D> SampleTwoDerivatives<(S, T)> for ZipCurve<S, T, C, D>
229where
230 S: HasTangent,
231 T: HasTangent,
232 C: SampleTwoDerivatives<S>,
233 D: SampleTwoDerivatives<T>,
234{
235 fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<(S, T)> {
236 let first_output = self.first.sample_with_two_derivatives_unchecked(t);
237 let second_output = self.second.sample_with_two_derivatives_unchecked(t);
238 WithTwoDerivatives {
239 value: (first_output.value, second_output.value),
240 derivative: Sum(first_output.derivative, second_output.derivative),
241 second_derivative: Sum(
242 first_output.second_derivative,
243 second_output.second_derivative,
244 ),
245 }
246 }
247}
248
249impl<T, C> SampleDerivative<(f32, T)> for GraphCurve<T, C>
252where
253 T: HasTangent,
254 C: SampleDerivative<T>,
255{
256 fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<(f32, T)> {
257 let output = self.base.sample_with_derivative_unchecked(t);
258 WithDerivative {
259 value: (t, output.value),
260 derivative: Sum(1.0, output.derivative),
261 }
262 }
263}
264
265impl<T, C> SampleTwoDerivatives<(f32, T)> for GraphCurve<T, C>
266where
267 T: HasTangent,
268 C: SampleTwoDerivatives<T>,
269{
270 fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<(f32, T)> {
271 let output = self.base.sample_with_two_derivatives_unchecked(t);
272 WithTwoDerivatives {
273 value: (t, output.value),
274 derivative: Sum(1.0, output.derivative),
275 second_derivative: Sum(0.0, output.second_derivative),
276 }
277 }
278}
279
280impl<T, C> SampleDerivative<T> for ReverseCurve<T, C>
283where
284 T: HasTangent,
285 C: SampleDerivative<T>,
286{
287 fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
288 let mut output = self
291 .curve
292 .sample_with_derivative_unchecked(self.domain().end() - (t - self.domain().start()));
293
294 output.derivative = -output.derivative;
295
296 output
297 }
298}
299
300impl<T, C> SampleTwoDerivatives<T> for ReverseCurve<T, C>
301where
302 T: HasTangent,
303 C: SampleTwoDerivatives<T>,
304{
305 fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
306 let mut output = self.curve.sample_with_two_derivatives_unchecked(
309 self.domain().end() - (t - self.domain().start()),
310 );
311
312 output.derivative = -output.derivative;
313
314 output
319 }
320}
321
322impl<T, C, D> SampleDerivative<T> for CurveReparamCurve<T, C, D>
325where
326 T: HasTangent,
327 C: SampleDerivative<T>,
328 D: SampleDerivative<f32>,
329{
330 fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
331 let reparam_output = self.reparam_curve.sample_with_derivative_unchecked(t);
336
337 let mut output = self
341 .base
342 .sample_with_derivative_unchecked(reparam_output.value);
343
344 output.derivative = output.derivative * reparam_output.derivative;
346
347 output
349 }
350}
351
352impl<T, C, D> SampleTwoDerivatives<T> for CurveReparamCurve<T, C, D>
353where
354 T: HasTangent,
355 C: SampleTwoDerivatives<T>,
356 D: SampleTwoDerivatives<f32>,
357{
358 fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
359 let reparam_output = self.reparam_curve.sample_with_two_derivatives_unchecked(t);
364
365 let mut output = self
370 .base
371 .sample_with_two_derivatives_unchecked(reparam_output.value);
372
373 output.second_derivative = (output.second_derivative
376 * (reparam_output.derivative * reparam_output.derivative))
377 + (output.derivative * reparam_output.second_derivative);
378
379 output.derivative = output.derivative * reparam_output.derivative;
382
383 output
384 }
385}
386
387impl<T, C> SampleDerivative<T> for LinearReparamCurve<T, C>
390where
391 T: HasTangent,
392 C: SampleDerivative<T>,
393{
394 fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T> {
395 let g = self.new_domain.linear_map_to(self.base.domain()).unwrap();
400
401 let g_derivative = self.base.domain().length() / self.new_domain.length();
403
404 let mut output = self.base.sample_with_derivative_unchecked(g(t));
408
409 output.derivative = output.derivative * g_derivative;
411
412 output
413 }
414}
415
416impl<T, C> SampleTwoDerivatives<T> for LinearReparamCurve<T, C>
417where
418 T: HasTangent,
419 C: SampleTwoDerivatives<T>,
420{
421 fn sample_with_two_derivatives_unchecked(&self, t: f32) -> WithTwoDerivatives<T> {
422 let g = self.new_domain.linear_map_to(self.base.domain()).unwrap();
427
428 let g_derivative = self.base.domain().length() / self.new_domain.length();
430
431 let mut output = self.base.sample_with_two_derivatives_unchecked(g(t));
436
437 output.second_derivative = output.second_derivative * (g_derivative * g_derivative);
440
441 output.derivative = output.derivative * g_derivative;
444
445 output
446 }
447}
448
449#[cfg(test)]
450mod tests {
451
452 use approx::assert_abs_diff_eq;
453
454 use super::*;
455 use crate::cubic_splines::{CubicBezier, CubicCardinalSpline, CubicCurve, CubicGenerator};
456 use crate::curve::{Curve, CurveExt, Interval};
457 use crate::{vec2, Vec2, Vec3};
458
459 fn test_curve() -> CubicCurve<Vec2> {
460 let control_pts = [[
461 vec2(0.0, 0.0),
462 vec2(1.0, 0.0),
463 vec2(0.0, 1.0),
464 vec2(1.0, 1.0),
465 ]];
466
467 CubicBezier::new(control_pts).to_curve().unwrap()
468 }
469
470 fn other_test_curve() -> CubicCurve<Vec2> {
471 let control_pts = [
472 vec2(1.0, 1.0),
473 vec2(2.0, 1.0),
474 vec2(2.0, 0.0),
475 vec2(1.0, 0.0),
476 ];
477
478 CubicCardinalSpline::new(0.5, control_pts)
479 .to_curve()
480 .unwrap()
481 }
482
483 fn reparam_curve() -> CubicCurve<f32> {
484 let control_pts = [[0.0, 0.25, 0.75, 1.0]];
485
486 CubicBezier::new(control_pts).to_curve().unwrap()
487 }
488
489 #[test]
490 fn constant_curve() {
491 let curve = ConstantCurve::new(Interval::UNIT, Vec3::new(0.2, 1.5, -2.6));
492 let jet = curve.sample_with_derivative(0.5).unwrap();
493 assert_abs_diff_eq!(jet.derivative, Vec3::ZERO);
494 }
495
496 #[test]
497 fn chain_curve() {
498 let curve1 = test_curve();
499 let curve2 = other_test_curve();
500 let curve = curve1.by_ref().chain(&curve2).unwrap();
501
502 let jet = curve.sample_with_derivative(0.65).unwrap();
503 let true_jet = curve1.sample_with_derivative(0.65).unwrap();
504 assert_abs_diff_eq!(jet.value, true_jet.value);
505 assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
506
507 let jet = curve.sample_with_derivative(1.1).unwrap();
508 let true_jet = curve2.sample_with_derivative(0.1).unwrap();
509 assert_abs_diff_eq!(jet.value, true_jet.value);
510 assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
511 }
512
513 #[test]
514 fn continuation_curve() {
515 let curve1 = test_curve();
516 let curve2 = other_test_curve();
517 let curve = curve1.by_ref().chain_continue(&curve2).unwrap();
518
519 let jet = curve.sample_with_derivative(0.99).unwrap();
520 let true_jet = curve1.sample_with_derivative(0.99).unwrap();
521 assert_abs_diff_eq!(jet.value, true_jet.value);
522 assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
523
524 let jet = curve.sample_with_derivative(1.3).unwrap();
525 let true_jet = curve2.sample_with_derivative(0.3).unwrap();
526 assert_abs_diff_eq!(jet.value, true_jet.value);
527 assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
528 }
529
530 #[test]
531 fn repeat_curve() {
532 let curve1 = test_curve();
533 let curve = curve1.by_ref().repeat(3).unwrap();
534
535 let jet = curve.sample_with_derivative(0.73).unwrap();
536 let true_jet = curve1.sample_with_derivative(0.73).unwrap();
537 assert_abs_diff_eq!(jet.value, true_jet.value);
538 assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
539
540 let jet = curve.sample_with_derivative(3.5).unwrap();
541 let true_jet = curve1.sample_with_derivative(0.5).unwrap();
542 assert_abs_diff_eq!(jet.value, true_jet.value);
543 assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
544 }
545
546 #[test]
547 fn forever_curve() {
548 let curve1 = test_curve();
549 let curve = curve1.by_ref().forever().unwrap();
550
551 let jet = curve.sample_with_derivative(0.12).unwrap();
552 let true_jet = curve1.sample_with_derivative(0.12).unwrap();
553 assert_abs_diff_eq!(jet.value, true_jet.value);
554 assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
555
556 let jet = curve.sample_with_derivative(500.5).unwrap();
557 let true_jet = curve1.sample_with_derivative(0.5).unwrap();
558 assert_abs_diff_eq!(jet.value, true_jet.value);
559 assert_abs_diff_eq!(jet.derivative, true_jet.derivative);
560 }
561
562 #[test]
563 fn ping_pong_curve() {
564 let curve1 = test_curve();
565 let curve = curve1.by_ref().ping_pong().unwrap();
566
567 let jet = curve.sample_with_derivative(0.99).unwrap();
568 let comparison_jet = curve1.sample_with_derivative(0.99).unwrap();
569 assert_abs_diff_eq!(jet.value, comparison_jet.value);
570 assert_abs_diff_eq!(jet.derivative, comparison_jet.derivative);
571
572 let jet = curve.sample_with_derivative(1.3).unwrap();
573 let comparison_jet = curve1.sample_with_derivative(0.7).unwrap();
574 assert_abs_diff_eq!(jet.value, comparison_jet.value);
575 assert_abs_diff_eq!(jet.derivative, -comparison_jet.derivative, epsilon = 1.0e-5);
576 }
577
578 #[test]
579 fn zip_curve() {
580 let curve1 = test_curve();
581 let curve2 = other_test_curve();
582 let curve = curve1.by_ref().zip(&curve2).unwrap();
583
584 let jet = curve.sample_with_derivative(0.7).unwrap();
585 let comparison_jet1 = curve1.sample_with_derivative(0.7).unwrap();
586 let comparison_jet2 = curve2.sample_with_derivative(0.7).unwrap();
587 assert_abs_diff_eq!(jet.value.0, comparison_jet1.value);
588 assert_abs_diff_eq!(jet.value.1, comparison_jet2.value);
589 let Sum(derivative1, derivative2) = jet.derivative;
590 assert_abs_diff_eq!(derivative1, comparison_jet1.derivative);
591 assert_abs_diff_eq!(derivative2, comparison_jet2.derivative);
592 }
593
594 #[test]
595 fn graph_curve() {
596 let curve1 = test_curve();
597 let curve = curve1.by_ref().graph();
598
599 let jet = curve.sample_with_derivative(0.25).unwrap();
600 let comparison_jet = curve1.sample_with_derivative(0.25).unwrap();
601 assert_abs_diff_eq!(jet.value.0, 0.25);
602 assert_abs_diff_eq!(jet.value.1, comparison_jet.value);
603 let Sum(one, derivative) = jet.derivative;
604 assert_abs_diff_eq!(one, 1.0);
605 assert_abs_diff_eq!(derivative, comparison_jet.derivative);
606 }
607
608 #[test]
609 fn reverse_curve() {
610 let curve1 = test_curve();
611 let curve = curve1.by_ref().reverse().unwrap();
612
613 let jet = curve.sample_with_derivative(0.23).unwrap();
614 let comparison_jet = curve1.sample_with_derivative(0.77).unwrap();
615 assert_abs_diff_eq!(jet.value, comparison_jet.value);
616 assert_abs_diff_eq!(jet.derivative, -comparison_jet.derivative);
617 }
618
619 #[test]
620 fn curve_reparam_curve() {
621 let reparam_curve = reparam_curve();
622 let reparam_jet = reparam_curve.sample_with_derivative(0.6).unwrap();
623
624 let curve1 = test_curve();
625 let curve = curve1.by_ref().reparametrize_by_curve(&reparam_curve);
626
627 let jet = curve.sample_with_derivative(0.6).unwrap();
628 let base_jet = curve1
629 .sample_with_derivative(reparam_curve.sample(0.6).unwrap())
630 .unwrap();
631 assert_abs_diff_eq!(jet.value, base_jet.value);
632 assert_abs_diff_eq!(jet.derivative, base_jet.derivative * reparam_jet.derivative);
633 }
634
635 #[test]
636 fn linear_reparam_curve() {
637 let curve1 = test_curve();
638 let curve = curve1
639 .by_ref()
640 .reparametrize_linear(Interval::new(0.0, 0.5).unwrap())
641 .unwrap();
642
643 let jet = curve.sample_with_derivative(0.23).unwrap();
644 let comparison_jet = curve1.sample_with_derivative(0.46).unwrap();
645 assert_abs_diff_eq!(jet.value, comparison_jet.value);
646 assert_abs_diff_eq!(jet.derivative, comparison_jet.derivative * 2.0);
647 }
648}