Loading...
Searching...
No Matches
knotData.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_KNOT_DATA_H
9#define PXR_BASE_TS_KNOT_DATA_H
10
11#include "pxr/pxr.h"
12#include "pxr/base/ts/api.h"
13#include "pxr/base/ts/types.h"
14#include "pxr/base/ts/typeHelpers.h"
15#include "pxr/base/vt/value.h"
16#include "pxr/base/tf/type.h"
17
18#include <memory>
19#include <cstring>
20
21PXR_NAMESPACE_OPEN_SCOPE
22
23
24// Ts API objects (Spline, Knot) are non-templated, but they can represent
25// different value types (double, float, half), and internally we handle those
26// different value types with templates. Some knot members (like time) are
27// type-independent, while others (like value) are type-dependent. All knots in
28// a spline have the same value type.
29//
30// Splines can have many knots, and we try to take up as little memory as
31// possible in storing them. We also try to be as fast as possible in accessing
32// them, but the combination of non-templated API classes and templated data is
33// a form of type erasure that requires a compromise: we use virtual methods to
34// retrieve data.
35//
36// We manage knot data with two class hierarchies:
37//
38// - The data itself is stored as a plain struct. There are two halves: a
39// non-templated base struct that contains the type-independent members, and a
40// templated derived struct that contains the type-dependent members. By
41// making the data a plain struct, we avoid storing a vtable pointer for every
42// instance, but in return we have no convenient way to get at the templated
43// data directly.
44//
45// - We access the type-dependent members using a proxy class. There is an
46// abstract base class that declares a virtual interface, and a templated
47// derived class that implements it. Proxy objects have a vtable pointer, and
48// contain a pointer to a templated derived data struct.
49
50
51// XXX TODO
52// review access patterns - do we need all?:
53// SplineData -> AsDouble -> direct access
54// SplineData virtuals
55// Ts_Get[Typed]SplineData
56// Knot -> proxy
57// Knot -> _TypedData -> direct access
58// TsDispatchToValueTypeTemplate
59
60
61// Non-template base class for knot data.
62//
63struct Ts_KnotData
64{
65public:
66 // Typically used by Create(), but can be invoked directly for clients that
67 // don't care about the value dimension, and instantiate this struct without
68 // subclassing.
69 Ts_KnotData();
70
71 // Creates an appropriately subtyped instance on the heap.
72 static Ts_KnotData* Create(TfType valueType);
73
74 // Compares two KnotData structs. Ignores subclasses.
75 bool operator==(const Ts_KnotData &other) const;
76
77public:
78 // Helpers that switch on flags.
79
80 TsTime GetPreTanWidth() const
81 {
82 return preTanWidth;
83 }
84
85 TsTime GetPostTanWidth() const
86 {
87 return postTanWidth;
88 }
89
90 void SetPreTanWidth(const TsTime width)
91 {
92 preTanWidth = width;
93 }
94
95 void SetPostTanWidth(const TsTime width)
96 {
97 postTanWidth = width;
98 }
99
100public:
101 // Knot time.
102 TsTime time;
103
104 // Time width of the pre-tangent. Always non-negative. Ignored for Hermite
105 // knots.
106 TsTime preTanWidth;
107
108 // Time width of the post-tangent. Always non-negative. Ignored for
109 // Hermite knots.
110 TsTime postTanWidth;
111
112 // BITFIELDS - note: for enum-typed bitfields, we declare one bit more than
113 // is minimally needed to represent all declared enum values. For example,
114 // TsCurveType has only two values, so it should be representable in one
115 // bit. However, compilers are free to choose the underlying representation
116 // of enums, and some platforms choose signed values, meaning that we
117 // actually need one bit more, so that we can hold the sign bit. We could
118 // declare the enums with unsigned underlying types, but that runs into a
119 // gcc 9.2 bug. We can spare the extra bit; alignment means there is no
120 // difference in struct size.
121
122 // Interpolation mode for the segment following this knot.
123 TsInterpMode nextInterp : 3;
124
125 // The spline type this knot belongs to, or is intended for.
126 TsCurveType curveType : 2; // deprecated
127
128 // Whether this knot is dual-valued (value discontinuity at the knot).
129 bool dualValued : 1;
130
131 // The pre- and post-tangent algorithms.
132 TsTangentAlgorithm preTanAlgorithm : 4;
133 TsTangentAlgorithm postTanAlgorithm : 4;
134};
135
136
137// Data for one knot in a spline.
138//
139// Tangents are expressed as width and slope.
140//
141template <typename T>
142struct Ts_TypedKnotData :
143 public Ts_KnotData
144{
145public:
146 Ts_TypedKnotData();
147
148 bool operator==(const Ts_TypedKnotData<T> &other) const;
149
150public:
151 // Helpers that switch on flags.
152 T GetPreValue() const;
153 T GetPreTanSlope() const;
154 T GetPreTanHeight() const;
155 T GetPostTanSlope() const;
156 T GetPostTanHeight() const;
157
158
159 // Compute algorithmic tangents for this Ts_KnotData based on the provided
160 // prev and next Ts_KnotData. See TsKnot::UpdateTangents for documentation.
161 TS_API
162 bool UpdateTangents(const Ts_TypedKnotData<T>* prevData,
163 const Ts_TypedKnotData<T>* nextData,
164 const TsCurveType curveType);
165
166protected:
167 TS_API
168 bool _UpdateTangent(const Ts_TypedKnotData<T>* prevData,
169 const Ts_TypedKnotData<T>* nextData,
170 TsCurveType curveType,
171 bool updatePre);
172
173 TS_API
174 bool _UpdateTangentAutoEase(const Ts_TypedKnotData<T>* prevData,
175 const Ts_TypedKnotData<T>* nextData,
176 bool updatePre);
177
178public:
179 // Value at this knot.
180 T value;
181
182 // If dual-valued, the pre-value at this knot.
183 T preValue;
184
185 // preTanSlope stores the slope of the pre-tangent, rise over run, value
186 // height divided by time width.
187 T preTanSlope;
188
189 // postTanSlope stores the slope of the post-tangent, rise over run, value
190 // height divided by time width.
191 T postTanSlope;
192};
193
194// Virtual interface to TypedKnotData.
195//
196// VtValue parameters are not type-checked. They are blindly cast. Callers
197// must verify types.
198//
199class Ts_KnotDataProxy
200{
201public:
202 // Creates an appropriately subtyped instance.
203 static std::unique_ptr<Ts_KnotDataProxy>
204 Create(Ts_KnotData *data, TfType valueType);
205
206 virtual ~Ts_KnotDataProxy();
207
208 virtual Ts_KnotData* CloneData() const = 0;
209 virtual void DeleteData() = 0;
210
211 virtual TfType GetValueType() const = 0;
212 virtual bool IsDataEqualTo(const Ts_KnotData &other) const = 0;
213
214 virtual void SetValue(VtValue value) = 0;
215 virtual void GetValue(VtValue *valueOut) const = 0;
216 virtual void SetPreValue(VtValue value) = 0;
217 virtual void GetPreValue(VtValue *valueOut) const = 0;
218
219 virtual void SetPreTanSlope(VtValue slope) = 0;
220 virtual void GetPreTanSlope(VtValue *slopeOut) const = 0;
221 virtual void SetPostTanSlope(VtValue slope) = 0;
222 virtual void GetPostTanSlope(VtValue *slopeOut) const = 0;
223
224 virtual bool UpdateTangents(const Ts_KnotDataProxy* prevProxy,
225 const Ts_KnotDataProxy* nextProxy,
226 const TsCurveType curveType) = 0;
227};
228
229
230// A means of accessing TypedKnotData.
231//
232template <typename T>
233class Ts_TypedKnotDataProxy final :
234 public Ts_KnotDataProxy
235{
236public:
237 explicit Ts_TypedKnotDataProxy(Ts_TypedKnotData<T> *data);
238
239 Ts_KnotData* CloneData() const override;
240 void DeleteData() override;
241
242 TfType GetValueType() const override;
243 bool IsDataEqualTo(const Ts_KnotData &other) const override;
244
245 void SetValue(VtValue value) override;
246 void GetValue(VtValue *valueOut) const override;
247 void SetPreValue(VtValue value) override;
248 void GetPreValue(VtValue *valueOut) const override;
249
250 void SetPreTanSlope(VtValue slope) override;
251 void GetPreTanSlope(VtValue *slopeOut) const override;
252 void SetPostTanSlope(VtValue slope) override;
253 void GetPostTanSlope(VtValue *slopeOut) const override;
254
255 bool UpdateTangents(const Ts_KnotDataProxy* prevProxy,
256 const Ts_KnotDataProxy* nextProxy,
257 const TsCurveType curveType) override;
258
259private:
260 Ts_TypedKnotData<T> *_data;
261};
262
263
265// TEMPLATE IMPLEMENTATIONS
266
267template <typename T>
268Ts_TypedKnotData<T>::Ts_TypedKnotData()
269 : Ts_KnotData(),
270 value(T()),
271 preValue(T()),
272 preTanSlope(T()),
273 postTanSlope(T())
274{
275}
276
277#define COMP(member) \
278 if (member != other.member) \
279 { \
280 return false; \
281 }
282
283template <typename T>
284bool Ts_TypedKnotData<T>::operator==(
285 const Ts_TypedKnotData<T> &other) const
286{
287 COMP(time);
288 COMP(preTanWidth);
289 COMP(postTanWidth);
290 COMP(dualValued);
291 COMP(nextInterp);
292 // CurveType for knots has been deprecated and we no longer consider its
293 // value when testing for equality.
294 // COMP(curveType);
295
296 COMP(value);
297 COMP(preValue);
298 COMP(preTanSlope);
299 COMP(postTanSlope);
300
301 COMP(preTanAlgorithm);
302 COMP(postTanAlgorithm);
303
304 return true;
305}
306
307#undef COMP
308
309template <typename T>
310T Ts_TypedKnotData<T>::GetPreValue() const
311{
312 return (dualValued ? preValue : value);
313}
314
315template <typename T>
316T Ts_TypedKnotData<T>::GetPreTanSlope() const
317{
318 return preTanSlope;
319}
320
321template <typename T>
322T Ts_TypedKnotData<T>::GetPreTanHeight() const
323{
324 return -preTanWidth * preTanSlope;
325}
326
327template <typename T>
328T Ts_TypedKnotData<T>::GetPostTanSlope() const
329{
330 return postTanSlope;
331}
332
333template <typename T>
334T Ts_TypedKnotData<T>::GetPostTanHeight() const
335{
336 return postTanWidth * postTanSlope;
337}
338
340// TypedKnotDataProxy
341
342template <typename T>
343Ts_TypedKnotDataProxy<T>::Ts_TypedKnotDataProxy(
344 Ts_TypedKnotData<T> *data)
345 : _data(data)
346{
347}
348
349template <typename T>
350Ts_KnotData* Ts_TypedKnotDataProxy<T>::CloneData() const
351{
352 return new Ts_TypedKnotData<T>(*_data);
353}
354
355template <typename T>
356void Ts_TypedKnotDataProxy<T>::DeleteData()
357{
358 delete _data;
359}
360
361template <typename T>
362TfType Ts_TypedKnotDataProxy<T>::GetValueType() const
363{
364 return Ts_GetType<T>();
365}
366
367template <typename T>
368bool Ts_TypedKnotDataProxy<T>::IsDataEqualTo(const Ts_KnotData &other) const
369{
370 // Force-downcast to our value type. Callers must verify types match.
371 const Ts_TypedKnotData<T> *typedOther =
372 static_cast<const Ts_TypedKnotData<T>*>(&other);
373
374 return *_data == *typedOther;
375}
376
377template <typename T>
378void Ts_TypedKnotDataProxy<T>::SetValue(
379 const VtValue value)
380{
381 _data->value = value.UncheckedGet<T>();
382}
383
384template <typename T>
385void Ts_TypedKnotDataProxy<T>::GetValue(
386 VtValue* const valueOut) const
387{
388 *valueOut = VtValue(_data->value);
389}
390
391template <typename T>
392void Ts_TypedKnotDataProxy<T>::SetPreValue(
393 const VtValue value)
394{
395 _data->preValue = value.UncheckedGet<T>();
396}
397
398template <typename T>
399void Ts_TypedKnotDataProxy<T>::GetPreValue(
400 VtValue* const valueOut) const
401{
402 *valueOut = VtValue(_data->preValue);
403}
404
405template <typename T>
406void Ts_TypedKnotDataProxy<T>::SetPreTanSlope(
407 const VtValue slope)
408{
409 _data->preTanSlope = slope.UncheckedGet<T>();
410}
411
412template <typename T>
413void Ts_TypedKnotDataProxy<T>::GetPreTanSlope(
414 VtValue* const slopeOut) const
415{
416 *slopeOut = VtValue(_data->GetPreTanSlope());
417}
418
419template <typename T>
420void Ts_TypedKnotDataProxy<T>::SetPostTanSlope(
421 const VtValue slope)
422{
423 _data->postTanSlope = slope.UncheckedGet<T>();
424}
425
426template <typename T>
427void Ts_TypedKnotDataProxy<T>::GetPostTanSlope(
428 VtValue* const slopeOut) const
429{
430 *slopeOut = VtValue(_data->GetPostTanSlope());
431}
432
433template <typename T>
434bool Ts_TypedKnotDataProxy<T>::UpdateTangents(
435 const Ts_KnotDataProxy* prevProxy,
436 const Ts_KnotDataProxy* nextProxy,
437 const TsCurveType curveType)
438{
439 const Ts_TypedKnotDataProxy<T>* prevTypedProxy =
440 dynamic_cast<const Ts_TypedKnotDataProxy<T>*>(prevProxy);
441 const Ts_TypedKnotData<T>* prevData = (prevTypedProxy
442 ? prevTypedProxy->_data
443 : nullptr);
444 const Ts_TypedKnotDataProxy<T>* nextTypedProxy =
445 dynamic_cast<const Ts_TypedKnotDataProxy<T>*>(nextProxy);
446 const Ts_TypedKnotData<T>* nextData = (nextTypedProxy
447 ? nextTypedProxy->_data
448 : nullptr);
449
450 return _data->UpdateTangents(prevData, nextData, curveType);
451}
452
453
454PXR_NAMESPACE_CLOSE_SCOPE
455
456#endif
TfType represents a dynamic runtime type.
Definition: type.h:48
Provides a container which may hold any type, and provides introspection and iteration over array typ...
Definition: value.h:90
T const & UncheckedGet() const &
Returns a const reference to the held object if the held object is of type T.
Definition: value.h:1046