Loading...
Searching...
No Matches
splineData.h
1//
2// Copyright 2024 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_SPLINE_DATA_H
9#define PXR_BASE_TS_SPLINE_DATA_H
10
11#include "pxr/pxr.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"
19#include "pxr/base/tf/stl.h"
20
21#include <vector>
22#include <unordered_map>
23#include <algorithm>
24#include <iterator>
25#include <utility>
26#include <cmath>
27
28PXR_NAMESPACE_OPEN_SCOPE
29
30class TsSpline;
31
32
33// Primary data structure for splines. Abstract; subclasses store knot data,
34// which is flexibly typed (double/float/half). This is the unit of data that
35// is managed by shared_ptr, and forms the basis of copy-on-write data sharing.
36//
37struct Ts_SplineData
38{
39public:
40 // If valueType is known, create a TypedSplineData of the specified type.
41 // If valueType is unknown, create a TypedSplineData<double> to store
42 // overall spline parameters in the absence of a value type; this assumes
43 // that when knots arrive, they are most likely to be double-typed. If
44 // overallParamSource is provided, it is a previous overall-only struct, and
45 // our guess about double was wrong, so we are transferring the overall
46 // parameters.
47 static Ts_SplineData* Create(
48 TfType valueType,
49 const Ts_SplineData *overallParamSource = nullptr);
50
51 virtual ~Ts_SplineData();
52
53public:
54 // Virtual interface for typed data.
55
56 virtual TfType GetValueType() const = 0;
57 virtual size_t GetKnotStructSize() const = 0;
58 virtual Ts_SplineData* Clone() const = 0;
59
60 virtual bool operator==(const Ts_SplineData &other) const = 0;
61
62 virtual void ReserveForKnotCount(size_t count) = 0;
63 virtual void PushKnot(
64 const Ts_KnotData *knotData,
65 const VtDictionary &customData) = 0;
66 // // Overload of PushKnot that offsets the time by timeOffset and values by
67 // // valueOffset. This allows us to unroll knots in loops without having to
68 // // dispatch based on TfType comparisons.
69 // virtual void PushKnot(
70 // const Ts_KnotData *knotData,
71 // const VtDictionary &customData,
72 // const double timeOffset,
73 // const double valueOffset) = 0;
74 virtual size_t SetKnot(
75 const Ts_KnotData *knotData,
76 const VtDictionary &customData) = 0;
77
78 // For ease of use by breakdown, double knot data to be set into any type of
79 // spline.
80 virtual size_t SetKnotFromDouble(
81 const Ts_TypedKnotData<double>* knotData,
82 const VtDictionary &customData) = 0;
83
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;
92
93 virtual void ClearKnots() = 0;
94 virtual void RemoveKnotAtTime(TsTime time) = 0;
95
96 virtual void ApplyOffsetAndScale(
97 TsTime offset,
98 double scale) = 0;
99
100 virtual bool HasValueBlocks() const = 0;
101 virtual bool HasValueBlockAtTime(TsTime time) const = 0;
102
103 virtual bool UpdateKnotTangentsAtIndex(size_t index) = 0;
104
105public:
106 // Returns whether there is a valid inner-loop configuration. If
107 // firstProtoIndexOut is provided, it receives the index of the first knot
108 // in the prototype.
109 bool HasInnerLoops(
110 size_t *firstProtoIndexOut = nullptr) const;
111
112 // Return the time at which pre-extrapolation ends and knot interpolation
113 // begins. Returns 0.0 if there are no knots. It is the caller's
114 // responsibility to ensure that there are knots before relying on the
115 // answer.
116 TsTime GetPreExtrapTime() const;
117
118 // Return the time at which knot interpolation ends and post-extrapolation
119 // begins. Returns 0.0 if there are no knots. It is the caller's
120 // responsibility to ensure that there are knots before relying on the
121 // answer.
122 TsTime GetPostExtrapTime() const;
123
124 // Return the value from which pre-extrapolation extrapolates (as a double).
125 // This accounts for dual valued knots and inner looping. Returns 0.0 if
126 // there are no knots. It is the caller's responsibility to ensure that
127 // there are knots before relying on the answer.
128 double GetPreExtrapValue() const;
129
130 // Return the value from which post-extrapolation extrapolates (as a
131 // double). This accounts for inner looping. Returns 0.0 if there are no
132 // knots. It is the caller's responsibility to ensure that there are knots
133 // before relying on the answer.
134 double GetPostExtrapValue() const;
135
136public:
137 // BITFIELDS - note: for enum-typed bitfields, we declare one bit more than
138 // is minimally needed to represent all declared enum values. For example,
139 // TsCurveType has only two values, so it should be representable in one
140 // bit. However, compilers are free to choose the underlying representation
141 // of enums, and some platforms choose signed values, meaning that we
142 // actually need one bit more, so that we can hold the sign bit. We could
143 // declare the enums with unsigned underlying types, but that runs into a
144 // gcc 9.2 bug. We can spare the extra bit; alignment means there is no
145 // difference in struct size.
146
147 // If true, our subtype is authoritative; we know our value type. If false,
148 // then no value type was provided at initialization, and no knots have been
149 // set. In the latter case, we exist only to store overall parameters, and
150 // we have been presumptively created as TypedSplineData<double>.
151 bool isTyped : 1;
152
153 // Whether ApplyOffsetAndScale applies to values also.
154 bool timeValued : 1;
155
156 // Overall spline parameters.
157 TsCurveType curveType : 2;
158 TsExtrapolation preExtrapolation;
159 TsExtrapolation postExtrapolation;
160 TsLoopParams loopParams;
161
162 // A duplicate of the knot times, so that we can maximize locality while
163 // performing binary searches for knots. This is part of the evaluation hot
164 // path; given an eval time, we must find either the knot at that time, or
165 // the knots before and after that time. The entries in this vector
166 // correspond exactly to the entries in the 'knots' vector in
167 // Ts_TypedSplineData. Times are unique and sorted in ascending order.
168 std::vector<TsTime> times;
169
170 // Custom data for knots, sparsely allocated, keyed by time.
171 std::unordered_map<TsTime, VtDictionary> customData;
172};
173
174
175// Concrete subclass of Ts_SplineData. Templated on T, the value type.
176//
177template <typename T>
178struct Ts_TypedSplineData final :
179 public Ts_SplineData
180{
181public:
182 TfType GetValueType() const override;
183 size_t GetKnotStructSize() const override;
184 Ts_SplineData* Clone() const override;
185
186 bool operator==(const Ts_SplineData &other) const override;
187
188 void ReserveForKnotCount(size_t count) override;
189 void PushKnot(
190 const Ts_KnotData *knotData,
191 const VtDictionary &customData) override;
192 // void PushKnot(
193 // const Ts_KnotData *knotData,
194 // const VtDictionary &customData,
195 // const double timeOffset,
196 // const double valueOffset) override;
197 size_t SetKnot(
198 const Ts_KnotData *knotData,
199 const VtDictionary &customData) override;
200
201 // For ease of use while splitting, double knot data to be set
202 // into any type of spline.
203 size_t SetKnotFromDouble(
204 const Ts_TypedKnotData<double>* knotData,
205 const VtDictionary &customData) override;
206
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;
215
216 void ClearKnots() override;
217 void RemoveKnotAtTime(TsTime time) override;
218
219 // Apply offset and scale to all spline data.
220 //
221 // If \p scale is negative, a coding error is generated. This is because
222 // the spline is not only scaled, but also time-reversed. Doing so can
223 // lead to incorrect evaluation results with any scenario where direction
224 // of time is assumed, like dual-value knots, inner looping,
225 // segment interpolation mode assignment, etc.
226 void ApplyOffsetAndScale(
227 TsTime offset,
228 double scale) override;
229
230 bool HasValueBlocks() const override;
231 bool HasValueBlockAtTime(TsTime time) const override;
232
233 bool UpdateKnotTangentsAtIndex(size_t index) override;
234
235public:
236 // Per-knot data.
237 std::vector<Ts_TypedKnotData<T>> knots;
238};
239
240
241// Data-access helpers for the Ts implementation. The untyped functions are
242// friends of TsSpline, and retrieve private data pointers.
243
244Ts_SplineData*
245Ts_GetSplineData(TsSpline &spline);
246
247const Ts_SplineData*
248Ts_GetSplineData(const TsSpline &spline);
249
250template <typename T>
251Ts_TypedSplineData<T>*
252Ts_GetTypedSplineData(TsSpline &spline);
253
254template <typename T>
255const Ts_TypedSplineData<T>*
256Ts_GetTypedSplineData(const TsSpline &spline);
257
258
260// TEMPLATE IMPLEMENTATIONS
261
262template <typename T>
263TfType Ts_TypedSplineData<T>::GetValueType() const
264{
265 if (!isTyped)
266 {
267 return TfType();
268 }
269
270 return Ts_GetType<T>();
271}
272
273template <typename T>
274size_t Ts_TypedSplineData<T>::GetKnotStructSize() const
275{
276 return sizeof(Ts_TypedKnotData<T>);
277}
278
279template <typename T>
280Ts_SplineData*
281Ts_TypedSplineData<T>::Clone() const
282{
283 return new Ts_TypedSplineData<T>(*this);
284}
285
286template <typename T>
287bool Ts_TypedSplineData<T>::operator==(
288 const Ts_SplineData &other) const
289{
290 // Compare non-templated data.
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)
298 {
299 return false;
300 }
301
302 // Downcast to our value type. If other is not of the same type, we're not
303 // equal.
304 const Ts_TypedSplineData<T>* const typedOther =
305 dynamic_cast<const Ts_TypedSplineData<T>*>(&other);
306 if (!typedOther)
307 {
308 return false;
309 }
310
311 // Compare all knots.
312 return knots == typedOther->knots;
313}
314
315template <typename T>
316void Ts_TypedSplineData<T>::ReserveForKnotCount(
317 const size_t count)
318{
319 times.reserve(count);
320 knots.reserve(count);
321}
322
323template <typename T>
324void Ts_TypedSplineData<T>::PushKnot(
325 const Ts_KnotData* const knotData,
326 const VtDictionary &customDataIn)
327{
328 const Ts_TypedKnotData<T>* const typedKnotData =
329 static_cast<const Ts_TypedKnotData<T>*>(knotData);
330
331 times.push_back(knotData->time);
332 knots.push_back(*typedKnotData);
333
334 if (!customDataIn.empty())
335 {
336 customData[knotData->time] = customDataIn;
337 }
338}
339
340// template <typename T>
341// void Ts_TypedSplineData<T>::PushKnot(
342// const Ts_KnotData *knotData,
343// const VtDictionary &customDataIn,
344// const double timeOffset,
345// const double valueOffset)
346// {
347// Ts_TypedKnotData<T> typedKnotData(
348// *static_cast<const Ts_TypedKnotData<T>*>(knotData));
349
350// typedKnotData.time += timeOffset;
351// typedKnotData.value += valueOffset;
352// typedKnotData.preValue += valueOffset;
353
354// // Clamp to prevent infinities in types smaller than double (especially
355// // GfHalf).
356// if constexpr(!std::is_same_v<T, double>) {
357// if (typedKnotData.value > std::numeric_limits<T>::max()) {
358// typedKnotData.value = std::numeric_limits<T>::max();
359// } else if (typedKnotData.value < std::numeric_limits<T>::lowest()) {
360// typedKnotData.value = std::numeric_limits<T>::lowest();
361// }
362
363// if (typedKnotData.preValue > std::numeric_limits<T>::max()) {
364// typedKnotData.preValue = std::numeric_limits<T>::max();
365// } else if (typedKnotData.preValue < std::numeric_limits<T>::lowest()) {
366// typedKnotData.preValue = std::numeric_limits<T>::lowest();
367// }
368// }
369
370// times.push_back(typedKnotData.time);
371// knots.push_back(typedKnotData);
372
373// if (!customDataIn.empty())
374// {
375// customData[knotData->time] = customDataIn;
376// }
377// }
378
379template <typename T>
380size_t Ts_TypedSplineData<T>::SetKnot(
381 const Ts_KnotData* const knotData,
382 const VtDictionary &customDataIn)
383{
384 const Ts_TypedKnotData<T>* const typedKnotData =
385 static_cast<const Ts_TypedKnotData<T>*>(knotData);
386
387 // Use binary search to find insert-or-overwrite position.
388 const auto it =
389 std::lower_bound(times.begin(), times.end(), knotData->time);
390 const size_t idx =
391 it - times.begin();
392 const bool overwrite =
393 (it != times.end() && *it == knotData->time);
394
395 // Insert or overwrite new time and knot data.
396 if (overwrite)
397 {
398 times[idx] = knotData->time;
399 knots[idx] = *typedKnotData;
400 }
401 else
402 {
403 times.insert(it, knotData->time);
404 knots.insert(knots.begin() + idx, *typedKnotData);
405 }
406
407 // Store customData, if any.
408 if (!customDataIn.empty())
409 {
410 customData[knotData->time] = customDataIn;
411 }
412
413 return idx;
414}
415
416template <typename T>
417size_t Ts_TypedSplineData<T>::SetKnotFromDouble(
418 const Ts_TypedKnotData<double>* knotData,
419 const VtDictionary &customDataIn)
420{
421 // If we have double data, just set it directly.
422 if constexpr(std::is_same_v<T, double>) {
423 return SetKnot(knotData, customDataIn);
424 }
425
426 Ts_TypedKnotData<T> typedData;
427
428 // Use operator= to copy base-class members. This is admittedly weird, but
429 // it will continue working if members are added to the base class.
430 static_cast<Ts_KnotData&>(typedData) =
431 static_cast<const Ts_KnotData&>(*knotData);
432
433 // We need to copy and convert the data from double to T. We don't want
434 // infinite values, so clamp to the largest possible finite value.
435 auto _Clamp_cast =
436 [](double v) -> T
437 {
438 if (v >= 0) {
439 return std::min(T(v), std::numeric_limits<T>::max());
440 } else {
441 return std::max(T(v), std::numeric_limits<T>::lowest());
442 }
443 };
444
445 // Convert and clamp the value fields.
446 typedData.value = _Clamp_cast(knotData->value);
447 typedData.preValue = _Clamp_cast(knotData->preValue);
448
449 // Slopes are tricky. If they overflow, we need to compute a new slope and a
450 // new width such that the new tangent end-point is as close as possible to
451 // the original tangent end point.
452
453 auto _ConvertTangent =
454 [&_Clamp_cast](double slope, double* width) -> T
455 {
456 T typedSlope = T(slope);
457 // std::isfinite<GfHalf>() is missing so use the helper from
458 // typeHelpers.h instead.
459 if (Ts_IsFinite(typedSlope)) {
460 return typedSlope;
461 }
462
463 // Convert both the slope and width to values that preserve the
464 // endpoint of the tangent as much as possible.
465 double height = *width * slope;
466
467 // typedSlope is infinite, clamp it to a finite value.
468 typedSlope = _Clamp_cast(slope);
469
470 // modify width to preserve the tangent's height.
471 *width = height / typedSlope;
472
473 return typedSlope;
474 };
475
476 typedData.preTanSlope = _ConvertTangent(knotData->preTanSlope,
477 &typedData.preTanWidth);
478 typedData.postTanSlope = _ConvertTangent(knotData->postTanSlope,
479 &typedData.postTanWidth);
480
481 return SetKnot(&typedData, customDataIn);
482}
483
484template <typename T>
485Ts_KnotData*
486Ts_TypedSplineData<T>::CloneKnotAtIndex(
487 const size_t index) const
488{
489 return new Ts_TypedKnotData<T>(knots[index]);
490}
491
492template <typename T>
493Ts_KnotData*
494Ts_TypedSplineData<T>::CloneKnotAtTime(
495 const TsTime time) const
496{
497 const auto it = std::lower_bound(times.begin(), times.end(), time);
498 if (it == times.end() || *it != time)
499 {
500 return nullptr;
501 }
502
503 const auto knotIt = knots.begin() + (it - times.begin());
504 return new Ts_TypedKnotData<T>(*knotIt);
505}
506
507template <typename T>
508Ts_KnotData*
509Ts_TypedSplineData<T>::GetKnotPtrAtIndex(
510 const size_t index)
511{
512 return &(knots[index]);
513}
514
515template <typename T>
516const Ts_KnotData*
517Ts_TypedSplineData<T>::GetKnotPtrAtIndex(
518 const size_t index) const
519{
520 return &(knots[index]);
521}
522
523// Depending on T, this is either a verbatim copy or an increase in precision.
524template <typename T>
525Ts_TypedKnotData<double>
526Ts_TypedSplineData<T>::GetKnotDataAsDouble(
527 const size_t index) const
528{
529 const Ts_TypedKnotData<T> &in = knots[index];
530 Ts_TypedKnotData<double> out;
531
532 // Use operator= to copy base-class members. This is admittedly weird, but
533 // it will continue working if members are added to the base class.
534 static_cast<Ts_KnotData&>(out) = static_cast<const Ts_KnotData&>(in);
535
536 // Copy derived members individually.
537 out.value = in.value;
538 out.preValue = in.preValue;
539 out.preTanSlope = in.preTanSlope;
540 out.postTanSlope = in.postTanSlope;
541
542 return out;
543}
544
545// Depending on T, this is either a verbatim copy or an increase in precision.
546template <typename T>
547double
548Ts_TypedSplineData<T>::GetKnotValueAsDouble(
549 const size_t index) const
550{
551 const Ts_TypedKnotData<T> &typedData = knots[index];
552 return typedData.value;
553}
554
555// Depending on T, this is either a verbatim copy or an increase in precision.
556template <typename T>
557double
558Ts_TypedSplineData<T>::GetKnotPreValueAsDouble(
559 const size_t index) const
560{
561 const Ts_TypedKnotData<T> &typedData = knots[index];
562 return typedData.GetPreValue();
563}
564
565template <typename T>
566void Ts_TypedSplineData<T>::ClearKnots()
567{
568 times.clear();
569 customData.clear();
570 knots.clear();
571}
572
573template <typename T>
574void Ts_TypedSplineData<T>::RemoveKnotAtTime(
575 const TsTime time)
576{
577 const auto it = std::lower_bound(times.begin(), times.end(), time);
578 if (it == times.end() || *it != time)
579 {
580 TF_CODING_ERROR("Cannot remove nonexistent knot from SplineData");
581 return;
582 }
583
584 const size_t idx = it - times.begin();
585 times.erase(it);
586 customData.erase(time);
587 knots.erase(knots.begin() + idx);
588
589 // Update the tangents on the knots either side of the one removed
590 if (idx > 0) {
591 UpdateKnotTangentsAtIndex(idx - 1);
592 }
593 if (idx < times.size()) {
594 UpdateKnotTangentsAtIndex(idx);
595 }
596}
597
598template <typename T>
599static void _ApplyOffsetAndScaleToKnot(
600 Ts_TypedKnotData<T>* const knotData,
601 const TsTime offset,
602 const double scale)
603{
604 // In our private implementation, we must have set a positive scale.
605 TF_VERIFY(scale > 0);
606
607 // Process knot time (absolute).
608 knotData->time = knotData->time * scale + offset;
609
610 // Process tangent widths (relative, strictly positive).
611 knotData->preTanWidth *= scale;
612 knotData->postTanWidth *= scale;
613
614 // Process slopes (inverse relative).
615 knotData->preTanSlope /= scale;
616 knotData->postTanSlope /= scale;
617}
618
619template <typename T>
620void Ts_TypedSplineData<T>::ApplyOffsetAndScale(
621 const TsTime offset,
622 const double scale)
623{
624 if (scale <= 0)
625 {
626 TF_CODING_ERROR("Applying zero or negative scale to spline data, "
627 "collapsing/reversing time and spline representation "
628 "is not allowed.");
629 return;
630 }
631
632 // The spline is changed in the time dimension only.
633 // Different parameters are affected in different ways:
634 // - Absolute times (e.g. knot times): apply scale and offset.
635 // - Relative times (e.g. tan widths): apply scale only.
636 // - Inverse relative (slopes): slope = height/width, so we apply 1/scale.
637
638 // Scale extrapolation slopes if applicable (inverse relative).
639 if (preExtrapolation.mode == TsExtrapSloped)
640 {
641 preExtrapolation.slope /= scale;
642 }
643 if (postExtrapolation.mode == TsExtrapSloped)
644 {
645 postExtrapolation.slope /= scale;
646 }
647
648 // Process inner-loop params.
649 if (loopParams.protoEnd > loopParams.protoStart)
650 {
651 // Process start and end times (absolute).
652 loopParams.protoStart = loopParams.protoStart * scale + offset;
653 loopParams.protoEnd = loopParams.protoEnd * scale + offset;
654 }
655
656 // Process knot-times vector (absolute).
657 for (TsTime &time : times) {
658 time = time * scale + offset;
659 }
660
661 // Process knots. Duplicate the logic that is applied unconditionally, so
662 // that we can rip through the entire vector just once, and we don't have to
663 // do the if-check on each iteration.
664 if (timeValued)
665 {
666 for (Ts_TypedKnotData<T> &knotData : knots)
667 {
668 _ApplyOffsetAndScaleToKnot(&knotData, offset, scale);
669
670 // Process time values (absolute).
671 knotData.value =
672 static_cast<T>(knotData.value * scale + offset);
673 knotData.preValue =
674 static_cast<T>(knotData.preValue * scale + offset);
675 }
676 }
677 else
678 {
679 for (Ts_TypedKnotData<T> &knotData : knots) {
680 _ApplyOffsetAndScaleToKnot(&knotData, offset, scale);
681 }
682 }
683
684 // Re-index custom data. Times are adjusted absolutely.
685 if (!customData.empty())
686 {
687 std::unordered_map<TsTime, VtDictionary> newCustomData;
688 for (const auto &mapPair : customData) {
689 newCustomData[mapPair.first * scale + offset] = mapPair.second;
690 }
691 customData.swap(newCustomData);
692 }
693}
694
695template <typename T>
696bool Ts_TypedSplineData<T>::HasValueBlocks() const
697{
698 if (knots.empty())
699 {
700 return false;
701 }
702
703 if (preExtrapolation.mode == TsExtrapValueBlock
704 || postExtrapolation.mode == TsExtrapValueBlock)
705 {
706 return true;
707 }
708
709 for (const Ts_TypedKnotData<T> &knotData : knots)
710 {
711 if (knotData.nextInterp == TsInterpValueBlock)
712 {
713 return true;
714 }
715 }
716
717 return false;
718}
719
720template <typename T>
721bool Ts_TypedSplineData<T>::HasValueBlockAtTime(
722 const TsTime time) const
723{
724 // If no knots, no blocks.
725 if (knots.empty())
726 {
727 return false;
728 }
729
730 // Find first knot at or after time.
731 const auto lbIt =
732 std::lower_bound(times.begin(), times.end(), time);
733
734 // If time is after all knots, return whether we have blocked
735 // post-extrapolation.
736 if (lbIt == times.end())
737 {
738 return postExtrapolation.mode == TsExtrapValueBlock;
739 }
740
741 // If there is a knot at this time, return whether its segment has blocked
742 // interpolation.
743 if (*lbIt == time)
744 {
745 const auto knotIt = knots.begin() + (lbIt - times.begin());
746 return knotIt->nextInterp == TsInterpValueBlock;
747 }
748
749 // If time is before all knots, return whether we have blocked
750 // pre-extrapolation.
751 if (lbIt == times.begin())
752 {
753 return preExtrapolation.mode == TsExtrapValueBlock;
754 }
755
756 // Between knots. Return whether the segment that we're in has blocked
757 // interpolation.
758 const auto knotIt = knots.begin() + (lbIt - times.begin());
759 return (knotIt - 1)->nextInterp == TsInterpValueBlock;
760}
761
762template <typename T>
763bool Ts_TypedSplineData<T>::UpdateKnotTangentsAtIndex(size_t index)
764{
765 // XXX: Should we use PXR_PREFER_SAFETY_OVER_SPEED around this test?
766 if (!TF_VERIFY(index < knots.size(),
767 "Knot index (%zd) out of range [0 .. %zd)",
768 index, knots.size()))
769 {
770 return false;
771 }
772
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
776 ? &knots[index + 1]
777 : nullptr);
778
779 return knot->UpdateTangents(prevKnot, nextKnot, curveType);
780}
781
782template <typename T>
783Ts_TypedSplineData<T>*
784Ts_GetTypedSplineData(TsSpline &spline)
785{
786 return static_cast<Ts_TypedSplineData<T>*>(
787 Ts_GetSplineData(spline));
788}
789
790template <typename T>
791const Ts_TypedSplineData<T>*
792Ts_GetTypedSplineData(const TsSpline &spline)
793{
794 return static_cast<Ts_TypedSplineData<T>*>(
795 Ts_GetSplineData(spline));
796}
797
798
799PXR_NAMESPACE_CLOSE_SCOPE
800
801#endif
Low-level utilities for informing users of various internal and external diagnostic conditions.
TfType represents a dynamic runtime type.
Definition: type.h:48
Extrapolation parameters for the ends of a spline beyond the knots.
Definition: types.h:220
Inner-loop parameters.
Definition: types.h:193
A mathematical description of a curved function from time to value.
Definition: spline.h:60
A map with string keys and VtValue values.
Definition: dictionary.h:52
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.
Definition: diagnostic.h:68
#define TF_VERIFY(cond, format,...)
Checks a condition and reports an error if it evaluates false.
Definition: diagnostic.h:266