8#ifndef PXR_BASE_TS_SPLINE_DATA_H
9#define PXR_BASE_TS_SPLINE_DATA_H
12#include "pxr/base/ts/api.h"
13#include "pxr/base/ts/knotData.h"
14#include "pxr/base/ts/types.h"
15#include "pxr/base/ts/typeHelpers.h"
18#include "pxr/base/tf/type.h"
22#include <unordered_map>
28PXR_NAMESPACE_OPEN_SCOPE
47 static Ts_SplineData* Create(
49 const Ts_SplineData *overallParamSource =
nullptr);
51 TS_API
virtual ~Ts_SplineData();
56 virtual TfType GetValueType()
const = 0;
57 virtual size_t GetKnotStructSize()
const = 0;
58 virtual Ts_SplineData* Clone()
const = 0;
60 virtual bool operator==(
const Ts_SplineData &other)
const = 0;
62 virtual void ReserveForKnotCount(
size_t count) = 0;
63 virtual void PushKnot(
64 const Ts_KnotData *knotData,
74 virtual size_t SetKnot(
75 const Ts_KnotData *knotData,
80 virtual size_t SetKnotFromDouble(
81 const Ts_TypedKnotData<double>* knotData,
84 virtual Ts_KnotData* CloneKnotAtIndex(
size_t index)
const = 0;
85 virtual Ts_KnotData* CloneKnotAtTime(TsTime time)
const = 0;
86 virtual Ts_KnotData* GetKnotPtrAtIndex(
size_t index) = 0;
87 virtual const Ts_KnotData* GetKnotPtrAtIndex(
size_t index)
const = 0;
88 virtual Ts_TypedKnotData<double>
89 GetKnotDataAsDouble(
size_t index)
const = 0;
90 virtual double GetKnotValueAsDouble(
size_t index)
const = 0;
91 virtual double GetKnotPreValueAsDouble(
size_t index)
const = 0;
93 virtual void ClearKnots() = 0;
94 virtual void RemoveKnotAtTime(TsTime time) = 0;
96 virtual void ApplyOffsetAndScale(
100 virtual bool HasValueBlocks()
const = 0;
101 virtual bool HasValueBlockAtTime(TsTime time)
const = 0;
103 virtual bool UpdateKnotTangentsAtIndex(
size_t index) = 0;
111 size_t *firstProtoIndexOut =
nullptr)
const;
117 TsTime GetPreExtrapTime()
const;
123 TsTime GetPostExtrapTime()
const;
129 double GetPreExtrapValue()
const;
135 double GetPostExtrapValue()
const;
155 TsCurveType curveType : 2;
159 Ts_LoopParams loopParams;
167 std::vector<TsTime> times;
170 std::unordered_map<TsTime, VtDictionary> customData;
177struct Ts_TypedSplineData final :
181 TfType GetValueType()
const override;
182 size_t GetKnotStructSize()
const override;
183 Ts_SplineData* Clone()
const override;
185 bool operator==(
const Ts_SplineData &other)
const override;
187 void ReserveForKnotCount(
size_t count)
override;
189 const Ts_KnotData *knotData,
197 const Ts_KnotData *knotData,
202 size_t SetKnotFromDouble(
203 const Ts_TypedKnotData<double>* knotData,
206 Ts_KnotData* CloneKnotAtIndex(
size_t index)
const override;
207 Ts_KnotData* CloneKnotAtTime(TsTime time)
const override;
208 Ts_KnotData* GetKnotPtrAtIndex(
size_t index)
override;
209 const Ts_KnotData* GetKnotPtrAtIndex(
size_t index)
const override;
210 Ts_TypedKnotData<double>
211 GetKnotDataAsDouble(
size_t index)
const override;
212 double GetKnotValueAsDouble(
size_t index)
const override;
213 double GetKnotPreValueAsDouble(
size_t index)
const override;
215 void ClearKnots()
override;
216 void RemoveKnotAtTime(TsTime time)
override;
225 void ApplyOffsetAndScale(
227 double scale)
override;
229 bool HasValueBlocks()
const override;
230 bool HasValueBlockAtTime(TsTime time)
const override;
232 bool UpdateKnotTangentsAtIndex(
size_t index)
override;
236 std::vector<Ts_TypedKnotData<T>> knots;
247Ts_GetSplineData(
const TsSpline &spline);
250Ts_TypedSplineData<T>*
251Ts_GetTypedSplineData(
TsSpline &spline);
254const Ts_TypedSplineData<T>*
255Ts_GetTypedSplineData(
const TsSpline &spline);
262TfType Ts_TypedSplineData<T>::GetValueType()
const
273size_t Ts_TypedSplineData<T>::GetKnotStructSize()
const
275 return sizeof(Ts_TypedKnotData<T>);
280Ts_TypedSplineData<T>::Clone()
const
282 return new Ts_TypedSplineData<T>(*
this);
286bool Ts_TypedSplineData<T>::operator==(
287 const Ts_SplineData &other)
const
290 if (isTyped != other.isTyped
291 || curveType != other.curveType
292 || preExtrapolation != other.preExtrapolation
293 || postExtrapolation != other.postExtrapolation
294 || loopParams != other.loopParams
295 || customData != other.customData)
302 const Ts_TypedSplineData<T>*
const typedOther =
303 dynamic_cast<const Ts_TypedSplineData<T>*
>(&other);
310 return knots == typedOther->knots;
314void Ts_TypedSplineData<T>::ReserveForKnotCount(
317 times.reserve(count);
318 knots.reserve(count);
322void Ts_TypedSplineData<T>::PushKnot(
323 const Ts_KnotData*
const knotData,
326 const Ts_TypedKnotData<T>*
const typedKnotData =
327 static_cast<const Ts_TypedKnotData<T>*
>(knotData);
329 times.push_back(knotData->time);
330 knots.push_back(*typedKnotData);
332 if (!customDataIn.
empty())
334 customData[knotData->time] = customDataIn;
378size_t Ts_TypedSplineData<T>::SetKnot(
379 const Ts_KnotData*
const knotData,
382 const Ts_TypedKnotData<T>*
const typedKnotData =
383 static_cast<const Ts_TypedKnotData<T>*
>(knotData);
387 std::lower_bound(times.begin(), times.end(), knotData->time);
390 const bool overwrite =
391 (it != times.end() && *it == knotData->time);
396 times[idx] = knotData->time;
397 knots[idx] = *typedKnotData;
401 times.insert(it, knotData->time);
402 knots.insert(knots.begin() + idx, *typedKnotData);
406 if (!customDataIn.
empty())
408 customData[knotData->time] = customDataIn;
415size_t Ts_TypedSplineData<T>::SetKnotFromDouble(
416 const Ts_TypedKnotData<double>* knotData,
420 if constexpr(std::is_same_v<T, double>) {
421 return SetKnot(knotData, customDataIn);
424 Ts_TypedKnotData<T> typedData;
428 static_cast<Ts_KnotData&
>(typedData) =
429 static_cast<const Ts_KnotData&
>(*knotData);
437 return std::min(T(v), std::numeric_limits<T>::max());
439 return std::max(T(v), std::numeric_limits<T>::lowest());
444 typedData.value = _Clamp_cast(knotData->value);
445 typedData.preValue = _Clamp_cast(knotData->preValue);
451 auto _ConvertTangent =
452 [&_Clamp_cast](
double slope,
double* width) -> T
454 T typedSlope = T(slope);
457 if (Ts_IsFinite(typedSlope)) {
463 double height = *width * slope;
466 typedSlope = _Clamp_cast(slope);
469 *width = height / typedSlope;
474 typedData.preTanSlope = _ConvertTangent(knotData->preTanSlope,
475 &typedData.preTanWidth);
476 typedData.postTanSlope = _ConvertTangent(knotData->postTanSlope,
477 &typedData.postTanWidth);
479 return SetKnot(&typedData, customDataIn);
484Ts_TypedSplineData<T>::CloneKnotAtIndex(
485 const size_t index)
const
487 return new Ts_TypedKnotData<T>(knots[index]);
492Ts_TypedSplineData<T>::CloneKnotAtTime(
493 const TsTime time)
const
495 const auto it = std::lower_bound(times.begin(), times.end(), time);
496 if (it == times.end() || *it != time)
501 const auto knotIt = knots.begin() + (it - times.begin());
502 return new Ts_TypedKnotData<T>(*knotIt);
507Ts_TypedSplineData<T>::GetKnotPtrAtIndex(
510 return &(knots[index]);
515Ts_TypedSplineData<T>::GetKnotPtrAtIndex(
516 const size_t index)
const
518 return &(knots[index]);
523Ts_TypedKnotData<double>
524Ts_TypedSplineData<T>::GetKnotDataAsDouble(
525 const size_t index)
const
527 const Ts_TypedKnotData<T> &in = knots[index];
528 Ts_TypedKnotData<double> out;
532 static_cast<Ts_KnotData&
>(out) =
static_cast<const Ts_KnotData&
>(in);
535 out.value = in.value;
536 out.preValue = in.preValue;
537 out.preTanSlope = in.preTanSlope;
538 out.postTanSlope = in.postTanSlope;
546Ts_TypedSplineData<T>::GetKnotValueAsDouble(
547 const size_t index)
const
549 const Ts_TypedKnotData<T> &typedData = knots[index];
550 return typedData.value;
556Ts_TypedSplineData<T>::GetKnotPreValueAsDouble(
557 const size_t index)
const
559 const Ts_TypedKnotData<T> &typedData = knots[index];
560 return typedData.GetPreValue();
564void Ts_TypedSplineData<T>::ClearKnots()
572void Ts_TypedSplineData<T>::RemoveKnotAtTime(
575 const auto it = std::lower_bound(times.begin(), times.end(), time);
576 if (it == times.end() || *it != time)
582 const size_t idx = it - times.begin();
584 customData.erase(time);
585 knots.erase(knots.begin() + idx);
589 UpdateKnotTangentsAtIndex(idx - 1);
591 if (idx < times.size()) {
592 UpdateKnotTangentsAtIndex(idx);
599static void _ApplyOffsetAndScaleToKnot(
600 Ts_TypedKnotData<T>*
const knotData,
605 knotData->time = knotData->time * scale + offset;
608 knotData->preTanWidth *= fabs(scale);
609 knotData->postTanWidth *= fabs(scale);
613void Ts_TypedSplineData<T>::ApplyOffsetAndScale(
630 if (scale < 0 && HasInnerLoops())
633 "inner loops. Please first bake inner loops.");
639 std::swap(preExtrapolation, postExtrapolation);
652 if (preExtrapolation.IsLooping()
653 && preExtrapolation.loopBoundaryTime.has_value())
655 double& lbt = preExtrapolation.loopBoundaryTime.value();
656 lbt = lbt * scale + offset;
658 if (postExtrapolation.IsLooping()
659 && postExtrapolation.loopBoundaryTime.has_value())
661 double& lbt = postExtrapolation.loopBoundaryTime.value();
662 lbt = lbt * scale + offset;
666 if (loopParams.protoEnd > loopParams.protoStart)
669 loopParams.protoStart = loopParams.protoStart * scale + offset;
670 loopParams.protoEnd = loopParams.protoEnd * scale + offset;
674 for (TsTime &time : times) {
675 time = time * scale + offset;
684 std::reverse(times.begin(), times.end());
686 for (
size_t i = 0; i < knots.size(); i++) {
687 size_t idx = knots.size() - 1 - i;
688 Ts_TypedKnotData<T> reversedKnot;
689 const Ts_TypedKnotData<T> knot = knots[idx];
690 if (knot.dualValued) {
691 reversedKnot.value = knot.preValue;
692 reversedKnot.dualValued =
true;
693 reversedKnot.preValue = knot.value;
695 reversedKnot.value = knot.value;
698 reversedKnot.time = knot.time;
699 reversedKnot.preTanWidth = knot.postTanWidth;
700 reversedKnot.postTanWidth = knot.preTanWidth;
701 reversedKnot.preTanSlope = knot.postTanSlope;
702 reversedKnot.postTanSlope = knot.preTanSlope;
703 reversedKnot.preTanAlgorithm = knot.postTanAlgorithm;
704 reversedKnot.postTanAlgorithm = knot.preTanAlgorithm;
707 reversedKnot.nextInterp = knots[idx - 1].nextInterp;
710 knots[idx] = reversedKnot;
712 std::reverse(knots.begin(), knots.end());
718 if (valueType == Ts_GetType<GfTimeCode>()) {
719 for (Ts_TypedKnotData<T> &knotData : knots)
721 _ApplyOffsetAndScaleToKnot(&knotData, offset, scale);
725 static_cast<T
>(knotData.value * scale + offset);
727 static_cast<T
>(knotData.preValue * scale + offset);
729 }
else if (valueType == Ts_GetType<GfDuration>()) {
730 for (Ts_TypedKnotData<T> &knotData : knots)
732 _ApplyOffsetAndScaleToKnot(&knotData, offset, scale);
736 static_cast<T
>(knotData.value * scale);
738 static_cast<T
>(knotData.preValue * scale);
744 for (Ts_TypedKnotData<T> &knotData : knots) {
745 _ApplyOffsetAndScaleToKnot(&knotData, offset, scale);
748 knotData.preTanSlope /= scale;
749 knotData.postTanSlope /= scale;
753 if (preExtrapolation.mode == TsExtrapSloped)
755 preExtrapolation.slope /= scale;
757 if (postExtrapolation.mode == TsExtrapSloped)
759 postExtrapolation.slope /= scale;
764 if (!customData.empty())
766 std::unordered_map<TsTime, VtDictionary> newCustomData;
767 for (
const auto &mapPair : customData) {
768 newCustomData[mapPair.first * scale + offset] = mapPair.second;
770 customData.swap(newCustomData);
775bool Ts_TypedSplineData<T>::HasValueBlocks()
const
782 if (preExtrapolation.mode == TsExtrapValueBlock
783 || postExtrapolation.mode == TsExtrapValueBlock)
788 for (
const Ts_TypedKnotData<T> &knotData : knots)
790 if (knotData.nextInterp == TsInterpValueBlock)
800bool Ts_TypedSplineData<T>::HasValueBlockAtTime(
801 const TsTime time)
const
811 std::lower_bound(times.begin(), times.end(), time);
815 if (lbIt == times.end())
817 return postExtrapolation.mode == TsExtrapValueBlock;
824 const auto knotIt = knots.begin() + (lbIt - times.begin());
825 return knotIt->nextInterp == TsInterpValueBlock;
830 if (lbIt == times.begin())
832 return preExtrapolation.mode == TsExtrapValueBlock;
837 const auto knotIt = knots.begin() + (lbIt - times.begin());
838 return (knotIt - 1)->nextInterp == TsInterpValueBlock;
842bool Ts_TypedSplineData<T>::UpdateKnotTangentsAtIndex(
size_t index)
846 "Knot index (%zd) out of range [0 .. %zd)",
847 index, knots.size()))
852 Ts_TypedKnotData<T>* prevKnot = (index > 0 ? &knots[index - 1] :
nullptr);
853 Ts_TypedKnotData<T>* knot = &knots[index];
854 Ts_TypedKnotData<T>* nextKnot = (index < knots.size() - 1
858 return knot->UpdateTangents(prevKnot, nextKnot, curveType);
862Ts_TypedSplineData<T>*
863Ts_GetTypedSplineData(
TsSpline &spline)
865 return static_cast<Ts_TypedSplineData<T>*
>(
866 Ts_GetSplineData(spline));
870const Ts_TypedSplineData<T>*
871Ts_GetTypedSplineData(
const TsSpline &spline)
873 return static_cast<Ts_TypedSplineData<T>*
>(
874 Ts_GetSplineData(spline));
878PXR_NAMESPACE_CLOSE_SCOPE
Low-level utilities for informing users of various internal and external diagnostic conditions.
TfType represents a dynamic runtime type.
A mathematical description of a curved function from time to value.
A map with string keys and VtValue values.
VT_API bool empty() const
true if the VtDictionary's size is 0.
#define TF_CODING_ERROR(fmt, args)
Issue an internal programming error, but continue execution.
#define TF_VERIFY(cond, format,...)
Checks a condition and reports an error if it evaluates false.