HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
knot.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_KNOT_H
9 #define PXR_BASE_TS_KNOT_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/vt/value.h"
18 #include "pxr/base/gf/half.h"
19 #include "pxr/base/tf/diagnostic.h"
21 #include "pxr/base/tf/type.h"
22 
23 #include <iosfwd>
24 #include <memory>
25 #include <optional>
26 #include <string>
27 #include <type_traits>
28 
30 
31 
32 /// A knot belonging to a TsSpline.
33 ///
34 /// This class is non-templated, but can hold data for varying value types
35 /// (double, float, and half). All knots in a spline must have the same value
36 /// type.
37 ///
38 /// \sa TsTypedKnot
39 ///
40 class TsKnot
41 {
42 public:
43  /// \name Construction and value semantics
44  ///
45  /// Unlike splines, knots have a fixed value type from the time they are
46  /// constructed.
47  ///
48  /// Knots are fairly small, so copying them is not particularly expensive.
49  /// Move construction and assignment are supported, but this is only
50  /// beneficial when there is custom data.
51  ///
52  /// @{
53 
54  /// Default constructor creates a double-typed knot.
55  TS_API
56  TsKnot();
57 
58  /// Creates a knot with a specified value type.
59  TS_API
60  TsKnot(
61  TfType valueType);
62 
63  /// Creates a knot with a specified value type and curve type.
64  ///
65  /// \deprecated The use of curveType for knots is deprecated, knots
66  /// now work in splines of any curve type. This constructor will be
67  /// removed in a future release.
68  TS_API
69  TsKnot(
70  TfType valueType,
71  TsCurveType curveType);
72 
73  TS_API
74  TsKnot(const TsKnot &other);
75 
76  TS_API
77  TsKnot(TsKnot &&other);
78 
79  TS_API
80  ~TsKnot();
81 
82  TS_API
83  TsKnot& operator=(const TsKnot &other);
84 
85  TS_API
86  TsKnot& operator=(TsKnot &&other);
87 
88  TS_API
89  bool operator==(const TsKnot &other) const;
90 
91  TS_API
92  bool operator!=(const TsKnot &other) const;
93 
94  /// @}
95  /// \name Knot time
96  /// @{
97 
98  TS_API
99  bool SetTime(
100  TsTime time);
101 
102  TS_API
103  TsTime GetTime() const;
104 
105  /// @}
106  /// \name Interpolation mode
107  /// @{
108 
109  /// Sets the interpolation mode of the spline segment following this knot.
110  TS_API
112 
113  TS_API
115 
116  /// @}
117  /// \name Knot value
118  /// @{
119 
120  TS_API
121  TfType GetValueType() const;
122 
123  template <typename T>
124  bool IsHolding() const;
125 
126  TS_API
127  bool SetValue(
128  VtValue value);
129 
130  template <typename T>
131  bool SetValue(
132  const T value);
133 
134  TS_API
135  bool GetValue(
136  VtValue *valueOut) const;
137 
138  template <typename T>
139  bool GetValue(
140  T *valueOut) const;
141 
142  /// @}
143  /// \name Dual values
144  /// @{
145 
146  TS_API
147  bool IsDualValued() const;
148 
149  TS_API
150  bool SetPreValue(
151  VtValue value);
152 
153  template <typename T>
154  bool SetPreValue(
155  const T value);
156 
157  TS_API
158  bool GetPreValue(
159  VtValue *valueOut) const;
160 
161  template <typename T>
162  bool GetPreValue(
163  T *valueOut) const;
164 
165  TS_API
166  bool ClearPreValue();
167 
168  /// @}
169  /// \name Curve type
170  ///
171  /// \deprecated Knots no longer need to have a curve type that matches
172  /// the curve type of the spline. \c SetCurveType and \c GetCurveType
173  /// no longer do anything useful and will be removed in a future
174  /// release.
175  ///
176  /// @{
177 
178  TS_API
179  bool SetCurveType(TsCurveType curveType);
180 
181  TS_API
182  TsCurveType GetCurveType() const;
183 
184  /// @}
185  /// \name Pre-tangent
186  ///
187  /// Tangents are expressed as width and slope.
188  ///
189  /// It is an error to read or write widths for Hermite knots.
190  ///
191  /// \note Note that Maya uses tangents in a different format.
192  /// \ref TsConvertFromStandardTangent is a utility function that can
193  /// convert a standard width and slope to values expected by Maya.
194  ///
195  /// @{
196 
197  TS_API
198  bool SetPreTanWidth(TsTime width);
199 
200  TS_API
201  TsTime GetPreTanWidth() const;
202 
203  TS_API
204  bool SetPreTanSlope(VtValue slope);
205 
206  template <typename T>
207  bool SetPreTanSlope(T slope);
208 
209  TS_API
210  bool GetPreTanSlope(VtValue *slopeOut) const;
211 
212  template <typename T>
213  bool GetPreTanSlope(T *slopeOut) const;
214 
215  /// Set the pre-tangent algorithm.
216  ///
217  /// When this knot is part of a spline, the tangent algorithm will
218  /// be used to compute updated tangent values, potentially
219  /// overriding explicit values set by SetPreTanWidth and
220  /// SetPreTanSlope. The algorithm TsTangentAlgorithmNone is used
221  /// by default meaning the explicitly set values will be used unless
222  /// a different algorithm is set.
223  TS_API
224  bool SetPreTanAlgorithm(TsTangentAlgorithm algorithm);
225 
226  /// Get the pre-tangent algorithm
227  TS_API
229 
230  /// @}
231  /// \name Post-tangent
232  /// @{
233 
234  TS_API
235  bool SetPostTanWidth(TsTime width);
236 
237  TS_API
238  TsTime GetPostTanWidth() const;
239 
240  TS_API
241  bool SetPostTanSlope(VtValue slope);
242 
243  template <typename T>
244  bool SetPostTanSlope(T slope);
245 
246  TS_API
247  bool GetPostTanSlope(VtValue *slopeOut) const;
248 
249  template <typename T>
250  bool GetPostTanSlope(T *slopeOut) const;
251 
252  /// Set the post-tangent algorithm.
253  ///
254  /// When this knot is part of a spline, the tangent algorithm will
255  /// be used to compute updated tangent values, potentially
256  /// overriding explicit values set by SetPostTanWidth and
257  /// SetPostTanSlope. The algorithm TsTangentAlgorithmNone is used
258  /// by default meaning the explicitly set values will be used unless
259  /// a different algorithm is set.
260  TS_API
261  bool SetPostTanAlgorithm(TsTangentAlgorithm algorithm);
262 
263  /// Get the post-tangent algorithm
264  TS_API
266 
267  /// @}
268  /// \name Computed Tangents
269  /// @{
270 
271  /// \brief Update tangent values algorithmically.
272  ///
273  /// Update the pre- and post-tangent values exactly the way a TsSpline
274  /// would update them if this knot were added to the spline between
275  /// prevKnot and nextKnot. If there is no previous and/or next knot,
276  /// you can pass \c std::nullopt to indicate that the optional value
277  /// is empty.
278  ///
279  /// If prevKnot is not empty, its time must be before this knot. Similarly
280  /// if nextKnot is not empty, its time must be after this knot. If either
281  /// condition is not met, a coding error will be emitted and no changes to
282  /// the tangent values will be made.
283  ///
284  /// The curveType argument should be TsCurveTypeHermite if the knot is
285  /// intended to be added to a Hermite spline. The curve type may affect the
286  /// tangent calculations. Specifically, tangent widths in Hermite splines
287  /// are always 1/3 of the width of the segment they are in, even if the
288  /// tangent algorithm is \c TsTangentAlgorithmNone.
289  ///
290  /// True will be returned if either of the algorithms in this knot run
291  /// successfully, even if no values change. False will be returned
292  /// otherwise.
293  TS_API
294  bool UpdateTangents(const std::optional<TsKnot> prevKnot,
295  const std::optional<TsKnot> nextKnot,
296  TsCurveType curveType = TsCurveTypeBezier);
297 
298  /// @}
299  /// \name Custom data
300  ///
301  /// Knots may have custom data: an arbitrary VtDictionary of key/value
302  /// pairs.
303  ///
304  /// Custom data does not affect evaluation. It is only for clients' use.
305  ///
306  /// When knots are edited, Ts does not automatically alter any custom data.
307  /// This means that, if splines are written out, edited by other clients
308  /// that do not recognize a particular kind of custom data, and read back
309  /// in, then that custom data may become outdated.
310  ///
311  /// @{
312 
313  TS_API
314  bool SetCustomData(
315  VtDictionary customData);
316 
317  TS_API
318  VtDictionary GetCustomData() const;
319 
320  TS_API
321  bool SetCustomDataByKey(
322  const std::string &keyPath,
323  VtValue value);
324 
325  TS_API
327  const std::string &keyPath) const;
328 
329  /// @}
330  /// \name Continuity queries
331  /// @{
332 
333  /// <b>Not yet implemented.</b>
334  TS_API
335  bool IsC0Continuous() const;
336 
337  /// <b>Not yet implemented.</b>
338  TS_API
339  bool IsG1Continuous() const;
340 
341  /// <b>Not yet implemented.</b>
342  TS_API
343  bool IsC1Continuous() const;
344 
345  /// @}
346 
347 protected:
348  friend class TsSpline;
349  friend class TsKnotMap;
350  friend class TsRegressionPreventer;
351 
352  // Constructor for copying knot data from SplineData. The data has been
353  // copied for us, and we take ownership of it.
354  TsKnot(
355  Ts_KnotData *data,
356  TfType valueType,
357  VtDictionary &&customData);
358 
359  // Accessors for low-level knot data.
360  Ts_KnotData* _GetData() { return _data; }
361  const Ts_KnotData* _GetData() const { return _data; }
362 
363 private:
364  template <typename T>
365  bool _CheckInParam(T value) const;
366 
367  template <typename T>
368  bool _CheckOutParam(T *valueOut) const;
369 
370  bool _CheckSetWidth(TsTime width) const;
371  bool _CheckInParamVt(VtValue value) const;
372  bool _CheckOutParamVt(VtValue* value) const;
373 
374  template <typename T>
375  Ts_TypedKnotData<T>* _TypedData() const;
376 
377  template <typename T>
378  const Ts_TypedKnotData<T>* _ConstTypedData() const;
379 
380 private:
381  // Main knot fields. Never null. The data is on the heap, and we own it
382  // exclusively, but we don't use unique_ptr because we need to deallocate in
383  // a type-aware way; see the destructor.
384  Ts_KnotData* _data;
385 
386  // Proxy object, for typed data access. Never null.
387  //
388  // XXX: it would be possible to eliminate this member by encoding value type
389  // in a bitfield in Ts_KnotData; there are only three possible value types,
390  // and there are plenty of unused bits in the Ts_KnotData padding. Then we
391  // could create proxy objects on the fly, or make them singletons.
392  //
393  std::unique_ptr<Ts_KnotDataProxy> _proxy;
394 
395  // Custom data. Optional; may be empty.
396  VtDictionary _customData;
397 };
398 
399 /// Output a text representation of a spline to a stream.
400 TS_API
401 std::ostream& operator<<(std::ostream& out, const TsKnot &knot);
402 
403 
404 /// A convenience for constructing knots with specified types.
405 ///
406 /// Instead of writing:
407 ///
408 /// <pre>
409 /// TsKnot knot1(TfType::Find<double>());
410 /// TsKnot knot2(TfType::Find<float>());
411 /// TsKnot knot3(TfType::Find<GfHalf>());
412 /// </pre>
413 ///
414 /// One may write:
415 ///
416 /// <pre>
417 /// TsDoubleKnot knot1;
418 /// TsFloatKnot knot2;
419 /// TsHalfKnot knot3;
420 /// </pre>
421 ///
422 template <typename T,
424 class TsTypedKnot : public TsKnot
425 {
426 public:
428 };
429 
430 /// \class TsDoubleKnot
431 /// A knot-construction convenience. See TsTypedKnot.
433 
434 /// \class TsFloatKnot
435 /// A knot-construction convenience. See TsTypedKnot.
437 
438 /// \class TsHalfKnot
439 /// A knot-construction convenience. See TsTypedKnot.
441 
442 // Make sure we have coverage for all allowed types.
443 #define _MAKE_CLAUSE(unused, tuple) \
444  static_assert(std::is_same_v<TF_PP_CAT(TF_PP_CAT(Ts, \
445  TS_SPLINE_VALUE_TYPE_NAME(tuple)), Knot), \
446  TsTypedKnot<TS_SPLINE_VALUE_CPP_TYPE(tuple)>>, \
447  "Incorrect type alias for TsKnot type: " #tuple);
449 #undef _MAKE_CLAUSE
450 
451 
452 
453 ////////////////////////////////////////////////////////////////////////////////
454 // TEMPLATE HELPERS
455 
456 template <typename T>
457 bool TsKnot::_CheckInParam(const T value) const
458 {
459  if constexpr (!Ts_IsSupportedValueType<T>::value)
460  {
461  static_assert(Ts_IsSupportedValueType<T>::value,
462  "Cannot pass non-floating-point type as T-typed knot parameter");
463  return false;
464  }
465  else
466  {
467  if (GetValueType() != Ts_GetType<T>())
468  {
470  "Cannot set '%s' value into knot of type '%s'",
471  Ts_GetType<T>().GetTypeName().c_str(),
472  GetValueType().GetTypeName().c_str());
473  return false;
474  }
475 
476  if (!Ts_IsFinite(value))
477  {
478  TF_CODING_ERROR("Set values must be finite.");
479  return false;
480  }
481 
482  return true;
483  }
484 }
485 
486 template <typename T>
487 bool TsKnot::_CheckOutParam(T *valueOut) const
488 {
489  if constexpr (!Ts_IsSupportedValueType<T>::value)
490  {
491  static_assert(Ts_IsSupportedValueType<T>::value,
492  "Cannot pass non-floating-point type as T-typed knot parameter");
493  return false;
494  }
495  else
496  {
497  if (!valueOut)
498  {
499  TF_CODING_ERROR("Null pointer");
500  return false;
501  }
502 
503  if (GetValueType() != Ts_GetType<T>())
504  {
506  "Cannot read from knot of type '%s' into '%s'",
507  GetValueType().GetTypeName().c_str(),
508  Ts_GetType<T>().GetTypeName().c_str());
509  return false;
510  }
511 
512  return true;
513  }
514 }
515 
516 template <typename T>
518 TsKnot::_TypedData() const
519 {
520  return static_cast<Ts_TypedKnotData<T>*>(_data);
521 }
522 
523 template <typename T>
524 const Ts_TypedKnotData<T>*
525 TsKnot::_ConstTypedData() const
526 {
527  return static_cast<const Ts_TypedKnotData<T>*>(_data);
528 }
529 
530 ////////////////////////////////////////////////////////////////////////////////
531 // TEMPLATE IMPLEMENTATIONS
532 
533 template <typename T>
534 bool TsKnot::IsHolding() const
535 {
536  return GetValueType() == Ts_GetType<T>();
537 }
538 
539 template <typename T>
540 bool TsKnot::SetValue(const T value)
541 {
542  if (!_CheckInParam(value))
543  {
544  return false;
545  }
546 
547  _TypedData<T>()->value = value;
548  return true;
549 }
550 
551 template <typename T>
552 bool TsKnot::GetValue(T *valueOut) const
553 {
554  if (!_CheckOutParam(valueOut))
555  {
556  return false;
557  }
558 
559  *valueOut = _ConstTypedData<T>()->value;
560  return true;
561 }
562 
563 template <typename T>
565 {
566  if (!_CheckInParam(value))
567  {
568  return false;
569  }
570 
571  _data->dualValued = true;
572  _TypedData<T>()->preValue = value;
573  return true;
574 }
575 
576 template <typename T>
577 bool TsKnot::GetPreValue(T* const valueOut) const
578 {
579  if (!_CheckOutParam(valueOut))
580  {
581  return false;
582  }
583 
584  if (_data->dualValued)
585  {
586  *valueOut = _ConstTypedData<T>()->preValue;
587  }
588  else
589  {
590  *valueOut = _ConstTypedData<T>()->value;
591  }
592 
593  return true;
594 }
595 
596 ////////////////////////////////////////////////////////////////////////////////
597 // Pre-Tangent
598 
599 template <typename T>
600 bool TsKnot::SetPreTanSlope(const T slope)
601 {
602  if (!_CheckInParam(slope))
603  {
604  return false;
605  }
606 
607  _TypedData<T>()->preTanSlope = slope;
608  return true;
609 }
610 
611 template <typename T>
612 bool TsKnot::GetPreTanSlope(T* const slopeOut) const
613 {
614  if (!_CheckOutParam(slopeOut))
615  {
616  return false;
617  }
618 
619  *slopeOut = _ConstTypedData<T>()->GetPreTanSlope();
620  return true;
621 }
622 
623 ////////////////////////////////////////////////////////////////////////////////
624 // Post-Tangent
625 
626 template <typename T>
627 bool TsKnot::SetPostTanSlope(const T slope)
628 {
629  if (!_CheckInParam(slope))
630  {
631  return false;
632  }
633 
634  _TypedData<T>()->postTanSlope = slope;
635  return true;
636 }
637 
638 template <typename T>
639 bool TsKnot::GetPostTanSlope(T* const slopeOut) const
640 {
641  if (!_CheckOutParam(slopeOut))
642  {
643  return false;
644  }
645 
646  *slopeOut = _ConstTypedData<T>()->GetPostTanSlope();
647  return true;
648 }
649 
650 
652 
653 #endif
TS_API bool IsC0Continuous() const
Not yet implemented.
TS_API bool SetCustomDataByKey(const std::string &keyPath, VtValue value)
TS_API bool SetTime(TsTime time)
TS_API bool GetValue(VtValue *valueOut) const
TS_API bool operator==(const TsKnot &other) const
Default constructor creates a double-typed knot.
GT_API const UT_StringHolder time
TS_API TsTangentAlgorithm GetPostTanAlgorithm() const
Get the post-tangent algorithm.
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
GLsizei const GLfloat * value
Definition: glcorearb.h:824
TS_API bool SetPreTanAlgorithm(TsTangentAlgorithm algorithm)
const Ts_KnotData * _GetData() const
Definition: knot.h:361
#define TF_CODING_ERROR
TS_API TsCurveType GetCurveType() const
TS_API bool GetPreTanSlope(VtValue *slopeOut) const
TS_API bool SetPreValue(VtValue value)
TS_API bool SetValue(VtValue value)
#define _MAKE_CLAUSE(unused, tuple)
Definition: knot.h:443
TS_API bool SetPreTanSlope(VtValue slope)
TS_API TfType GetValueType() const
TS_API TsInterpMode GetNextInterpolation() const
Sets the interpolation mode of the spline segment following this knot.
TS_API TsTime GetPostTanWidth() const
TS_API bool SetPreTanWidth(TsTime width)
TsCurveType
Definition: types.h:92
TS_API std::ostream & operator<<(std::ostream &out, const TsKnot &knot)
Output a text representation of a spline to a stream.
TS_API bool SetCustomData(VtDictionary customData)
Ts_KnotData * _GetData()
Definition: knot.h:360
TS_API TsTime GetTime() const
TS_API bool SetNextInterpolation(TsInterpMode mode)
Sets the interpolation mode of the spline segment following this knot.
TS_API bool operator!=(const TsKnot &other) const
Default constructor creates a double-typed knot.
TS_API TsTangentAlgorithm GetPreTanAlgorithm() const
Get the pre-tangent algorithm.
TS_API TsTime GetPreTanWidth() const
#define TS_API
Definition: api.h:24
GLenum mode
Definition: glcorearb.h:99
Definition: knot.h:40
TS_API VtValue GetCustomDataByKey(const std::string &keyPath) const
bool dualValued
Definition: knotData.h:129
TS_API bool UpdateTangents(const std::optional< TsKnot > prevKnot, const std::optional< TsKnot > nextKnot, TsCurveType curveType=TsCurveTypeBezier)
Update tangent values algorithmically.
TS_API bool SetPostTanWidth(TsTime width)
bool Ts_IsFinite(T value)
Definition: typeHelpers.h:70
bool IsHolding() const
Definition: knot.h:534
TS_API bool IsC1Continuous() const
Not yet implemented.
#define TF_PP_SEQ_FOR_EACH(_macro, data, seq)
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
PXR_NAMESPACE_OPEN_SCOPE TfType Ts_GetType()
TS_API bool GetPostTanSlope(VtValue *slopeOut) const
Definition: type.h:47
TS_API bool SetPostTanSlope(VtValue slope)
GLint GLsizei width
Definition: glcorearb.h:103
#define TS_SPLINE_SUPPORTED_VALUE_TYPES
Definition: types.h:29
TS_API bool IsG1Continuous() const
Not yet implemented.
OIIO_UTIL_API const char * c_str(string_view str)
TS_API bool ClearPreValue()
TS_API ~TsKnot()
Default constructor creates a double-typed knot.
TS_API TsKnot & operator=(const TsKnot &other)
Default constructor creates a double-typed knot.
TS_API bool SetCurveType(TsCurveType curveType)
TS_API bool SetPostTanAlgorithm(TsTangentAlgorithm algorithm)
TS_API VtDictionary GetCustomData() const
TS_API TsKnot()
Default constructor creates a double-typed knot.
TS_API bool GetPreValue(VtValue *valueOut) const
Definition: value.h:89
TsTangentAlgorithm
Definition: types.h:153
TsInterpMode
Definition: types.h:82
Definition: format.h:1821
TS_API bool IsDualValued() const
TsTypedKnot()
Definition: knot.h:427