Skip to content

Commit 2a5cec6

Browse files
o2b3dpixar-oss
authored andcommitted
Implement regression prevention for Ts_Segments
Specifically, add support to Ts_RegressionPreventerBatchAccess and TsRegressionPreventer::_SegmentSolver. Overloads of the methods IsSegmentRegressive and ProcessSegment now operate on a Ts_Segment. ProcessSegment modifies the Ts_Segment in place. In practice, these methods will only be called internally by eval, sample, bake, and breakdown code to ensure that the segment they are about to operate on is non-regressive. Currently this code only uses the "keep ratio" anti-regression mode. In practice, splines are de-regressed at authoring time so Ts_Segments should only be regressive if the user has deliberately disabled anti-regression when authoring and then wants to eval the spline. Note: this functionality is only exercised by the test, but it is proposed to be the basis for exec, breakdown, sample, and bake. (Internal change: 2389416)
1 parent 989e00e commit 2a5cec6

8 files changed

Lines changed: 766 additions & 289 deletions

File tree

pxr/base/ts/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ set(classes
2626
knotMap
2727
raii
2828
regressionPreventer
29+
segment
2930
spline
3031
splineData
3132
tangentConversions
@@ -162,6 +163,16 @@ pxr_build_test(
162163
tf
163164
)
164165

166+
pxr_build_test(
167+
testTsRegressionPreventerCpp
168+
CPPFILES
169+
testenv/testTsRegressionPreventerCpp.cpp
170+
LIBRARIES
171+
gf
172+
tf
173+
ts
174+
)
175+
165176
pxr_build_test(
166177
testTsSegmentIterator
167178
CPPFILES
@@ -304,6 +315,11 @@ pxr_register_test(
304315
COMMAND "${CMAKE_INSTALL_PREFIX}/tests/testTsThreadedCOW"
305316
)
306317

318+
pxr_register_test(
319+
testTsRegressionPreventerCpp
320+
COMMAND "${CMAKE_INSTALL_PREFIX}/tests/testTsRegressionPreventerCpp"
321+
)
322+
307323
pxr_register_test(
308324
testTsSegmentIterator
309325
COMMAND "${CMAKE_INSTALL_PREFIX}/tests/testTsSegmentIterator"

pxr/base/ts/iterator.cpp

Lines changed: 2 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,6 @@ constexpr double inf = std::numeric_limits<double>::infinity();
2020
constexpr bool OPEN = false;
2121
constexpr bool CLOSED = true;
2222

23-
// Utility to convert a TsInterpMode value into a Ts_SegmentInterp. Because the
24-
// TsInterpMode just includes a generic "curve" interpolation, we also need the
25-
// spline's curve type to know if this segment is a bezier or hermite curve.
26-
Ts_SegmentInterp _ConvertInterpMode(TsInterpMode interpMode,
27-
TsCurveType curveType)
28-
{
29-
switch (interpMode)
30-
{
31-
case TsInterpValueBlock: return Ts_SegmentInterp::ValueBlock;
32-
case TsInterpHeld: return Ts_SegmentInterp::Held;
33-
case TsInterpLinear: return Ts_SegmentInterp::Linear;
34-
case TsInterpCurve:
35-
if (curveType == TsCurveTypeHermite) {
36-
return Ts_SegmentInterp::Hermite;
37-
} else {
38-
return Ts_SegmentInterp::Bezier;
39-
}
40-
}
41-
// Generate a nicer error message.
42-
constexpr bool invalid_TsInterpMode_value = false;
43-
TF_AXIOM(invalid_TsInterpMode_value);
44-
45-
return Ts_SegmentInterp::ValueBlock;
46-
}
47-
4823
// Get a looped interval that's open on the right-hand end. The one returned by
4924
// TsLoopParams::GetLoopedInterval() is closed on the right and changing that
5025
// breaks a couple of existing tests.
@@ -59,100 +34,6 @@ GfInterval _GetOpenLoopedInterval(const TsLoopParams& lp)
5934

6035
} // end anonymous namespace
6136

62-
////////////////////////////////////////////////////////////////
63-
// Ts_Segment
64-
65-
// This computes a derivative, but only for u == 0 or u == 1 for curved
66-
// segments. Fortunately, this is all that's required for linear extrapolation.
67-
double
68-
Ts_Segment::_ComputeDerivative(double u) const
69-
{
70-
u = std::clamp(u, 0.0, 1.0);
71-
72-
// It's possible to not have a derivative due to value blocks. But we're
73-
// going to return 0.0 in that case just to keep things simple. The result
74-
// will be a value block with slope of 0.0. :-)
75-
switch (interp)
76-
{
77-
case Ts_SegmentInterp::ValueBlock:
78-
case Ts_SegmentInterp::Held:
79-
return 0.0;
80-
81-
case Ts_SegmentInterp::Linear:
82-
return (p1[1] - p0[1]) / (p1[0] - p0[0]);
83-
84-
case Ts_SegmentInterp::PreExtrap:
85-
return p0[1];
86-
87-
case Ts_SegmentInterp::PostExtrap:
88-
return p1[1];
89-
90-
case Ts_SegmentInterp::Bezier:
91-
case Ts_SegmentInterp::Hermite:
92-
TF_VERIFY(u == 0.0 || u == 1.0,
93-
"Cannot yet compute derivatives at arbitrary values. u = %g",
94-
u);
95-
96-
// Not a generalized solution right now, but it works for 0 and 1
97-
if (u <= 0.5) {
98-
// slope is equal to first tangent direction.
99-
GfVec2d tangent = t0 - p0;
100-
return tangent[1] / tangent[0];
101-
} else if (u > 0.5) {
102-
// slope is equal to the last tangent direction. Note that
103-
// this direction is measured from t1 to p1.
104-
GfVec2d tangent = p1 - t1;
105-
return tangent[1] / tangent[0];
106-
}
107-
108-
default:
109-
TF_CODING_ERROR("Invalid segment interp (%d) in _ComputeDerivative",
110-
int(interp));
111-
}
112-
113-
return 0.0;
114-
}
115-
116-
// Output operator for testing purposes.
117-
std::ostream& operator <<(std::ostream& out, const Ts_SegmentInterp interp)
118-
{
119-
switch (interp)
120-
{
121-
case Ts_SegmentInterp::ValueBlock:
122-
return out << "Ts_SegmentInterp::ValueBlock";
123-
124-
case Ts_SegmentInterp::Held:
125-
return out << "Ts_SegmentInterp::Held";
126-
127-
case Ts_SegmentInterp::Linear:
128-
return out << "Ts_SegmentInterp::Linear";
129-
130-
case Ts_SegmentInterp::Bezier:
131-
return out << "Ts_SegmentInterp::Bezier";
132-
133-
case Ts_SegmentInterp::Hermite:
134-
return out << "Ts_SegmentInterp::Hermite";
135-
136-
case Ts_SegmentInterp::PreExtrap:
137-
return out << "Ts_SegmentInterp::PreExtrap";
138-
139-
case Ts_SegmentInterp::PostExtrap:
140-
return out << "Ts_SegmentInterp::PostExtrap";
141-
}
142-
143-
return out << "Ts_SegmentInterp(" << int(interp) << ")";
144-
}
145-
146-
// Output operators for testing purposes.
147-
std::ostream& operator <<(std::ostream& out, const Ts_Segment& seg)
148-
{
149-
return out << "Ts_Segment{"
150-
<< "{" << seg.p0[0] << ", " << seg.p0[1] << "}, "
151-
<< "{" << seg.t0[0] << ", " << seg.t0[1] << "}, "
152-
<< "{" << seg.t1[0] << ", " << seg.t1[1] << "}, "
153-
<< "{" << seg.p1[0] << ", " << seg.p1[1] << "}, "
154-
<< seg.interp << "}";
155-
}
15637

15738
////////////////////////////////////////////////////////////////
15839
// Ts_SegmentPrototypeIterator
@@ -256,7 +137,7 @@ Ts_SegmentPrototypeIterator::_UpdateSegment()
256137
_segment.t1 = _segment.p1 +
257138
GfVec2d(-nextKnot.preTanWidth,
258139
nextKnot.GetPreTanHeight());
259-
_segment.interp = _ConvertInterpMode(prevKnot.nextInterp, _data->curveType);
140+
_segment.SetInterp(prevKnot.nextInterp, _data->curveType);
260141
}
261142

262143
Ts_Segment Ts_SegmentPrototypeIterator::operator *() const
@@ -634,7 +515,7 @@ Ts_SegmentKnotIterator::_UpdateSegment()
634515
_segment.t1 = _segment.p1 -
635516
GfVec2d(nextKnot.preTanWidth,
636517
nextKnot.preTanWidth * nextKnot.preTanSlope);
637-
_segment.interp = _ConvertInterpMode(prevKnot.nextInterp, _data->curveType);
518+
_segment.SetInterp(prevKnot.nextInterp, _data->curveType);
638519
}
639520

