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