Loading...
Searching...
No Matches
regressionPreventer.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_REGRESSION_PREVENTER_H
9#define PXR_BASE_TS_REGRESSION_PREVENTER_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/knot.h"
15#include "pxr/base/ts/knotData.h"
16#include "pxr/base/ts/segment.h"
17
18#include <optional>
19#include <string>
20
21PXR_NAMESPACE_OPEN_SCOPE
22
23class TsSpline;
24
25
37{
38public:
46 enum TS_API InteractiveMode
47 {
50 ModeLimitActive = 100,
51
59 ModeLimitOpposite
60 };
61
64 {
65 public:
67 bool adjusted = false;
68
70 bool havePreSegment = false;
71 bool preActiveAdjusted = false;
72 TsTime preActiveAdjustedWidth = 0;
73 bool preOppositeAdjusted = false;
74 TsTime preOppositeAdjustedWidth = 0;
75
77 bool havePostSegment = false;
78 bool postActiveAdjusted = false;
79 TsTime postActiveAdjustedWidth = 0;
80 bool postOppositeAdjusted = false;
81 TsTime postOppositeAdjustedWidth = 0;
82
83 public:
84 TS_API
85 std::string GetDebugDescription(int precision = 6) const;
86 };
87
88public:
96 TS_API
98 TsSpline *spline,
99 TsTime activeKnotTime,
100 bool limit = true);
101
106 TS_API
108 TsSpline *spline,
109 TsTime activeKnotTime,
110 InteractiveMode mode,
111 bool limit = true);
112
146 TS_API
147 bool Set(
148 const TsKnot &proposedActiveKnot,
149 SetResult *resultOut = nullptr);
150
151private:
152 friend struct Ts_RegressionPreventerBatchAccess;
153
154 // Unified enum for both interactive and batch use.
155 enum _Mode
156 {
157 _ModeNone = TsAntiRegressionNone,
158 _ModeContain = TsAntiRegressionContain,
159 _ModeKeepRatio = TsAntiRegressionKeepRatio,
160 _ModeKeepStart = TsAntiRegressionKeepStart,
161 _ModeLimitActive = ModeLimitActive,
162 _ModeLimitOpposite = ModeLimitOpposite
163 };
164
165 // Private constructor to which the public constructors delegate.
167 TsSpline *spline,
168 TsTime activeKnotTime,
169 _Mode mode,
170 bool limit);
171
172 // PERFORMANCE NOTE: this class would probably be faster if it dealt
173 // directly with Ts_SplineData and Ts_KnotData, rather than going through
174 // TsSpline and TsKnot.
175
176 // Knot state stored for the lifetime of an interactive Preventer. Tracks
177 // the original knot from construction time, and the current time parameters
178 // in the spline.
179 struct _KnotState
180 {
181 public:
182 // Uses the original value for both 'original' and 'current'.
183 _KnotState(
184 TsSpline *spline,
185 const TsKnot &originalKnot);
186
187 // Write the original back to the spline, undoing any prior writes.
188 void RestoreOriginal();
189
190 // Remove the knot from the spline. This is needed when knot time is
191 // changing.
192 void RemoveCurrent();
193
194 // Write a new version of the knot, and record it as 'current'.
195 void Write(
196 const TsKnot &newKnot);
197
198 public:
199 // The spline, so we can write into it.
200 TsSpline* const spline;
201
202 // Original knot.
203 const TsKnot originalKnot;
204
205 // Current time parameters, possibly modified from original.
206 Ts_KnotData currentParams;
207 };
208
209 // Knot state used for the duration of a single Preventer iteration (Set or
210 // _ProcessSegment). Tracks the proposed new knot, and a potentially
211 // adjusted working version of the time parameters.
212 struct _WorkingKnotState
213 {
214 public:
215 // Uses the proposed value for 'proposed' and 'working'. This is for
216 // interactive use with active knots, for which a proposed new value is
217 // given as input.
218 _WorkingKnotState(
219 _KnotState *parentState,
220 const TsKnot &proposedKnot);
221
222 // Uses the parent's original for 'proposed' and 'working'. This is for
223 // interactive use with opposite knots, which always start out proposed
224 // as the original knots.
225 _WorkingKnotState(
226 _KnotState *parentState);
227
228 // For batch use. Stores only the original time parameters, which are
229 // used as the proposed parameters. Has no parent state, and cannot be
230 // used to write to the spline. The only output is 'working'.
231 _WorkingKnotState(
232 const Ts_KnotData &originalParams);
233
234 // Write the proposed value to the spline, without adjustment. Update
235 // the parent state's 'current'.
236 void WriteProposed();
237
238 // Write the possibly adjusted value to the spline. Update the parent
239 // state's 'current'.
240 void WriteWorking();
241
242 public:
243 // Link to whole-operation state.
244 _KnotState* const parentState;
245
246 // The proposed knot, if one was provided.
247 const TsKnot proposedKnot;
248
249 // The proposed time parameters.
250 const Ts_KnotData proposedParams;
251
252 // Copy of time parameters that we are modifying.
253 Ts_KnotData workingParams;
254 };
255
256 // Encapsulates the core math, and the details specific to whether we're
257 // operating on a pre-segment (the one before the active knot) or a
258 // post-segment (the one after).
259 class _SegmentSolver
260 {
261 public:
262 enum WhichSegment
263 {
264 PreSegment,
265 PostSegment
266 };
267
268 _SegmentSolver(
269 WhichSegment whichSegment,
270 _Mode mode,
271 _WorkingKnotState* activeKnotState,
272 _WorkingKnotState* oppositeKnotState,
273 SetResult* result);
274
275 _SegmentSolver(
276 _Mode mode,
277 Ts_Segment* segment,
278 SetResult* result);
279
280 // If adjustments are needed, update activeKnotState->working,
281 // oppositeKnotState->working, and *result. Does not immediately write
282 // to the spline. Returns true on success, false on failure.
283 bool Adjust();
284
285 private:
286 // Mode kernels.
287 bool _AdjustWithContain();
288 bool _AdjustWithKeepRatio();
289 bool _AdjustWithKeepStart();
290 bool _AdjustWithLimitActive();
291 bool _AdjustWithLimitOpposite();
292
293 // Accessors and mutators for the active and opposite tangent widths.
294 // The widths passed and returned here are always normalized to the
295 // [0, 1] segment time interval.
296 TsTime _GetProposedActiveWidth() const;
297 TsTime _GetProposedOppositeWidth() const;
298 void _SetActiveWidth(TsTime width);
299 void _SetOppositeWidth(TsTime width);
300
301 // Like the above, but for asymmetrical algorithms that differentiate
302 // between start and end knots rather than active and opposite.
303 TsTime _GetProposedStartWidth() const;
304 TsTime _GetProposedEndWidth() const;
305 void _SetStartWidth(TsTime width);
306 void _SetEndWidth(TsTime width);
307
308 // Plumbing helpers.
309 TsTime _GetSegmentWidth() const;
310
311 private:
312 const WhichSegment _whichSegment;
313 const _Mode _mode;
314 Ts_Segment* const _segment;
315 _WorkingKnotState* const _activeKnotState;
316 _WorkingKnotState* const _oppositeKnotState;
317 SetResult* const _result;
318 };
319
320private:
321 // Set() helpers.
322 void _InitSetResult(
323 const TsKnot &proposedActiveKnot,
324 SetResult *resultOut) const;
325 void _HandleInitialAdjustment(
326 const TsKnot &proposedActiveKnot,
327 SetResult* resultOut);
328 void _HandleTimeChange(TsTime proposedActiveTime);
329 void _DoSet(
330 const TsKnot &proposedActiveKnot,
331 _Mode mode,
332 SetResult* resultOut);
333
334private:
335 TsSpline* const _spline;
336 const _Mode _mode;
337 const bool _limit;
338
339 bool _valid;
340 bool _initialAdjustmentDone;
341
342 std::optional<_KnotState> _activeKnotState;
343 std::optional<_KnotState> _preKnotState;
344 std::optional<_KnotState> _postKnotState;
345 std::optional<_KnotState> _overwrittenKnotState;
346};
347
348struct Ts_RegressionPreventerBatchAccess
349{
350 // Batch operation for one segment of a spline. In Contain mode, this
351 // method returns true for "bold" tangents that are non-regressive but
352 // exceed the segment interval.
353 TS_API
354 static bool IsSegmentRegressive(
355 const Ts_KnotData *startKnot,
356 const Ts_KnotData *endKnot,
357 TsAntiRegressionMode mode);
358
359 // Overload of the above function that operates on a Ts_Segment
360 TS_API
361 static bool IsSegmentRegressive(
362 const Ts_Segment* segment,
363 TsAntiRegressionMode mode);
364
365 // Batch operation for one segment of a spline. Returns whether anything
366 // was changed.
367 TS_API
368 static bool ProcessSegment(
369 Ts_KnotData *startKnot,
370 Ts_KnotData *endKnot,
371 TsAntiRegressionMode mode);
372
373 // Overload of the above function that operates on a Ts_Segment
374 TS_API
375 static bool ProcessSegment(
376 Ts_Segment* segment,
377 TsAntiRegressionMode mode);
378};
379
380
381PXR_NAMESPACE_CLOSE_SCOPE
382
383#endif
A knot belonging to a TsSpline.
Definition: knot.h:41
Details of the result of an interactive Set call.
bool havePostSegment
If there is a post-segment, what adjustments were made to it.
bool havePreSegment
If there is a pre-segment, what adjustments were made to it.
bool adjusted
Whether any adjustments were made.
An authoring helper class that enforces non-regression in splines.
TS_API TsRegressionPreventer(TsSpline *spline, TsTime activeKnotTime, bool limit=true)
Constructor for interactive use (repeated calls to Set).
TS_API TsRegressionPreventer(TsSpline *spline, TsTime activeKnotTime, InteractiveMode mode, bool limit=true)
Same as the above, but with an InteractiveMode.
TS_API bool Set(const TsKnot &proposedActiveKnot, SetResult *resultOut=nullptr)
Set an edited version of the active knot into the spline, adjusting tangent widths if needed,...
A mathematical description of a curved function from time to value.
Definition: spline.h:60
Ts_Segment represents one section of a spline.
Definition: segment.h:57