Loading...
Searching...
No Matches
evalCache.h
1//
2// Copyright 2023 Pixar
3//
4// Licensed under the terms set forth in the LICENSE.txt file available at
5// https://openusd.org/license.
6//
7
8#ifndef PXR_BASE_TS_EVAL_CACHE_H
9#define PXR_BASE_TS_EVAL_CACHE_H
10
11#include "pxr/pxr.h"
12#include "pxr/base/gf/math.h"
13#include "pxr/base/ts/keyFrameUtils.h"
14#include "pxr/base/ts/mathUtils.h"
15#include "pxr/base/ts/types.h"
16#include "pxr/base/vt/value.h"
17
18#include "pxr/base/tf/tf.h"
19
20#include <type_traits>
21
22PXR_NAMESPACE_OPEN_SCOPE
23
24class TsKeyFrame;
25template <typename T> class Ts_TypedData;
26
27// Bezier data. This holds two beziers (time and value) as both points
28// and the coefficients of a cubic polynomial.
29template <typename T>
30class Ts_Bezier {
31public:
32 Ts_Bezier() { }
33 Ts_Bezier(const TsTime timePoints[4], const T valuePoints[4]);
34 void DerivePolynomial();
35
36public:
37 TsTime timePoints[4];
38 TsTime timeCoeff[4];
39 T valuePoints[4];
40 T valueCoeff[4];
41};
42
43template <typename T>
44Ts_Bezier<T>::Ts_Bezier(const TsTime time[4], const T value[4])
45{
46 timePoints[0] = time[0];
47 timePoints[1] = time[1];
48 timePoints[2] = time[2];
49 timePoints[3] = time[3];
50 valuePoints[0] = value[0];
51 valuePoints[1] = value[1];
52 valuePoints[2] = value[2];
53 valuePoints[3] = value[3];
54 DerivePolynomial();
55}
56
57template <typename T>
58void
59Ts_Bezier<T>::DerivePolynomial()
60{
61 timeCoeff[0] = timePoints[0];
62 timeCoeff[1] = -3.0 * timePoints[0] +
63 3.0 * timePoints[1];
64 timeCoeff[2] = 3.0 * timePoints[0] +
65 -6.0 * timePoints[1] +
66 3.0 * timePoints[2];
67 timeCoeff[3] = -1.0 * timePoints[0] +
68 3.0 * timePoints[1] +
69 -3.0 * timePoints[2] +
70 timePoints[3];
71 valueCoeff[0] = valuePoints[0];
72 valueCoeff[1] = -3.0 * valuePoints[0] +
73 3.0 * valuePoints[1];
74 valueCoeff[2] = 3.0 * valuePoints[0] +
75 -6.0 * valuePoints[1] +
76 3.0 * valuePoints[2];
77 valueCoeff[3] = -1.0 * valuePoints[0] +
78 3.0 * valuePoints[1] +
79 -3.0 * valuePoints[2] +
80 valuePoints[3];
81}
82
83class Ts_UntypedEvalCache {
84public:
85 typedef std::shared_ptr<Ts_UntypedEvalCache> SharedPtr;
86
88 static SharedPtr New(const TsKeyFrame &kf1, const TsKeyFrame &kf2);
89
90 virtual VtValue Eval(TsTime) const = 0;
91 virtual VtValue EvalDerivative(TsTime) const = 0;
92
93 // Equivalent to invoking New() and Eval(time) on the newly created cache,
94 // but without the heap allocation.
95 static VtValue EvalUncached(const TsKeyFrame &kf1,
96 const TsKeyFrame &kf2,
97 TsTime time);
98
99 // Equivalent to invoking New() and EvalDerivative(time) on the newly
100 // created cache, but without the heap allocation.
101 static VtValue EvalDerivativeUncached(const TsKeyFrame &kf1,
102 const TsKeyFrame &kf2,
103 TsTime time);
104
105protected:
106 ~Ts_UntypedEvalCache() = default;
107
108 // Compute the Bezier control points.
109 template <typename T>
110 static void _SetupBezierGeometry(TsTime* timePoints, T* valuePoints,
111 const Ts_TypedData<T>* kf1,
112 const Ts_TypedData<T>* kf2);
113
114 // Compute the time coordinate of the 2nd Bezier control point. This
115 // synthesizes tangents for held and linear knots.
116 template <typename T>
117 static TsTime _GetBezierPoint2Time(const Ts_TypedData<T>* kf1,
118 const Ts_TypedData<T>* kf2);
119
120 // Compute the time coordinate of the 3rd Bezier control point. This
121 // synthesizes tangents for held and linear knots.
122 template <typename T>
123 static TsTime _GetBezierPoint3Time(const Ts_TypedData<T>* kf1,
124 const Ts_TypedData<T>* kf2);
125
126 // Compute the value coordinate of the 2nd Bezier control point. This
127 // synthesizes tangents for held and linear knots.
128 template <typename T>
129 static T _GetBezierPoint2Value(const Ts_TypedData<T>* kf1,
130 const Ts_TypedData<T>* kf2);
131
132 // Compute the value coordinate of the 3rd Bezier control point. This
133 // synthesizes tangents for held and linear knots.
134 template <typename T>
135 static T _GetBezierPoint3Value(const Ts_TypedData<T>* kf1,
136 const Ts_TypedData<T>* kf2);
137
138 // Compute the value coordinate of the 4th Bezier control point. This
139 // synthesizes tangents for held and linear knots.
140 template <typename T>
141 static T _GetBezierPoint4Value(const Ts_TypedData<T>* kf1,
142 const Ts_TypedData<T>* kf2);
143};
144
145template <typename T, bool INTERPOLATABLE = TsTraits<T>::interpolatable >
146class Ts_EvalCache;
147
148template <typename T>
149class Ts_EvalQuaternionCache : public Ts_UntypedEvalCache {
150protected:
151 static_assert(std::is_same<T, GfQuatf>::value
152 || std::is_same<T, GfQuatd>::value
153 , "T must be Quatd or Quatf");
154 Ts_EvalQuaternionCache(const Ts_EvalQuaternionCache<T> * rhs);
155 Ts_EvalQuaternionCache(const Ts_TypedData<T>* kf1,
156 const Ts_TypedData<T>* kf2);
157 Ts_EvalQuaternionCache(const TsKeyFrame & kf1,
158 const TsKeyFrame & kf2);
159
160public:
161 T TypedEval(TsTime) const;
162 T TypedEvalDerivative(TsTime) const;
163
164 VtValue Eval(TsTime t) const override;
165 VtValue EvalDerivative(TsTime t) const override;
166private:
167 void _Init(const Ts_TypedData<T>* kf1, const Ts_TypedData<T>* kf2);
168 double _kf1_time, _kf2_time;
169 T _kf1_value, _kf2_value;
170 TsKnotType _kf1_knot_type;
171};
172
173template<>
174class Ts_EvalCache<GfQuatf, true> final
175 : public Ts_EvalQuaternionCache<GfQuatf> {
176public:
177 Ts_EvalCache(const Ts_EvalCache<GfQuatf, true> *rhs) :
178 Ts_EvalQuaternionCache<GfQuatf>(rhs) {}
179 Ts_EvalCache(const Ts_TypedData<GfQuatf>* kf1,
180 const Ts_TypedData<GfQuatf>* kf2) :
181 Ts_EvalQuaternionCache<GfQuatf>(kf1, kf2) {}
182 Ts_EvalCache(const TsKeyFrame & kf1, const TsKeyFrame & kf2) :
183 Ts_EvalQuaternionCache<GfQuatf>(kf1, kf2) {}
184
185 typedef std::shared_ptr<Ts_EvalCache<GfQuatf, true> > TypedSharedPtr;
186
188 static TypedSharedPtr New(const TsKeyFrame &kf1, const TsKeyFrame &kf2);
189};
190
191template<>
192class Ts_EvalCache<GfQuatd, true> final
193 : public Ts_EvalQuaternionCache<GfQuatd> {
194public:
195 Ts_EvalCache(const Ts_EvalCache<GfQuatd, true> *rhs) :
196 Ts_EvalQuaternionCache<GfQuatd>(rhs) {}
197 Ts_EvalCache(const Ts_TypedData<GfQuatd>* kf1,
198 const Ts_TypedData<GfQuatd>* kf2) :
199 Ts_EvalQuaternionCache<GfQuatd>(kf1, kf2) {}
200 Ts_EvalCache(const TsKeyFrame & kf1, const TsKeyFrame & kf2) :
201 Ts_EvalQuaternionCache<GfQuatd>(kf1, kf2) {}
202
203 typedef std::shared_ptr<Ts_EvalCache<GfQuatd, true> > TypedSharedPtr;
204
206 static TypedSharedPtr New(const TsKeyFrame &kf1, const TsKeyFrame &kf2);
207
208};
209
210// Partial specialization for types that cannot be interpolated.
211template <typename T>
212class Ts_EvalCache<T, false> final : public Ts_UntypedEvalCache {
213public:
214 Ts_EvalCache(const Ts_EvalCache<T, false> * rhs);
215 Ts_EvalCache(const Ts_TypedData<T>* kf1, const Ts_TypedData<T>* kf2);
216 Ts_EvalCache(const TsKeyFrame & kf1, const TsKeyFrame & kf2);
217 T TypedEval(TsTime) const;
218 T TypedEvalDerivative(TsTime) const;
219
220 VtValue Eval(TsTime t) const override;
221 VtValue EvalDerivative(TsTime t) const override;
222
223 typedef std::shared_ptr<Ts_EvalCache<T, false> > TypedSharedPtr;
224
226 static TypedSharedPtr New(const TsKeyFrame &kf1, const TsKeyFrame &kf2);
227
228private:
229 T _value;
230};
231
232// Partial specialization for types that can be interpolated.
233template <typename T>
234class Ts_EvalCache<T, true> final : public Ts_UntypedEvalCache {
235public:
236 Ts_EvalCache(const Ts_EvalCache<T, true> * rhs);
237 Ts_EvalCache(const Ts_TypedData<T>* kf1, const Ts_TypedData<T>* kf2);
238 Ts_EvalCache(const TsKeyFrame & kf1, const TsKeyFrame & kf2);
239 T TypedEval(TsTime) const;
240 T TypedEvalDerivative(TsTime) const;
241
242 VtValue Eval(TsTime t) const override;
243 VtValue EvalDerivative(TsTime t) const override;
244
245 const Ts_Bezier<T>* GetBezier() const;
246
247 typedef std::shared_ptr<Ts_EvalCache<T, true> > TypedSharedPtr;
248
250 static TypedSharedPtr New(const TsKeyFrame &kf1, const TsKeyFrame &kf2);
251
252private:
253 void _Init(const Ts_TypedData<T>* kf1, const Ts_TypedData<T>* kf2);
254
255private:
256 bool _interpolate;
257
258 // Value to use when _interpolate is false.
259 T _value;
260
261 Ts_Bezier<T> _cache;
262};
263
265// Ts_UntypedEvalCache
266
267template <typename T>
268TsTime
269Ts_UntypedEvalCache::_GetBezierPoint2Time(const Ts_TypedData<T>* kf1,
270 const Ts_TypedData<T>* kf2)
271{
272 switch (kf1->_knotType) {
273 default:
274 case TsKnotHeld:
275 case TsKnotLinear:
276 return (2.0 * kf1->GetTime() + kf2->GetTime()) / 3.0;
277
278 case TsKnotBezier:
279 return kf1->GetTime() + kf1->_rightTangentLength;
280 }
281}
282
283template <typename T>
284TsTime
285Ts_UntypedEvalCache::_GetBezierPoint3Time(const Ts_TypedData<T>* kf1,
286 const Ts_TypedData<T>* kf2)
287{
288 // If the the first keyframe is held then the we treat the third bezier
289 // point as held too.
290 TsKnotType knotType = (kf1->_knotType == TsKnotHeld) ?
291 TsKnotHeld : kf2->_knotType;
292
293 switch (knotType) {
294 default:
295 case TsKnotHeld:
296 case TsKnotLinear:
297 return (kf1->GetTime() + 2.0 * kf2->GetTime()) / 3.0;
298
299 case TsKnotBezier:
300 return kf2->GetTime() - kf2->_leftTangentLength;
301 }
302}
303
304template <typename T>
305T
306Ts_UntypedEvalCache::_GetBezierPoint2Value(const Ts_TypedData<T>* kf1,
307 const Ts_TypedData<T>* kf2)
308{
309 switch (kf1->_knotType) {
310 default:
311 case TsKnotHeld:
312 return kf1->_GetRightValue();
313
314 case TsKnotLinear:
315 return (1.0 / 3.0) *
316 (2.0 * kf1->_GetRightValue() +
317 (kf2->_isDual ? kf2->_GetLeftValue() : kf2->_GetRightValue()));
318
319 case TsKnotBezier:
320 return kf1->_GetRightValue() +
321 kf1->_rightTangentLength * kf1->_GetRightTangentSlope();
322 }
323}
324
325template <typename T>
326T
327Ts_UntypedEvalCache::_GetBezierPoint3Value(const Ts_TypedData<T>* kf1,
328 const Ts_TypedData<T>* kf2)
329{
330 // If the first key frame is held then the we just use the first key frame's
331 // value
332 if (kf1->_knotType == TsKnotHeld) {
333 return kf1->_GetRightValue();
334 }
335
336 switch (kf2->_knotType) {
337 default:
338 case TsKnotHeld:
339 if (kf1->_knotType != TsKnotLinear) {
340 return kf2->_isDual ? kf2->_GetLeftValue() : kf2->_GetRightValue();
341 }
342 // Fall through to linear case if the first knot is linear
343 [[fallthrough]];
344
345 case TsKnotLinear:
346 return (1.0 / 3.0) *
347 (kf1->_GetRightValue() + 2.0 *
348 (kf2->_isDual ? kf2->_GetLeftValue() : kf2->_GetRightValue()));
349
350 case TsKnotBezier:
351 return (kf2->_isDual ? kf2->_GetLeftValue() : kf2->_GetRightValue()) -
352 kf2->_leftTangentLength * kf2->_GetLeftTangentSlope();
353 }
354}
355
356template <typename T>
357T
358Ts_UntypedEvalCache::_GetBezierPoint4Value(const Ts_TypedData<T>* kf1,
359 const Ts_TypedData<T>* kf2)
360{
361 // If the first knot is held then the last value is still the value of
362 // the first knot, otherwise it's the left side of the second knot
363 if (kf1->_knotType == TsKnotHeld) {
364 return kf1->_GetRightValue();
365 } else {
366 return (kf2->_isDual ? kf2->_GetLeftValue() : kf2->_GetRightValue());
367 }
368}
369
370template <typename T>
371void
372Ts_UntypedEvalCache::_SetupBezierGeometry(
373 TsTime* timePoints, T* valuePoints,
374 const Ts_TypedData<T>* kf1, const Ts_TypedData<T>* kf2)
375{
376 timePoints[0] = kf1->GetTime();
377 timePoints[1] = _GetBezierPoint2Time(kf1, kf2);
378 timePoints[2] = _GetBezierPoint3Time(kf1, kf2);
379 timePoints[3] = kf2->GetTime();
380 valuePoints[0] = kf1->_GetRightValue();
381 valuePoints[1] = _GetBezierPoint2Value(kf1, kf2);
382 valuePoints[2] = _GetBezierPoint3Value(kf1, kf2);
383 valuePoints[3] = _GetBezierPoint4Value(kf1, kf2);
384}
385
387// Ts_EvalCache non-interpolatable
388
389template <typename T>
390Ts_EvalCache<T, false>::Ts_EvalCache(const Ts_EvalCache<T, false> * rhs)
391{
392 _value = rhs->_value;
393}
394
395template <typename T>
396Ts_EvalCache<T, false>::Ts_EvalCache(const Ts_TypedData<T>* kf1,
397 const Ts_TypedData<T>* kf2)
398{
399 if (!kf1 || !kf2) {
400 TF_CODING_ERROR("Constructing an Ts_EvalCache from invalid keyframes");
401 return;
402 }
403
404 _value = kf1->_GetRightValue();
405}
406
407template <typename T>
408Ts_EvalCache<T, false>::Ts_EvalCache(const TsKeyFrame &kf1,
409 const TsKeyFrame &kf2)
410{
411 // Cast to the correct typed data. This is a private class, and we assume
412 // callers are passing only keyframes from the same spline, and correctly
413 // arranging our T to match.
414 Ts_TypedData<T> *data =
415 static_cast<Ts_TypedData<T> const*>(Ts_GetKeyFrameData(kf1));
416
417 _value = data->_GetRightValue();
418}
419
420template <typename T>
422Ts_EvalCache<T, false>::Eval(TsTime t) const {
423 return VtValue(TypedEval(t));
424}
425
426template <typename T>
428Ts_EvalCache<T, false>::EvalDerivative(TsTime t) const {
429 return VtValue(TypedEvalDerivative(t));
430}
431
432template <typename T>
433T
434Ts_EvalCache<T, false>::TypedEval(TsTime) const
435{
436 return _value;
437}
438
439template <typename T>
440T
441Ts_EvalCache<T, false>::TypedEvalDerivative(TsTime) const
442{
443 return TsTraits<T>::zero;
444}
445
447// Ts_EvalCache interpolatable
448
449template <typename T>
450Ts_EvalCache<T, true>::Ts_EvalCache(const Ts_EvalCache<T, true> * rhs)
451{
452 _interpolate = rhs->_interpolate;
453 _value = rhs->_value;
454 _cache = rhs->_cache;
455}
456
457template <typename T>
458Ts_EvalCache<T, true>::Ts_EvalCache(const Ts_TypedData<T>* kf1,
459 const Ts_TypedData<T>* kf2)
460{
461 _Init(kf1,kf2);
462}
463
464template <typename T>
465Ts_EvalCache<T, true>::Ts_EvalCache(const TsKeyFrame &kf1,
466 const TsKeyFrame &kf2)
467{
468 // Cast to the correct typed data. This is a private class, and we assume
469 // callers are passing only keyframes from the same spline, and correctly
470 // arranging our T to match.
471 _Init(static_cast<Ts_TypedData<T> const*>(Ts_GetKeyFrameData(kf1)),
472 static_cast<Ts_TypedData<T> const*>(Ts_GetKeyFrameData(kf2)));
473}
474
475template <typename T>
476void
477Ts_EvalCache<T, true>::_Init(
478 const Ts_TypedData<T>* kf1,
479 const Ts_TypedData<T>* kf2)
480{
481 if (!kf1 || !kf2) {
482 TF_CODING_ERROR("Constructing an Ts_EvalCache from invalid keyframes");
483 return;
484 }
485
486 // Curve for same knot types or left half of blend for different knot types
487 _SetupBezierGeometry(_cache.timePoints, _cache.valuePoints, kf1, kf2);
488 _cache.DerivePolynomial();
489
490 if (kf1->ValueCanBeInterpolated() && kf2->ValueCanBeInterpolated()) {
491 _interpolate = true;
492 } else {
493 _interpolate = false;
494 _value = kf1->_GetRightValue();
495 }
496}
497
498template <typename T>
500Ts_EvalCache<T, true>::Eval(TsTime t) const {
501 return VtValue(TypedEval(t));
502}
503
504template <typename T>
506Ts_EvalCache<T, true>::EvalDerivative(TsTime t) const {
507 return VtValue(TypedEvalDerivative(t));
508}
509
510template <typename T>
511T
512Ts_EvalCache<T, true>::TypedEval(TsTime time) const
513{
514 if (!_interpolate)
515 return _value;
516
517 double u = GfClamp(Ts_SolveCubic(_cache.timeCoeff, time), 0.0, 1.0);
518 return Ts_EvalCubic(_cache.valueCoeff, u);
519}
520
521template <typename T>
522T
523Ts_EvalCache<T, true>::TypedEvalDerivative(TsTime time) const
524{
525 if (!TsTraits<T>::supportsTangents || !_interpolate) {
526 return TsTraits<T>::zero;
527 }
528
529 // calculate the derivative as
530 // u = t^-1(time)
531 // dx(u)
532 // ----
533 // du dx(u)
534 // -------- = -----
535 // dt(u) dt(u)
536 // ----
537 // du
538 double u;
539 u = GfClamp(Ts_SolveCubic(_cache.timeCoeff, time), 0.0, 1.0);
540 T x = Ts_EvalCubicDerivative(_cache.valueCoeff, u);
541 TsTime t = Ts_EvalCubicDerivative(_cache.timeCoeff, u);
542 T derivative = x * (1.0 / t);
543 return derivative;
544}
545
546template <typename T>
547const Ts_Bezier<T>*
548Ts_EvalCache<T, true>::GetBezier() const
549{
550 return &_cache;
551}
552
553template <typename T>
554std::shared_ptr<Ts_EvalCache<T, true> >
555Ts_EvalCache<T, true>::New(const TsKeyFrame &kf1, const TsKeyFrame &kf2)
556{
557 // Cast to the correct typed data. This is a private class, and we assume
558 // callers are passing only keyframes from the same spline, and correctly
559 // arranging our T to match.
560 return static_cast<const Ts_TypedData<T>*>(
561 Ts_GetKeyFrameData(kf1))->
562 CreateTypedEvalCache(Ts_GetKeyFrameData(kf2));
563}
564
565template <typename T>
566std::shared_ptr<Ts_EvalCache<T, false> >
567Ts_EvalCache<T, false>::New(const TsKeyFrame &kf1, const TsKeyFrame &kf2)
568{
569 // Cast to the correct typed data. This is a private class, and we assume
570 // callers are passing only keyframes from the same spline, and correctly
571 // arranging our T to match.
572 return static_cast<const Ts_TypedData<T>*>(
573 Ts_GetKeyFrameData(kf1))->
574 CreateTypedEvalCache(Ts_GetKeyFrameData(kf2));
575}
576
578// Ts_EvalQuaternionCache
579
580template <typename T>
581Ts_EvalQuaternionCache<T>::Ts_EvalQuaternionCache(
582 const Ts_EvalQuaternionCache<T> * rhs)
583{
584 _kf1_knot_type = rhs->_kf1_knot_type;
585
586 _kf1_time = rhs->_kf1_time;
587 _kf2_time = rhs->_kf2_time;
588
589 _kf1_value = rhs->_kf1_value;
590 _kf2_value = rhs->_kf2_value;
591}
592
593template <typename T>
594Ts_EvalQuaternionCache<T>::Ts_EvalQuaternionCache(
595 const Ts_TypedData<T>* kf1, const Ts_TypedData<T>* kf2)
596{
597 _Init(kf1,kf2);
598}
599
600template <typename T>
601Ts_EvalQuaternionCache<T>::Ts_EvalQuaternionCache(const TsKeyFrame &kf1,
602 const TsKeyFrame &kf2)
603{
604 // Cast to the correct typed data. This is a private class, and we assume
605 // callers are passing only keyframes from the same spline, and correctly
606 // arranging our T to match.
607 _Init(static_cast<Ts_TypedData<T> const*>(Ts_GetKeyFrameData(kf1)),
608 static_cast<Ts_TypedData<T> const*>(Ts_GetKeyFrameData(kf2)));
609}
610
611template <typename T>
612void
613Ts_EvalQuaternionCache<T>::_Init(
614 const Ts_TypedData<T>* kf1,
615 const Ts_TypedData<T>* kf2)
616{
617 if (!kf1 || !kf2) {
618 TF_CODING_ERROR("Constructing an Ts_EvalQuaternionCache"
619 " from invalid keyframes");
620 return;
621 }
622
623 _kf1_knot_type = kf1->_knotType;
624
625 _kf1_time = kf1->GetTime();
626 _kf2_time = kf2->GetTime();
627
628 _kf1_value = kf1->_GetRightValue();
629 _kf2_value = kf2->_isDual ? kf2->_GetLeftValue() : kf2->_GetRightValue();
630}
631
632template <typename T>
634Ts_EvalQuaternionCache<T>::Eval(TsTime t) const {
635 return VtValue(TypedEval(t));
636}
637
638template <typename T>
639T Ts_EvalQuaternionCache<T>::TypedEval(TsTime time) const
640{
641 if (_kf1_knot_type == TsKnotHeld) {
642 return _kf1_value;
643 }
644
645 // XXX: do we want any snapping here? divide-by-zero avoidance?
646 // The following code was in Presto; not sure it belongs in Ts.
647 //
648 //if (fabs(_kf2_time - _kf1_time) < ARCH_MIN_FLOAT_EPS_SQR) {
649 // return _kf1_value;
650 //}
651
652 double u = (time - _kf1_time) / (_kf2_time - _kf1_time);
653 return GfSlerp(_kf1_value, _kf2_value, u);
654}
655
656template<typename T>
657VtValue Ts_EvalQuaternionCache<T>::EvalDerivative(TsTime t) const {
658 return VtValue(TypedEvalDerivative(t));
659}
660
661template<typename T>
662T Ts_EvalQuaternionCache<T>::TypedEvalDerivative(TsTime) const {
663 return TsTraits<T>::zero;
664}
665
666PXR_NAMESPACE_CLOSE_SCOPE
667
668#endif
Basic type: a quaternion, a complex number with a real coefficient and three imaginary coefficients,...
Definition: quatd.h:43
Basic type: a quaternion, a complex number with a real coefficient and three imaginary coefficients,...
Definition: quatf.h:43
Specifies the value of an TsSpline object at a particular point in time.
Definition: keyFrame.h:50
Provides a container which may hold any type, and provides introspection and iteration over array typ...
Definition: value.h:147
Assorted mathematical utility functions.
double GfClamp(double value, double min, double max)
Return the resulting of clamping value to lie between min and max.
Definition: math.h:139
#define TF_CODING_ERROR(fmt, args)
Issue an internal programming error, but continue execution.
Definition: diagnostic.h:68
GF_API GfQuatd GfSlerp(double alpha, const GfQuatd &q0, const GfQuatd &q1)
Spherically linearly interpolate between q0 and q1.
A file containing basic constants and definitions.