HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
splineData.h
Go to the documentation of this file.
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"
16 #include "pxr/base/vt/dictionary.h"
17 #include "pxr/base/tf/diagnostic.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 
29 
30 class 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 //
38 {
39 public:
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 
53 public:
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;
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 
105 public:
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 
136 public:
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.
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 //
177 template <typename T>
178 struct Ts_TypedSplineData final :
179  public Ts_SplineData
180 {
181 public:
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;
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 
235 public:
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 
246 
247 const Ts_SplineData*
249 
250 template <typename T>
253 
254 template <typename T>
257 
258 
259 ////////////////////////////////////////////////////////////////////////////////
260 // TEMPLATE IMPLEMENTATIONS
261 
262 template <typename T>
264 {
265  if (!isTyped)
266  {
267  return TfType();
268  }
269 
270  return Ts_GetType<T>();
271 }
272 
273 template <typename T>
275 {
276  return sizeof(Ts_TypedKnotData<T>);
277 }
278 
279 template <typename T>
282 {
283  return new Ts_TypedSplineData<T>(*this);
284 }
285 
286 template <typename T>
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 
315 template <typename T>
317  const size_t count)
318 {
319  times.reserve(count);
320  knots.reserve(count);
321 }
322 
323 template <typename T>
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 
379 template <typename T>
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 
416 template <typename T>
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) {
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 
484 template <typename T>
487  const size_t index) const
488 {
489  return new Ts_TypedKnotData<T>(knots[index]);
490 }
491 
492 template <typename T>
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 
507 template <typename T>
510  const size_t index)
511 {
512  return &(knots[index]);
513 }
514 
515 template <typename T>
516 const Ts_KnotData*
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.
524 template <typename T>
527  const size_t index) const
528 {
529  const Ts_TypedKnotData<T> &in = knots[index];
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.
546 template <typename T>
547 double
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.
556 template <typename T>
557 double
559  const size_t index) const
560 {
561  const Ts_TypedKnotData<T> &typedData = knots[index];
562  return typedData.GetPreValue();
563 }
564 
565 template <typename T>
567 {
568  times.clear();
569  customData.clear();
570  knots.clear();
571 }
572 
573 template <typename T>
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 
598 template <typename T>
599 static 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 
619 template <typename T>
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 
695 template <typename T>
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 
720 template <typename T>
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 
762 template <typename T>
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 
782 template <typename T>
785 {
786  return static_cast<Ts_TypedSplineData<T>*>(
787  Ts_GetSplineData(spline));
788 }
789 
790 template <typename T>
793 {
794  return static_cast<Ts_TypedSplineData<T>*>(
795  Ts_GetSplineData(spline));
796 }
797 
798 
800 
801 #endif
void PushKnot(const Ts_KnotData *knotData, const VtDictionary &customData) override
Definition: splineData.h:324
std::vector< Ts_TypedKnotData< T > > knots
Definition: splineData.h:237
std::unordered_map< TsTime, VtDictionary > customData
Definition: splineData.h:171
Ts_KnotData * CloneKnotAtIndex(size_t index) const override
Definition: splineData.h:486
bool operator==(const Ts_SplineData &other) const override
Definition: splineData.h:287
virtual Ts_KnotData * CloneKnotAtIndex(size_t index) const =0
GT_API const UT_StringHolder time
virtual Ts_SplineData * Clone() const =0
const GLdouble * v
Definition: glcorearb.h:837
T GetPreValue() const
Definition: knotData.h:310
virtual bool operator==(const Ts_SplineData &other) const =0
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
bool HasValueBlockAtTime(TsTime time) const override
Definition: splineData.h:721
TsTime GetPreExtrapTime() const
#define TF_CODING_ERROR
TsTime preTanWidth
Definition: knotData.h:106
void RemoveKnotAtTime(TsTime time) override
Definition: splineData.h:574
virtual bool HasValueBlocks() const =0
virtual void ClearKnots()=0
virtual double GetKnotValueAsDouble(size_t index) const =0
ImageBuf OIIO_API min(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
IMATH_HOSTDEVICE IMATH_CONSTEXPR14 Quat< T > spline(const Quat< T > &q0, const Quat< T > &q1, const Quat< T > &q2, const Quat< T > &q3, T t) IMATH_NOEXCEPT
Definition: ImathQuat.h:621
double GetKnotPreValueAsDouble(size_t index) const override
Definition: splineData.h:558
TsExtrapolation postExtrapolation
Definition: splineData.h:159
bool HasInnerLoops(size_t *firstProtoIndexOut=nullptr) const
TsCurveType curveType
Definition: splineData.h:157
constexpr auto in(type t, int set) -> bool
Definition: core.h:611
virtual double GetKnotPreValueAsDouble(size_t index) const =0
bool UpdateKnotTangentsAtIndex(size_t index) override
Definition: splineData.h:763
std::vector< TsTime > times
Definition: splineData.h:168
GA_API const UT_StringHolder scale
virtual size_t SetKnot(const Ts_KnotData *knotData, const VtDictionary &customData)=0
GLint GLsizei GLsizei height
Definition: glcorearb.h:103
double GetKnotValueAsDouble(size_t index) const override
Definition: splineData.h:548
virtual size_t SetKnotFromDouble(const Ts_TypedKnotData< double > *knotData, const VtDictionary &customData)=0
GLintptr offset
Definition: glcorearb.h:665
TsExtrapolation preExtrapolation
Definition: splineData.h:158
virtual Ts_KnotData * GetKnotPtrAtIndex(size_t index)=0
size_t GetKnotStructSize() const override
Definition: splineData.h:274
virtual bool HasValueBlockAtTime(TsTime time) const =0
TsCurveType
Definition: types.h:92
double GetPreExtrapValue() const
virtual ~Ts_SplineData()
virtual Ts_KnotData * CloneKnotAtTime(TsTime time) const =0
Ts_SplineData * Clone() const override
Definition: splineData.h:281
TfType GetValueType() const override
Definition: splineData.h:263
Ts_TypedKnotData< double > GetKnotDataAsDouble(size_t index) const override
Definition: splineData.h:526
virtual bool UpdateKnotTangentsAtIndex(size_t index)=0
Ts_KnotData * CloneKnotAtTime(TsTime time) const override
Definition: splineData.h:494
VT_API bool empty() const
true if the VtDictionary's size is 0.
void ClearKnots() override
Definition: splineData.h:566
Ts_TypedSplineData< T > * Ts_GetTypedSplineData(TsSpline &spline)
Definition: splineData.h:784
void ReserveForKnotCount(size_t count) override
Definition: splineData.h:316
TsTime GetPostExtrapTime() const
double GetPostExtrapValue() const
size_t SetKnot(const Ts_KnotData *knotData, const VtDictionary &customData) override
Definition: splineData.h:380
bool Ts_IsFinite(T value)
Definition: typeHelpers.h:70
virtual Ts_TypedKnotData< double > GetKnotDataAsDouble(size_t index) const =0
TsLoopParams loopParams
Definition: splineData.h:160
virtual void PushKnot(const Ts_KnotData *knotData, const VtDictionary &customData)=0
GLuint index
Definition: glcorearb.h:786
virtual TfType GetValueType() const =0
bool HasValueBlocks() const override
Definition: splineData.h:696
TsTime time
Definition: knotData.h:102
virtual void ApplyOffsetAndScale(TsTime offset, double scale)=0
ImageBuf OIIO_API max(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
void ApplyOffsetAndScale(TsTime offset, double scale) override
Definition: splineData.h:620
Definition: type.h:47
GLint GLsizei width
Definition: glcorearb.h:103
Ts_KnotData * GetKnotPtrAtIndex(size_t index) override
Definition: splineData.h:509
static Ts_SplineData * Create(TfType valueType, const Ts_SplineData *overallParamSource=nullptr)
virtual void RemoveKnotAtTime(TsTime time)=0
virtual void ReserveForKnotCount(size_t count)=0
TS_API bool UpdateTangents(const Ts_TypedKnotData< T > *prevData, const Ts_TypedKnotData< T > *nextData, const TsCurveType curveType)
size_t SetKnotFromDouble(const Ts_TypedKnotData< double > *knotData, const VtDictionary &customData) override
Definition: splineData.h:417
TsTime postTanWidth
Definition: knotData.h:110
Ts_SplineData * Ts_GetSplineData(TsSpline &spline)
virtual size_t GetKnotStructSize() const =0
GLint GLsizei count
Definition: glcorearb.h:405