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 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;
110 size_t *firstProtoIndexOut =
nullptr)
const;
116 TsTime GetPreExtrapTime()
const;
122 TsTime GetPostExtrapTime()
const;
128 double GetPreExtrapValue()
const;
134 double GetPostExtrapValue()
const;
157 TsCurveType curveType : 2;
168 std::vector<TsTime> times;
171 std::unordered_map<TsTime, VtDictionary> customData;
178struct Ts_TypedSplineData final :
182 TfType GetValueType()
const override;
183 size_t GetKnotStructSize()
const override;
184 Ts_SplineData* Clone()
const override;
186 bool operator==(
const Ts_SplineData &other)
const override;
188 void ReserveForKnotCount(
size_t count)
override;
190 const Ts_KnotData *knotData,
198 const Ts_KnotData *knotData,
203 size_t SetKnotFromDouble(
204 const Ts_TypedKnotData<double>* knotData,
207 Ts_KnotData* CloneKnotAtIndex(
size_t index)
const override;
208 Ts_KnotData* CloneKnotAtTime(TsTime time)
const override;
209 Ts_KnotData* GetKnotPtrAtIndex(
size_t index)
override;
210 const Ts_KnotData* GetKnotPtrAtIndex(
size_t index)
const override;
211 Ts_TypedKnotData<double>
212 GetKnotDataAsDouble(
size_t index)
const override;
213 double GetKnotValueAsDouble(
size_t index)
const override;
214 double GetKnotPreValueAsDouble(
size_t index)
const override;
216 void ClearKnots()
override;
217 void RemoveKnotAtTime(TsTime time)
override;
226 void ApplyOffsetAndScale(
228 double scale)
override;
230 bool HasValueBlocks()
const override;
231 bool HasValueBlockAtTime(TsTime time)
const override;
233 bool UpdateKnotTangentsAtIndex(
size_t index)
override;
237 std::vector<Ts_TypedKnotData<T>> knots;
248Ts_GetSplineData(
const TsSpline &spline);
251Ts_TypedSplineData<T>*
252Ts_GetTypedSplineData(
TsSpline &spline);
255const Ts_TypedSplineData<T>*
256Ts_GetTypedSplineData(
const TsSpline &spline);
263TfType Ts_TypedSplineData<T>::GetValueType()
const
270 return Ts_GetType<T>();
274size_t Ts_TypedSplineData<T>::GetKnotStructSize()
const
276 return sizeof(Ts_TypedKnotData<T>);
281Ts_TypedSplineData<T>::Clone()
const
283 return new Ts_TypedSplineData<T>(*
this);
287bool Ts_TypedSplineData<T>::operator==(
288 const Ts_SplineData &other)
const
291 if (isTyped != other.isTyped
292 || timeValued != other.timeValued
293 || curveType != other.curveType
294 || preExtrapolation != other.preExtrapolation
295 || postExtrapolation != other.postExtrapolation
296 || loopParams != other.loopParams
297 || customData != other.customData)
304 const Ts_TypedSplineData<T>*
const typedOther =
305 dynamic_cast<const Ts_TypedSplineData<T>*
>(&other);
312 return knots == typedOther->knots;
316void Ts_TypedSplineData<T>::ReserveForKnotCount(
319 times.reserve(count);
320 knots.reserve(count);
324void Ts_TypedSplineData<T>::PushKnot(
325 const Ts_KnotData*
const knotData,
328 const Ts_TypedKnotData<T>*
const typedKnotData =
329 static_cast<const Ts_TypedKnotData<T>*
>(knotData);
331 times.push_back(knotData->time);
332 knots.push_back(*typedKnotData);
334 if (!customDataIn.
empty())
336 customData[knotData->time] = customDataIn;
380size_t Ts_TypedSplineData<T>::SetKnot(
381 const Ts_KnotData*
const knotData,
384 const Ts_TypedKnotData<T>*
const typedKnotData =
385 static_cast<const Ts_TypedKnotData<T>*
>(knotData);
389 std::lower_bound(times.begin(), times.end(), knotData->time);
392 const bool overwrite =
393 (it != times.end() && *it == knotData->time);
398 times[idx] = knotData->time;
399 knots[idx] = *typedKnotData;
403 times.insert(it, knotData->time);
404 knots.insert(knots.begin() + idx, *typedKnotData);
408 if (!customDataIn.
empty())
410 customData[knotData->time] = customDataIn;
417size_t Ts_TypedSplineData<T>::SetKnotFromDouble(
418 const Ts_TypedKnotData<double>* knotData,
422 if constexpr(std::is_same_v<T, double>) {
423 return SetKnot(knotData, customDataIn);
426 Ts_TypedKnotData<T> typedData;
430 static_cast<Ts_KnotData&
>(typedData) =
431 static_cast<const Ts_KnotData&
>(*knotData);
439 return std::min(T(v), std::numeric_limits<T>::max());
441 return std::max(T(v), std::numeric_limits<T>::lowest());
446 typedData.value = _Clamp_cast(knotData->value);
447 typedData.preValue = _Clamp_cast(knotData->preValue);
453 auto _ConvertTangent =
454 [&_Clamp_cast](
double slope,
double* width) -> T
456 T typedSlope = T(slope);
459 if (Ts_IsFinite(typedSlope)) {
465 double height = *width * slope;
468 typedSlope = _Clamp_cast(slope);
471 *width = height / typedSlope;
476 typedData.preTanSlope = _ConvertTangent(knotData->preTanSlope,
477 &typedData.preTanWidth);
478 typedData.postTanSlope = _ConvertTangent(knotData->postTanSlope,
479 &typedData.postTanWidth);
481 return SetKnot(&typedData, customDataIn);
486Ts_TypedSplineData<T>::CloneKnotAtIndex(
487 const size_t index)
const
489 return new Ts_TypedKnotData<T>(knots[index]);
494Ts_TypedSplineData<T>::CloneKnotAtTime(
495 const TsTime time)
const
497 const auto it = std::lower_bound(times.begin(), times.end(), time);
498 if (it == times.end() || *it != time)
503 const auto knotIt = knots.begin() + (it - times.begin());
504 return new Ts_TypedKnotData<T>(*knotIt);
509Ts_TypedSplineData<T>::GetKnotPtrAtIndex(
512 return &(knots[index]);
517Ts_TypedSplineData<T>::GetKnotPtrAtIndex(
518 const size_t index)
const
520 return &(knots[index]);
525Ts_TypedKnotData<double>
526Ts_TypedSplineData<T>::GetKnotDataAsDouble(
527 const size_t index)
const
529 const Ts_TypedKnotData<T> &in = knots[index];
530 Ts_TypedKnotData<double> out;
534 static_cast<Ts_KnotData&
>(out) =
static_cast<const Ts_KnotData&
>(in);
537 out.value = in.value;
538 out.preValue = in.preValue;
539 out.preTanSlope = in.preTanSlope;
540 out.postTanSlope = in.postTanSlope;
548Ts_TypedSplineData<T>::GetKnotValueAsDouble(
549 const size_t index)
const
551 const Ts_TypedKnotData<T> &typedData = knots[index];
552 return typedData.value;
558Ts_TypedSplineData<T>::GetKnotPreValueAsDouble(
559 const size_t index)
const
561 const Ts_TypedKnotData<T> &typedData = knots[index];
562 return typedData.GetPreValue();
566void Ts_TypedSplineData<T>::ClearKnots()
574void Ts_TypedSplineData<T>::RemoveKnotAtTime(
577 const auto it = std::lower_bound(times.begin(), times.end(), time);
578 if (it == times.end() || *it != time)
584 const size_t idx = it - times.begin();
586 customData.erase(time);
587 knots.erase(knots.begin() + idx);
591 UpdateKnotTangentsAtIndex(idx - 1);
593 if (idx < times.size()) {
594 UpdateKnotTangentsAtIndex(idx);
599static void _ApplyOffsetAndScaleToKnot(
600 Ts_TypedKnotData<T>*
const knotData,
608 knotData->time = knotData->time * scale + offset;
611 knotData->preTanWidth *= scale;
612 knotData->postTanWidth *= scale;
615 knotData->preTanSlope /= scale;
616 knotData->postTanSlope /= scale;
620void Ts_TypedSplineData<T>::ApplyOffsetAndScale(
627 "collapsing/reversing time and spline representation "
639 if (preExtrapolation.mode == TsExtrapSloped)
641 preExtrapolation.slope /= scale;
643 if (postExtrapolation.mode == TsExtrapSloped)
645 postExtrapolation.slope /= scale;
649 if (loopParams.protoEnd > loopParams.protoStart)
652 loopParams.protoStart = loopParams.protoStart * scale + offset;
653 loopParams.protoEnd = loopParams.protoEnd * scale + offset;
657 for (TsTime &time : times) {
658 time = time * scale + offset;
666 for (Ts_TypedKnotData<T> &knotData : knots)
668 _ApplyOffsetAndScaleToKnot(&knotData, offset, scale);
672 static_cast<T
>(knotData.value * scale + offset);
674 static_cast<T
>(knotData.preValue * scale + offset);
679 for (Ts_TypedKnotData<T> &knotData : knots) {
680 _ApplyOffsetAndScaleToKnot(&knotData, offset, scale);
685 if (!customData.empty())
687 std::unordered_map<TsTime, VtDictionary> newCustomData;
688 for (
const auto &mapPair : customData) {
689 newCustomData[mapPair.first * scale + offset] = mapPair.second;
691 customData.swap(newCustomData);
696bool Ts_TypedSplineData<T>::HasValueBlocks()
const
703 if (preExtrapolation.mode == TsExtrapValueBlock
704 || postExtrapolation.mode == TsExtrapValueBlock)
709 for (
const Ts_TypedKnotData<T> &knotData : knots)
711 if (knotData.nextInterp == TsInterpValueBlock)
721bool Ts_TypedSplineData<T>::HasValueBlockAtTime(
722 const TsTime time)
const
732 std::lower_bound(times.begin(), times.end(), time);
736 if (lbIt == times.end())
738 return postExtrapolation.mode == TsExtrapValueBlock;
745 const auto knotIt = knots.begin() + (lbIt - times.begin());
746 return knotIt->nextInterp == TsInterpValueBlock;
751 if (lbIt == times.begin())
753 return preExtrapolation.mode == TsExtrapValueBlock;
758 const auto knotIt = knots.begin() + (lbIt - times.begin());
759 return (knotIt - 1)->nextInterp == TsInterpValueBlock;
763bool Ts_TypedSplineData<T>::UpdateKnotTangentsAtIndex(
size_t index)
767 "Knot index (%zd) out of range [0 .. %zd)",
768 index, knots.size()))
773 Ts_TypedKnotData<T>* prevKnot = (index > 0 ? &knots[index - 1] :
nullptr);
774 Ts_TypedKnotData<T>* knot = &knots[index];
775 Ts_TypedKnotData<T>* nextKnot = (index < knots.size() - 1
779 return knot->UpdateTangents(prevKnot, nextKnot, curveType);
783Ts_TypedSplineData<T>*
784Ts_GetTypedSplineData(
TsSpline &spline)
786 return static_cast<Ts_TypedSplineData<T>*
>(
787 Ts_GetSplineData(spline));
791const Ts_TypedSplineData<T>*
792Ts_GetTypedSplineData(
const TsSpline &spline)
794 return static_cast<Ts_TypedSplineData<T>*
>(
795 Ts_GetSplineData(spline));
799PXR_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.