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// TsDispatchToStorageValueTypeTemplate
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_API 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 TfType valueType;
228};
229
230
231// A means of accessing TypedKnotData.
232//
233template <typename T>
234class Ts_TypedKnotDataProxy final :
235 public Ts_KnotDataProxy
236{
237public:
238 explicit Ts_TypedKnotDataProxy(Ts_TypedKnotData<T> *data);
239
240 Ts_KnotData* CloneData() const override;
241 void DeleteData() override;
242
243 // Note that T is the storage type and the TfType returned by
244 // GetValueType is the true type.
245 TfType GetValueType() const override;
246 bool IsDataEqualTo(const Ts_KnotData &other) const override;
247
248 void SetValue(VtValue value) override;
249 void GetValue(VtValue *valueOut) const override;
250 void SetPreValue(VtValue value) override;
251 void GetPreValue(VtValue *valueOut) const override;
252
253 void SetPreTanSlope(VtValue slope) override;
254 void GetPreTanSlope(VtValue *slopeOut) const override;
255 void SetPostTanSlope(VtValue slope) override;
256 void GetPostTanSlope(VtValue *slopeOut) const override;
257
258 bool UpdateTangents(const Ts_KnotDataProxy* prevProxy,
259 const Ts_KnotDataProxy* nextProxy,
260 const TsCurveType curveType) override;
261
262private:
263 Ts_TypedKnotData<T> *_data;
264};
265
266
268// TEMPLATE IMPLEMENTATIONS
269
270template <typename T>
271Ts_TypedKnotData<T>::Ts_TypedKnotData()
272 : Ts_KnotData(),
273 value(T()),
274 preValue(T()),
275 preTanSlope(T()),
276 postTanSlope(T())
277{
278}
279
280#define COMP(member) \
281 if (member != other.member) \
282 { \
283 return false; \
284 }
285
286template <typename T>
287bool Ts_TypedKnotData<T>::operator==(
288 const Ts_TypedKnotData<T> &other) const
289{
290 COMP(time);
291 COMP(preTanWidth);
292 COMP(postTanWidth);
293 COMP(dualValued);
294 COMP(nextInterp);
295 // CurveType for knots has been deprecated and we no longer consider its
296 // value when testing for equality.
297 // COMP(curveType);
298
299 COMP(value);
300 if (dualValued) {
301 COMP(preValue);
302 }
303 COMP(preTanSlope);
304 COMP(postTanSlope);
305
306 COMP(preTanAlgorithm);
307 COMP(postTanAlgorithm);
308
309 return true;
310}
311
312#undef COMP
313
314template <typename T>
315T Ts_TypedKnotData<T>::GetPreValue() const
316{
317 return (dualValued ? preValue : value);
318}
319
320template <typename T>
321T Ts_TypedKnotData<T>::GetPreTanSlope() const
322{
323 return preTanSlope;
324}
325
326template <typename T>
327T Ts_TypedKnotData<T>::GetPreTanHeight() const
328{
329 return -preTanWidth * preTanSlope;
330}
331
332template <typename T>
333T Ts_TypedKnotData<T>::GetPostTanSlope() const
334{
335 return postTanSlope;
336}
337
338template <typename T>
339T Ts_TypedKnotData<T>::GetPostTanHeight() const
340{
341 return postTanWidth * postTanSlope;
342}
343
345// TypedKnotDataProxy
346
347template <typename T>
348Ts_TypedKnotDataProxy<T>::Ts_TypedKnotDataProxy(
349 Ts_TypedKnotData<T> *data)
350 : _data(data)
351{
352}
353
354template <typename T>
355Ts_KnotData* Ts_TypedKnotDataProxy<T>::CloneData() const
356{
357 return new Ts_TypedKnotData<T>(*_data);
358}
359
360template <typename T>
361void Ts_TypedKnotDataProxy<T>::DeleteData()
362{
363 delete _data;
364}
365
366template <typename T>
367TfType Ts_TypedKnotDataProxy<T>::GetValueType() const
368{
369 return valueType;
370}
371
372template <typename T>
373bool Ts_TypedKnotDataProxy<T>::IsDataEqualTo(const Ts_KnotData &other) const
374{
375 // Force-downcast to our value type. Callers must verify types match.
376 const Ts_TypedKnotData<T> *typedOther =
377 static_cast<const Ts_TypedKnotData<T>*>(&other);
378
379 return *_data == *typedOther;
380}
381
382template <typename T>
383void Ts_TypedKnotDataProxy<T>::SetValue(
384 const VtValue value)
385{
386 _data->value = value.UncheckedGet<T>();
387}
388
389template <typename T>
390void Ts_TypedKnotDataProxy<T>::GetValue(
391 VtValue* const valueOut) const
392{
393 *valueOut = VtValue(_data->value);
394}
395
396template <typename T>
397void Ts_TypedKnotDataProxy<T>::SetPreValue(
398 const VtValue value)
399{
400 _data->preValue = value.UncheckedGet<T>();
401}
402
403template <typename T>
404void Ts_TypedKnotDataProxy<T>::GetPreValue(
405 VtValue* const valueOut) const
406{
407 *valueOut = VtValue(_data->preValue);
408}
409
410template <typename T>
411void Ts_TypedKnotDataProxy<T>::SetPreTanSlope(
412 const VtValue slope)
413{
414 _data->preTanSlope = slope.UncheckedGet<T>();
415}
416
417template <typename T>
418void Ts_TypedKnotDataProxy<T>::GetPreTanSlope(
419 VtValue* const slopeOut) const
420{
421 *slopeOut = VtValue(_data->GetPreTanSlope());
422}
423
424template <typename T>
425void Ts_TypedKnotDataProxy<T>::SetPostTanSlope(
426 const VtValue slope)
427{
428 _data->postTanSlope = slope.UncheckedGet<T>();
429}
430
431template <typename T>
432void Ts_TypedKnotDataProxy<T>::GetPostTanSlope(
433 VtValue* const slopeOut) const
434{
435 *slopeOut = VtValue(_data->GetPostTanSlope());
436}
437
438template <typename T>
439bool Ts_TypedKnotDataProxy<T>::UpdateTangents(
440 const Ts_KnotDataProxy* prevProxy,
441 const Ts_KnotDataProxy* nextProxy,
442 const TsCurveType curveType)
443{
444 const Ts_TypedKnotDataProxy<T>* prevTypedProxy =
445 dynamic_cast<const Ts_TypedKnotDataProxy<T>*>(prevProxy);
446 const Ts_TypedKnotData<T>* prevData = (prevTypedProxy
447 ? prevTypedProxy->_data
448 : nullptr);
449 const Ts_TypedKnotDataProxy<T>* nextTypedProxy =
450 dynamic_cast<const Ts_TypedKnotDataProxy<T>*>(nextProxy);
451 const Ts_TypedKnotData<T>* nextData = (nextTypedProxy
452 ? nextTypedProxy->_data
453 : nullptr);
454
455 return _data->UpdateTangents(prevData, nextData, curveType);
456}
457
458
459PXR_NAMESPACE_CLOSE_SCOPE
460
461#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:1105