Loading...
Searching...
No Matches
data.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_DATA_H
9#define PXR_BASE_TS_DATA_H
10
11#include "pxr/pxr.h"
12#include "pxr/base/ts/api.h"
14#include "pxr/base/gf/math.h"
16#include "pxr/base/ts/evalCache.h"
17#include "pxr/base/ts/mathUtils.h"
18#include "pxr/base/ts/types.h"
19#include "pxr/base/vt/value.h"
20
21#include <string>
22#include <math.h>
23
24PXR_NAMESPACE_OPEN_SCOPE
25
26class Ts_PolymorphicDataHolder;
27
33class Ts_Data {
34public:
35 virtual ~Ts_Data() = default;
36 virtual void CloneInto(Ts_PolymorphicDataHolder *holder) const = 0;
37
38 // Create and return an EvalCache that represents the spline segment from
39 // this keyframe to kf2.
40 virtual std::shared_ptr<Ts_UntypedEvalCache> CreateEvalCache(
41 Ts_Data const* kf2) const = 0;
42
43 // Evaluate between this keyframe data and \p kf2 at \p time. This is
44 // useful for callers that do not otherwise want or need to create/retain an
45 // eval cache.
46 virtual VtValue
47 EvalUncached(Ts_Data const *kf2, TsTime time) const = 0;
48
49 // Evaluate the derivative between this keyframe data and \p kf2 at \p time.
50 // This is useful for callers that do not otherwise want or need to
51 // create/retain an eval cache.
52 virtual VtValue
53 EvalDerivativeUncached(Ts_Data const *kf2, TsTime time) const = 0;
54
55 virtual bool operator==(const Ts_Data &) const = 0;
56
57 // Time
58 inline TsTime GetTime() const {
59 return _time;
60 }
61 inline void SetTime(TsTime newTime) {
62 _time = newTime;
63 }
64
65 // Knot type
66 virtual TsKnotType GetKnotType() const = 0;
67 virtual void SetKnotType( TsKnotType knotType ) = 0;
68 virtual bool CanSetKnotType( TsKnotType knotType,
69 std::string *reason ) const = 0;
70
71 // Values
72 virtual VtValue GetValue() const = 0;
73 virtual void SetValue( VtValue val ) = 0;
74 virtual VtValue GetValueDerivative() const = 0;
75 virtual bool GetIsDualValued() const = 0;
76 virtual void SetIsDualValued( bool isDual ) = 0;
77 virtual VtValue GetLeftValue() const = 0;
78 virtual VtValue GetLeftValueDerivative() const = 0;
79 virtual void SetLeftValue( VtValue ) = 0;
80 virtual VtValue GetZero() const = 0;
81 virtual bool ValueCanBeInterpolated() const = 0;
82 virtual bool ValueCanBeExtrapolated() const = 0;
83
84 // Extrapolation.
85 // Note these methods don't actually use any data from this object
86 // and only depend on the spline type and the given parameters.
87 //
88 virtual VtValue GetSlope( const Ts_Data& ) const = 0;
89 virtual VtValue Extrapolate( const VtValue& value, TsTime dt,
90 const VtValue& slope) const = 0;
91
92 // Tangents
93
97 virtual bool HasTangents() const = 0;
98
102 // XXX: pixar-ism?
103 virtual bool ValueTypeSupportsTangents() const = 0;
104
105 virtual VtValue GetLeftTangentSlope() const = 0;
106 virtual VtValue GetRightTangentSlope() const = 0;
107 virtual TsTime GetLeftTangentLength() const = 0;
108 virtual TsTime GetRightTangentLength() const = 0;
109 virtual void SetLeftTangentSlope( VtValue ) = 0;
110 virtual void SetRightTangentSlope( VtValue ) = 0;
111 virtual void SetLeftTangentLength( TsTime ) = 0;
112 virtual void SetRightTangentLength( TsTime ) = 0;
113 virtual bool GetTangentSymmetryBroken() const = 0;
114 virtual void SetTangentSymmetryBroken( bool broken ) = 0;
115 virtual void ResetTangentSymmetryBroken() = 0;
116
117private:
118
119 TsTime _time = 0.0;
120};
121
122// Typed keyframe data class.
123template <typename T>
124class Ts_TypedData : public Ts_Data {
125public:
126 typedef T ValueType;
127 typedef Ts_TypedData<T> This;
128
129 Ts_TypedData(const T&);
130 Ts_TypedData(
131 const TsTime &t,
132 bool isDual,
133 const T& leftValue,
134 const T& rightValue,
135 const T& leftTangentSlope,
136 const T& rightTangentSlope);
137
138 ~Ts_TypedData() override = default;
139
140 void CloneInto(Ts_PolymorphicDataHolder *holder) const override;
141
142 // Create a untyped eval cache for the segment defined by ourself and the
143 // given keyframe.
144 std::shared_ptr<Ts_UntypedEvalCache> CreateEvalCache(
145 Ts_Data const* kf2) const override;
146
147 // Evaluate between this keyframe data and \p kf2 at \p time. This is
148 // useful for callers that do not otherwise want or need to create/retain an
149 // eval cache.
150 VtValue EvalUncached(
151 Ts_Data const *kf2, TsTime time) const override;
152
153 // Evaluate the derivative between this keyframe data and \p kf2 at \p time.
154 // This is useful for callers that do not otherwise want or need to
155 // create/retain an eval cache.
156 VtValue EvalDerivativeUncached(
157 Ts_Data const *kf2, TsTime time) const override;
158
159 // Create a typed eval cache for the segment defined by ourself and the
160 // given keyframe.
161 std::shared_ptr<Ts_EvalCache<T,
162 TsTraits<T>::interpolatable> > CreateTypedEvalCache(
163 Ts_Data const* kf2) const;
164
165 bool operator==(const Ts_Data &) const override;
166
167 // Knot type
168 TsKnotType GetKnotType() const override;
169 void SetKnotType( TsKnotType knotType ) override;
170 bool CanSetKnotType(
171 TsKnotType knotType, std::string *reason ) const override;
172
173 // Values
174 VtValue GetValue() const override;
175 void SetValue( VtValue ) override;
176 VtValue GetValueDerivative() const override;
177 bool GetIsDualValued() const override;
178 void SetIsDualValued( bool isDual ) override;
179 VtValue GetLeftValue() const override;
180 VtValue GetLeftValueDerivative() const override;
181 void SetLeftValue( VtValue ) override;
182 VtValue GetZero() const override;
183 bool ValueCanBeInterpolated() const override;
184 bool ValueCanBeExtrapolated() const override;
185
186 // Tangents
187 bool HasTangents() const override;
188 bool ValueTypeSupportsTangents() const override;
189 VtValue GetLeftTangentSlope() const override;
190 VtValue GetRightTangentSlope() const override;
191 TsTime GetLeftTangentLength() const override;
192 TsTime GetRightTangentLength() const override;
193 void SetLeftTangentSlope( VtValue ) override;
194 void SetRightTangentSlope( VtValue ) override;
195 void SetLeftTangentLength( TsTime ) override;
196 void SetRightTangentLength( TsTime ) override;
197 bool GetTangentSymmetryBroken() const override;
198 void SetTangentSymmetryBroken( bool broken ) override;
199 void ResetTangentSymmetryBroken() override;
200
201public:
202
203 // Slope computation methods.
204
205 VtValue GetSlope(const Ts_Data &right) const override
206 {
207 if constexpr (TsTraits<T>::extrapolatable)
208 {
209 const TsTime dx = right.GetTime() - GetTime();
210 const TsTime dxInv = 1.0 / dx;
211
212 const T y1 = GetValue().template Get<T>();
213 const T y2 = right.GetLeftValue().template Get<T>();
214 const T dy = y2 - y1;
215
216 // This is effectively dy/dx, but some types lack operator/, so
217 // phrase in terms of operator*.
218 const T slope = dy * dxInv;
219 return VtValue(slope);
220 }
221 else
222 {
223 return VtValue(TsTraits<T>::zero);
224 }
225 }
226
227 VtValue Extrapolate(
228 const VtValue &value, TsTime dt, const VtValue &slope) const override
229 {
230 if constexpr (TsTraits<T>::extrapolatable)
231 {
232 const T v = value.template Get<T>();
233 const T s = slope.template Get<T>();
234 const T result = v + dt * s;
235 return VtValue(result);
236 }
237 else
238 {
239 return value;
240 }
241 }
242
243private:
244
245 // Convenience accessors for the data stored inside the _Values struct.
246
247 T const& _GetRightValue() const {
248 return _values.Get()._rhv;
249 }
250 T const& _GetLeftValue() const {
251 return _values.Get()._lhv;
252 }
253 T const& _GetRightTangentSlope() const {
254 return _values.Get()._rightTangentSlope;
255 }
256 T const& _GetLeftTangentSlope() const {
257 return _values.Get()._leftTangentSlope;
258 }
259
260 void _SetRightValue(T const& rhv) {
261 _values.GetMutable()._rhv = rhv;
262 }
263 void _SetLeftValue(T const& lhv) {
264 _values.GetMutable()._lhv = lhv;
265 }
266 void _SetRightTangentSlope(T const& rightTangentSlope) {
267 _values.GetMutable()._rightTangentSlope = rightTangentSlope;
268 }
269 void _SetLeftTangentSlope(T const& leftTangentSlope) {
270 _values.GetMutable()._leftTangentSlope = leftTangentSlope;
271 }
272
273private:
274 friend class TsKeyFrame;
275 friend class Ts_UntypedEvalCache;
276 friend class Ts_EvalQuaternionCache<T>;
277 friend class Ts_EvalCache<T, TsTraits<T>::interpolatable>;
278
279 // A struct containing all the member variables that depend on type T.
280 template <class V>
281 struct _Values {
282
283 explicit _Values(
284 V const& lhv=TsTraits<T>::zero,
285 V const& rhv=TsTraits<T>::zero,
286 V const& leftTangentSlope=TsTraits<T>::zero,
287 V const& rightTangentSlope=TsTraits<T>::zero) :
288 _lhv(lhv),
289 _rhv(rhv),
290 _leftTangentSlope(leftTangentSlope),
291 _rightTangentSlope(rightTangentSlope)
292 {
293 }
294
295 // Left and right hand values.
296 // Single-value knots only use _rhv; dual-value knots use both.
297 V _lhv, _rhv;
298
299 // Tangent slope, or derivative, in units per frame.
300 V _leftTangentSlope, _rightTangentSlope;
301 };
302
303 // A wrapper for _Values with small-object optimization. The _ValuesHolder
304 // object is always the same size. If T is sizeof(double) or smaller, the
305 // _Values struct is held in member data. If T is larger than double, the
306 // struct is heap-allocated.
307 class _ValuesHolder
308 {
309 private:
310 static constexpr size_t _size = sizeof(_Values<double>);
311 static constexpr bool _isSmall = (sizeof(_Values<T>) <= _size);
312
313 // Storage implementation for small types.
314 struct _LocalStorage
315 {
316 _LocalStorage(_Values<T> &&values)
317 : _data(std::move(values)) {}
318
319 const _Values<T>& Get() const { return _data; }
320 _Values<T>& GetMutable() { return _data; }
321
322 _Values<T> _data;
323 };
324
325 // Storage implementation for large types.
326 struct _HeapStorage
327 {
328 _HeapStorage(_Values<T> &&values)
329 : _data(new _Values<T>(std::move(values))) {}
330
331 // Copy constructor: deep-copies data.
332 _HeapStorage(const _HeapStorage &other)
333 : _data(new _Values<T>(other.Get())) {}
334
335 const _Values<T>& Get() const { return *_data; }
336 _Values<T>& GetMutable() { return *_data; }
337
338 std::unique_ptr<_Values<T>> _data;
339 };
340
341 // Select storage implementation.
342 using _Storage =
343 typename std::conditional<
344 _isSmall, _LocalStorage, _HeapStorage>::type;
345
346 public:
347 // Construct from _Values rvalue.
348 explicit _ValuesHolder(_Values<T> &&values)
349 : _storage(std::move(values)) {}
350
351 // Copy constructor.
352 _ValuesHolder(const _ValuesHolder &other)
353 : _storage(other._storage) {}
354
355 // Destructor: explicitly call _Storage destructor.
356 ~_ValuesHolder() { _storage.~_Storage(); }
357
358 // Accessors.
359 const _Values<T>& Get() const { return _storage.Get(); }
360 _Values<T>& GetMutable() { return _storage.GetMutable(); }
361
362 private:
363 union
364 {
365 _Storage _storage;
366 char _padding[_size];
367 };
368 };
369
370 // Sanity check: every instantiation of _ValuesHolder is the same size.
371 static_assert(
372 sizeof(_ValuesHolder) == sizeof(_Values<double>),
373 "_ValuesHolder does not have expected type-independent size");
374
375private:
376 _ValuesHolder _values;
377
378 // Tangent length, in frames.
379 TsTime _leftTangentLength, _rightTangentLength;
380
381 TsKnotType _knotType;
382 bool _isDual;
383 bool _tangentSymmetryBroken;
384};
385
386// A wrapper for Ts_TypedData<T> for arbitrary T, exposed as a pointer to the
387// non-templated base class Ts_Data, but allocated in member data rather than
388// on the heap.
389class Ts_PolymorphicDataHolder
390{
391public:
392 // Wrapper for held-knot-at-time-zero constructor.
393 template <typename T>
394 void New(const T &val)
395 {
396 new (&_storage) Ts_TypedData<T>(val);
397 }
398
399 // Wrapper for general constructor.
400 template <typename T>
401 void New(
402 const TsTime &t,
403 bool isDual,
404 const T &leftValue,
405 const T &rightValue,
406 const T &leftTangentSlope,
407 const T &rightTangentSlope)
408 {
409 new (&_storage) Ts_TypedData<T>(
410 t, isDual, leftValue, rightValue,
411 leftTangentSlope, rightTangentSlope);
412 }
413
414 // Copy constructor.
415 template <typename T>
416 void New(const Ts_TypedData<T> &other)
417 {
418 new (&_storage) Ts_TypedData<T>(other);
419 }
420
421 // Explicit destructor. Clients call this method from their destructors,
422 // and prior to calling New to replace an existing knot.
423 void Destroy()
424 {
425 reinterpret_cast<Ts_Data*>(&_storage)->~Ts_Data();
426 }
427
428 // Const accessor.
429 const Ts_Data* Get() const
430 {
431 return reinterpret_cast<const Ts_Data*>(&_storage);
432 }
433
434 // Non-const accessor.
435 Ts_Data* GetMutable()
436 {
437 return reinterpret_cast<Ts_Data*>(&_storage);
438 }
439
440private:
441 // Our buffer is sized for Ts_TypedData<T>. This is always the same size
442 // regardless of T; see Ts_TypedData::_ValuesHolder.
443 using _Storage =
444 typename std::aligned_storage<
445 sizeof(Ts_TypedData<double>), sizeof(void*)>::type;
446
447private:
448 _Storage _storage;
449};
450
452// Ts_TypedData
453
454template <typename T>
455Ts_TypedData<T>::Ts_TypedData(const T& value) :
456 _values(_Values<T>(value,value)),
457 _leftTangentLength(0.0),
458 _rightTangentLength(0.0),
459 _knotType(TsKnotHeld),
460 _isDual(false),
461 _tangentSymmetryBroken(false)
462{
463}
464
465template <typename T>
466Ts_TypedData<T>::Ts_TypedData(
467 const TsTime &t,
468 bool isDual,
469 const T& leftValue,
470 const T& rightValue,
471 const T& leftTangentSlope,
472 const T& rightTangentSlope) :
473 _values(_Values<T>(leftValue,rightValue,
474 leftTangentSlope,rightTangentSlope)),
475 _leftTangentLength(0.0),
476 _rightTangentLength(0.0),
477 _knotType(TsKnotHeld),
478 _isDual(isDual),
479 _tangentSymmetryBroken(false)
480{
481 SetTime(t);
482}
483
484template <typename T>
485void
486Ts_TypedData<T>::CloneInto(Ts_PolymorphicDataHolder *holder) const
487{
488 holder->New(*this);
489}
490
491template <typename T>
492std::shared_ptr<Ts_UntypedEvalCache>
493Ts_TypedData<T>::CreateEvalCache(Ts_Data const* kf2) const
494{
495 // Cast kf2 to the correct typed data. This is a private class, and we
496 // assume kf2 is from the same spline, so it will have the same value type.
497 Ts_TypedData<T> const* typedKf2 =
498 static_cast<Ts_TypedData<T> const*>(kf2);
499
500 // Construct and return a new EvalCache of the appropriate type.
501 return std::make_shared<
502 Ts_EvalCache<T, TsTraits<T>::interpolatable>>(this, typedKf2);
503}
504
505template <typename T>
506std::shared_ptr<Ts_EvalCache<T, TsTraits<T>::interpolatable> >
507Ts_TypedData<T>::CreateTypedEvalCache(Ts_Data const* kf2) const
508{
509 Ts_TypedData<T> const* typedKf2 =
510 static_cast<Ts_TypedData<T> const*>(kf2);
511
512 return std::shared_ptr<Ts_EvalCache<T, TsTraits<T>::interpolatable> >(
513 new Ts_EvalCache<T, TsTraits<T>::interpolatable>(this, typedKf2));
514}
515
516template <typename T>
518Ts_TypedData<T>
519::EvalUncached(Ts_Data const *kf2, TsTime time) const
520{
521 // Cast kf2 to the correct typed data. This is a private class, and we
522 // assume kf2 is from the same spline, so it will have the same value type.
523 Ts_TypedData<T> const* typedKf2 =
524 static_cast<Ts_TypedData<T> const*>(kf2);
525
526 return Ts_EvalCache<T, TsTraits<T>::interpolatable>(this, typedKf2)
527 .Eval(time);
528}
529
530template <typename T>
532Ts_TypedData<T>
533::EvalDerivativeUncached(Ts_Data const *kf2, TsTime time) const
534{
535 // Cast kf2 to the correct typed data. This is a private class, and we
536 // assume kf2 is from the same spline, so it will have the same value type.
537 Ts_TypedData<T> const* typedKf2 =
538 static_cast<Ts_TypedData<T> const*>(kf2);
539
540 return Ts_EvalCache<T, TsTraits<T>::interpolatable>(this, typedKf2)
541 .EvalDerivative(time);
542}
543
544template <typename T>
545bool
546Ts_TypedData<T>::operator==(const Ts_Data &rhs) const
547{
548 if (!TsTraits<T>::supportsTangents) {
549 return
550 GetKnotType() == rhs.GetKnotType() &&
551 GetTime() == rhs.GetTime() &&
552 GetValue() == rhs.GetValue() &&
553 GetIsDualValued() == rhs.GetIsDualValued() &&
554 (!GetIsDualValued() || (GetLeftValue() == rhs.GetLeftValue()));
555 }
556
557 return
558 GetTime() == rhs.GetTime() &&
559 GetValue() == rhs.GetValue() &&
560 GetKnotType() == rhs.GetKnotType() &&
561 GetIsDualValued() == rhs.GetIsDualValued() &&
562 (!GetIsDualValued() || (GetLeftValue() == rhs.GetLeftValue())) &&
563 GetLeftTangentLength() == rhs.GetLeftTangentLength() &&
564 GetRightTangentLength() == rhs.GetRightTangentLength() &&
565 GetLeftTangentSlope() == rhs.GetLeftTangentSlope() &&
566 GetRightTangentSlope() == rhs.GetRightTangentSlope() &&
567 GetTangentSymmetryBroken() == rhs.GetTangentSymmetryBroken();
568}
569
570template <typename T>
571TsKnotType
572Ts_TypedData<T>::GetKnotType() const
573{
574 return _knotType;
575}
576
577template <typename T>
578void
579Ts_TypedData<T>::SetKnotType( TsKnotType knotType )
580{
581 std::string reason;
582
583 if (!CanSetKnotType(knotType, &reason)) {
584 TF_CODING_ERROR(reason);
585 return;
586 }
587
588 _knotType = knotType;
589}
590
591template <typename T>
592bool
593Ts_TypedData<T>::CanSetKnotType( TsKnotType knotType,
594 std::string *reason ) const
595{
596 // Non-interpolatable values can only have held key frames.
597 if (!ValueCanBeInterpolated() && knotType != TsKnotHeld) {
598 if (reason) {
599 *reason = "Value cannot be interpolated; only 'held' " \
600 "key frames are allowed.";
601 }
602 return false;
603 }
604
605 // Only value types that support tangents can have bezier key frames.
606 if (!TsTraits<T>::supportsTangents && knotType == TsKnotBezier) {
607 if (reason) {
608 *reason = TfStringPrintf(
609 "Cannot set keyframe type %s; values of type '%s' "
610 "do not support tangents.",
611 TfEnum::GetDisplayName(knotType).c_str(),
612 ArchGetDemangled(typeid(ValueType)).c_str());
613 }
614 return false;
615 }
616
617 return true;
618}
619
620template <typename T>
622Ts_TypedData<T>::GetValue() const
623{
624 return VtValue(_GetRightValue());
625}
626
627template <typename T>
629Ts_TypedData<T>::GetValueDerivative() const
630{
631 if (TsTraits<T>::supportsTangents) {
632 return GetRightTangentSlope();
633 } else {
634 return VtValue(TsTraits<T>::zero);
635 }
636}
637
638template <typename T>
639void
640Ts_TypedData<T>::SetValue( VtValue val )
641{
642 VtValue v = val.Cast<T>();
643 if (!v.IsEmpty()) {
644 _SetRightValue(v.Get<T>());
645 if (!ValueCanBeInterpolated())
646 SetKnotType(TsKnotHeld);
647 } else {
648 TF_CODING_ERROR("cannot convert type '%s' to '%s' to assign "
649 "to keyframe", val.GetTypeName().c_str(),
650 ArchGetDemangled(typeid(ValueType)).c_str());
651 }
652}
653
654template <typename T>
655bool
656Ts_TypedData<T>::GetIsDualValued() const
657{
658 return _isDual;
659}
660
661template <typename T>
662void
663Ts_TypedData<T>::SetIsDualValued( bool isDual )
664{
665 if (isDual && !TsTraits<T>::interpolatable) {
666 TF_CODING_ERROR("keyframes of type '%s' cannot be dual-valued",
667 ArchGetDemangled(typeid(ValueType)).c_str());
668 return;
669 }
670
671 _isDual = isDual;
672
673 if (_isDual) {
674 // The data stored for the left value was meaningless.
675 // Mirror the right-side value to the left.
676 SetLeftValue(GetValue());
677 }
678}
679
680template <typename T>
681VtValue
682Ts_TypedData<T>::GetLeftValue() const
683{
684 return VtValue(_isDual ? _GetLeftValue() : _GetRightValue());
685}
686
687template <typename T>
688VtValue
689Ts_TypedData<T>::GetLeftValueDerivative() const
690{
691 if (TsTraits<T>::supportsTangents) {
692 return GetLeftTangentSlope();
693 } else {
694 return VtValue(TsTraits<T>::zero);
695 }
696}
697
698template <typename T>
699void
700Ts_TypedData<T>::SetLeftValue( VtValue val )
701{
702 if (!TsTraits<T>::interpolatable) {
703 TF_CODING_ERROR("keyframes of type '%s' cannot be dual-valued",
704 ArchGetDemangled(typeid(ValueType)).c_str() );
705 return;
706 }
707 if (!GetIsDualValued()) {
708 TF_CODING_ERROR("keyframe is not dual-valued; cannot set left value");
709 return;
710 }
711
712 VtValue v = val.Cast<T>();
713 if (!v.IsEmpty()) {
714 _SetLeftValue(v.Get<T>());
715 if (!ValueCanBeInterpolated())
716 SetKnotType(TsKnotHeld);
717 } else {
718 TF_CODING_ERROR("cannot convert type '%s' to '%s' to assign to "
719 "keyframe", val.GetTypeName().c_str(),
720 ArchGetDemangled(typeid(ValueType)).c_str());
721 }
722}
723
724template <typename T>
726Ts_TypedData<T>::GetZero() const
727{
728 return VtValue(TsTraits<T>::zero);
729}
730
731template <typename T>
732bool
733Ts_TypedData<T>::ValueCanBeInterpolated() const
734{
735 return TsTraits<T>::interpolatable;
736}
737
738template <typename T>
739bool
740Ts_TypedData<T>::ValueCanBeExtrapolated() const
741{
742 return TsTraits<T>::extrapolatable;
743}
744
745template <typename T>
746bool
747Ts_TypedData<T>::HasTangents() const
748{
749 return TsTraits<T>::supportsTangents && _knotType == TsKnotBezier;
750}
751
752template <typename T>
753bool
754Ts_TypedData<T>::ValueTypeSupportsTangents() const
755{
756 // Oddly, linear and held knots have settable tangents. Animators use
757 // this when switching Beziers to Held and then back again.
758 return TsTraits<T>::supportsTangents;
759}
760
761template <typename T>
763Ts_TypedData<T>::GetLeftTangentSlope() const
764{
765 if (!TsTraits<T>::supportsTangents) {
766 TF_CODING_ERROR("keyframes of type '%s' do not have tangents",
767 ArchGetDemangled(typeid(ValueType)).c_str());
768 return VtValue();
769 }
770
771 return VtValue(_GetLeftTangentSlope());
772}
773
774template <typename T>
776Ts_TypedData<T>::GetRightTangentSlope() const
777{
778 if (!TsTraits<T>::supportsTangents) {
779 TF_CODING_ERROR("keyframes of type '%s' do not have tangents",
780 ArchGetDemangled(typeid(ValueType)).c_str() );
781 return VtValue();
782 }
783
784 return VtValue(_GetRightTangentSlope());
785}
786
787template <typename T>
788TsTime
789Ts_TypedData<T>::GetLeftTangentLength() const
790{
791 if (!TsTraits<T>::supportsTangents) {
792 TF_CODING_ERROR("keyframes of type '%s' do not have tangents",
793 ArchGetDemangled(typeid(ValueType)).c_str());
794 return 0;
795 }
796
797 return _leftTangentLength;
798}
799
800template <typename T>
801TsTime
802Ts_TypedData<T>::GetRightTangentLength() const
803{
804 if (!TsTraits<T>::supportsTangents) {
805 TF_CODING_ERROR("keyframes of type '%s' do not have tangents",
806 ArchGetDemangled(typeid(ValueType)).c_str());
807 return 0;
808 }
809
810 return _rightTangentLength;
811}
812
813template <typename T>
814void
815Ts_TypedData<T>::SetLeftTangentSlope( VtValue val )
816{
817 if (!TsTraits<T>::supportsTangents) {
818 TF_CODING_ERROR("keyframes of type '%s' do not have tangents",
819 ArchGetDemangled(typeid(ValueType)).c_str());
820 return;
821 }
822
823 VtValue v = val.Cast<T>();
824 if (!v.IsEmpty()) {
825 _SetLeftTangentSlope(val.Get<T>());
826 } else {
827 TF_CODING_ERROR("cannot convert type '%s' to '%s' to assign to "
828 "keyframe", val.GetTypeName().c_str(),
829 ArchGetDemangled(typeid(ValueType)).c_str());
830 }
831}
832
833template <typename T>
834void
835Ts_TypedData<T>::SetRightTangentSlope( VtValue val )
836{
837 if (!TsTraits<T>::supportsTangents) {
838 TF_CODING_ERROR("keyframes of type '%s' do not have tangents",
839 ArchGetDemangled(typeid(ValueType)).c_str());
840 return;
841 }
842
843 VtValue v = val.Cast<T>();
844 if (!v.IsEmpty()) {
845 _SetRightTangentSlope(val.Get<T>());
846 } else {
847 TF_CODING_ERROR("cannot convert type '%s' to '%s' to assign to keyframe"
848 , val.GetTypeName().c_str(),
849 ArchGetDemangled(typeid(ValueType)).c_str());
850 }
851}
852
853#define TS_LENGTH_EPSILON 1e-6
854
855template <typename T>
856void
857Ts_TypedData<T>::SetLeftTangentLength( TsTime newLen )
858{
859 if (!TsTraits<T>::supportsTangents) {
860 TF_CODING_ERROR( "keyframes of type '%s' do not have tangents",
861 ArchGetDemangled(typeid(ValueType)).c_str());
862 return;
863 }
864 if (std::isnan(newLen)) {
865 TF_CODING_ERROR("Cannot set tangent length to NaN; ignoring");
866 return;
867 }
868 if (std::isinf(newLen)) {
869 TF_CODING_ERROR("Cannot set tangent length to inf; ignoring");
870 return;
871 }
872 if (newLen < 0.0) {
873 if (-newLen < TS_LENGTH_EPSILON) {
874 newLen = 0.0;
875 } else {
877 "Cannot set tangent length to negative value; ignoring");
878 return;
879 }
880 }
881
882 _leftTangentLength = newLen;
883}
884
885template <typename T>
886void
887Ts_TypedData<T>::SetRightTangentLength( TsTime newLen )
888{
889 if (!TsTraits<T>::supportsTangents) {
890 TF_CODING_ERROR("keyframes of type '%s' do not have tangents",
891 ArchGetDemangled(typeid(ValueType)).c_str());
892 return;
893 }
894 if (std::isnan(newLen)) {
895 TF_CODING_ERROR("Cannot set tangent length to NaN; ignoring");
896 return;
897 }
898 if (std::isinf(newLen)) {
899 TF_CODING_ERROR("Cannot set tangent length to inf; ignoring");
900 return;
901 }
902 if (newLen < 0.0) {
903 if (-newLen < TS_LENGTH_EPSILON) {
904 newLen = 0.0;
905 } else {
907 "Cannot set tangent length to negative value; ignoring");
908 return;
909 }
910 }
911
912 _rightTangentLength = newLen;
913}
914
915template <typename T>
916bool
917Ts_TypedData<T>::GetTangentSymmetryBroken() const
918{
919 if (!TsTraits<T>::supportsTangents) {
920 TF_CODING_ERROR("keyframes of type '%s' do not have tangents",
921 ArchGetDemangled(typeid(ValueType)).c_str());
922 return false;
923 }
924
925 return _tangentSymmetryBroken;
926}
927
928template <typename T>
929void
930Ts_TypedData<T>::SetTangentSymmetryBroken( bool broken )
931{
932 if (!TsTraits<T>::supportsTangents) {
933 TF_CODING_ERROR("keyframes of type '%s' do not have tangents",
934 ArchGetDemangled(typeid(ValueType)).c_str());
935 return;
936 }
937
938 if (_tangentSymmetryBroken != broken) {
939 _tangentSymmetryBroken = broken;
940 if (!_tangentSymmetryBroken) {
941 _SetLeftTangentSlope(_GetRightTangentSlope());
942 }
943 }
944}
945
946template <typename T>
947void
948Ts_TypedData<T>::ResetTangentSymmetryBroken()
949{
950 // do nothing -- no tangents
951}
952
953// Declare specializations for float and double.
954// Definitions are in Data.cpp.
955template <>
956TS_API void
957Ts_TypedData<float>::ResetTangentSymmetryBroken();
958
959template <>
960TS_API void
961Ts_TypedData<double>::ResetTangentSymmetryBroken();
962
963template <>
964TS_API bool
965Ts_TypedData<float>::ValueCanBeInterpolated() const;
966
967template <>
968TS_API bool
969Ts_TypedData<double>::ValueCanBeInterpolated() const;
970
971PXR_NAMESPACE_CLOSE_SCOPE
972
973#endif
Low-level utilities for informing users of various internal and external diagnostic conditions.
static TF_API std::string GetDisplayName(TfEnum val)
Returns the display name for an enumerated value.
Holds the data for an TsKeyFrame.
Definition: data.h:33
virtual bool ValueTypeSupportsTangents() const =0
If true, implies the tangents can be written.
virtual bool HasTangents() const =0
True if the data type supports tangents, and the knot type is one that shows tangents in the UI.
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
VT_API std::string GetTypeName() const
Return the type name of the held typeid.
bool IsEmpty() const
Returns true iff this value is empty.
Definition: value.h:1283
static VtValue Cast(VtValue const &val)
Return a VtValue holding val cast to hold T.
Definition: value.h:1187
T const & Get() const &
Returns a const reference to the held object if the held object is of type T.
Definition: value.h:1120
Demangle C++ typenames generated by the typeid() facility.
Assorted mathematical utility functions.
std::string ArchGetDemangled()
Return demangled RTTI generated-type name.
Definition: demangle.h:86
#define TF_CODING_ERROR(fmt, args)
Issue an internal programming error, but continue execution.
Definition: diagnostic.h:68
TF_API std::string TfStringPrintf(const char *fmt,...)
Returns a string formed by a printf()-like specification.
STL namespace.