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