640521
Ts_Segment

pxr/base/ts/iterator.h

Lines changed: 1 addition & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "pxr/pxr.h"
1212

1313
#include "pxr/base/ts/api.h"
14+
#include "pxr/base/ts/segment.h"
1415
#include "pxr/base/ts/splineData.h"
1516

1617
#include "pxr/base/gf/interval.h"
@@ -81,151 +82,6 @@
8182

8283
PXR_NAMESPACE_OPEN_SCOPE
8384

84-
/// Ts_SegmentInterp declares the type of interpolation that should be used
85-
/// between the end-points of a segment.
86-
//
87-
/// It is a scoped enum so the use of the Ts_SegmentInterp:: prefix is required.
88-
enum class Ts_SegmentInterp
89-
{
90-
ValueBlock, //< This segment explicitly has no value
91-
Held, //< The value is always the starting value
92-
Linear, //< Interpolate linearly from start to end
93-
Bezier, //< The segment uses Bezier interpolation
94-
Hermite, //< The segment uses Hermite interpolation
95-
PreExtrap, //< Linear extrapolation from -infinity to
96-
//< the end value with a fixed slope
97-
PostExtrap //< Linear extrapolation to +infinity from
98-
//< the start value with a fixed slope
99-
};
100-
101-
/// Ts_Segment represents one section of a spline. It generally contains the
102-
/// "post" side values of one knot (time, value, tangent, and interpolation) and
103-
/// the "pre" side of the next knot (preValue and tangent). There are special
104-
/// case interpolation types for pre- and post-extrapolation and the curve
105-
/// interpolation of \c TsKnot has been split into separate Bezier and Hermite
106-
/// values.
107-
///
108-
/// The data is stored as 4 GfVec2d values that represent (time, value) points
109-
/// that are the knot points and the tangent end points. Note that the tangents
110-
/// are stored as their end points rather than as width and slope as they are in
111-
/// the knots. Also note that held interpolation segments still store the value
112-
/// of the post-side knot even though that value is not used for segment
113-
/// interpolation. This allows the knots to be reconstructed from the segment
114-
/// data.
115-
///
116-
/// If the interpolation is PreExtrap or PostExtrap then the starting or ending
117-
/// point (respectively) contains (+/- infinity, slope) rather than (time,
118-
/// value). The straight line is extrapolated to infinity from the other end
119-
/// point of the segment with the given slope.
120-
struct Ts_Segment
121-
{
122-
GfVec2d p0 = GfVec2d(0);
123-
GfVec2d t0 = GfVec2d(0);
124-
GfVec2d t1 = GfVec2d(0);
125-
GfVec2d p1 = GfVec2d(0);
126-
Ts_SegmentInterp interp = Ts_SegmentInterp::ValueBlock;
127-
128-
/// Add the (timeDelta, valueDelta) contained in the \c GfVec2d argument to
129-
/// the segment's time and value.
130-
Ts_Segment& operator +=(const GfVec2d& delta) {
131-
p0 += delta;
132-
t0 += delta;
133-
t1 += delta;
134-
p1 += delta;
135-
136-
return *this;
137-
}
138-
139-
/// Add the timeDelta argument to the segment's time
140-
Ts_Segment& operator +=(const double timeDelta) {
141-
p0[0] += timeDelta;
142-
t0[0] += timeDelta;
143-
t1[0] += timeDelta;
144-
p1[0] += timeDelta;
145-
146-
return *this;
147-
}
148-
149-
/// Subtract the (timeDelta, valueDelta) contained in the \c GfVec2d
150-
/// argument from the segment's time and value.
151-
Ts_Segment& operator -=(const GfVec2d& delta) {
152-
p0 -= delta;
153-
t0 -= delta;
154-
t1 -= delta;
155-
p1 -= delta;
156-
157-
return *this;
158-
}
159-
160-
/// Subtract the timeDelta argument from the segment's time
161-
Ts_Segment& operator -=(const double timeDelta) {
162-
p0[0] -= timeDelta;
163-
t0[0] -= timeDelta;
164-
t1[0] -= timeDelta;
165-
p1[0] -= timeDelta;
166-
167-
return *this;
168-
}
169-
170-
/// Addition operator.
171-
template <typename T>
172-
Ts_Segment operator +(const T& delta) const {
173-
Ts_Segment result = *this;
174-
result += delta;
175-
return result;
176-
}
177-
178-
// Subtraction operator
179-
template <typename T>
180-
Ts_Segment operator -(const T& delta) const {
181-
Ts_Segment result = *this;
182-
result -= delta;
183-
return result;
184-
}
185-
186-
// Unary negation operator - negates the times, not the values.
187-
Ts_Segment operator -() const {
188-
Ts_Segment result = *this;
189-
result.p0[0] = -result.p0[0];
190-
result.t0[0] = -result.t0[0];
191-
result.t1[0] = -result.t1[0];
192-
result.p1[0] = -result.p1[0];
193-
194-
// Now swap the points back into the correct order (p0 always has the
195-
// smallest time value).
196-
using std::swap;
197-
swap(result.p0, result.p1);
198-
swap(result.t0, result.t1);
199-
200-
return result;
201-
}
202-
203-
/// Compare for equivalence
204-
bool operator ==(const Ts_Segment& rhs) const {
205-
return (p0 == rhs.p0 &&
206-
t0 == rhs.t0 &&
207-
t1 == rhs.t1 &&
208-
p1 == rhs.p1 &&
209-
interp == rhs.interp);
210-
}
211-
212-
/// Not equivalent
213-
bool operator !=(const Ts_Segment& rhs) const {
214-
return !(*this == rhs);
215-
}
216-
217-
/// Compute dv/dt at the value u in the interval [0..1].
218-
/// Note: This currently only supports u == 0.0 or u == 1.0.
219-
TS_API
220-
double _ComputeDerivative(double u) const;
221-
};
222-
223-
// For testing purposes.
224-
TS_API
225-
std::ostream& operator <<(std::ostream&, const Ts_SegmentInterp);
226-
TS_API
227-
std::ostream& operator <<(std::ostream&, const Ts_Segment&);
228-
22985
/// \brief Iterate over the segments in an inner loop prototype that intersect
23086
/// the input time interval.
23187
///

0 commit comments

Comments
 (0)