KSeExpr 6.0.0.0
Curve.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2011-2019 Disney Enterprises, Inc.
2// SPDX-License-Identifier: LicenseRef-Apache-2.0
3// SPDX-FileCopyrightText: 2020 L. E. Segovia <amy@amyspark.me>
4// SPDX-License-Identifier: GPL-3.0-or-later
5
6#include <algorithm>
7#include <cassert>
8#include <cfloat>
9
10#include "Curve.h"
11#include "ExprBuiltins.h"
12#include "Expression.h"
13
14namespace KSeExpr
15{
16template<> double Curve<double>::comp(const double &val, const int)
17{
18 return val;
19}
20
21template<> double Curve<Vec3d>::comp(const Vec3d &val, const int i)
22{
23 return val[i];
24}
25
26template<class T> bool Curve<T>::cvLessThan(const CV &cv1, const CV &cv2)
27{
28 return cv1._pos < cv2._pos;
29}
30
31template<class T>
33 : cacheCV(0)
34 , prepared(false)
35{
36 _cvData.push_back(CV(-FLT_MAX, T(), kNone));
37 _cvData.push_back(CV(FLT_MAX, T(), kNone));
38}
39
40template<class T> void Curve<T>::addPoint(double position, const T &val, InterpType type)
41{
42 prepared = false;
43 _cvData.push_back(CV(position, val, type));
44}
45
46template<class T> void Curve<T>::preparePoints()
47{
48 prepared = true;
49 cacheCV = 0;
50 // sort
51 std::sort(_cvData.begin(), _cvData.end(), cvLessThan);
52
53 // Setup boundary conditions on sentinel values
54 CV &end = *(_cvData.end() - 1);
55 CV &begin = *(_cvData.begin());
56 auto realCVs = _cvData.size() - 2;
57 assert(realCVs >= 0);
58 if (realCVs > 0) {
59 begin._val = _cvData[1]._val;
60 begin._deriv = T();
61 begin._interp = kNone;
62 auto lastIndex = _cvData.size() - 1;
63 end._val = _cvData[lastIndex - 1]._val;
64 end._deriv = T();
65 end._interp = kNone;
66 } else {
67 begin._pos = end._pos = 0;
68 begin._val = end._val = T();
69 begin._interp = kNone;
70 begin._deriv = end._deriv = T();
71 }
72
73 // Initialize "Catmull-Rom" derivatives (centered differences)
74 for (unsigned int i = 1; i < _cvData.size() - 1; i++) {
75 _cvData[i]._deriv = (_cvData[i + 1]._val - _cvData[i - 1]._val) / (_cvData[i + 1]._pos - _cvData[i - 1]._pos);
76 }
77
78 // Fix extrema by going through all intervals
79 for (unsigned int i = 0; i < _cvData.size() - 1; i++) {
80 if (_cvData[i]._interp == kMonotoneSpline) {
81 double h = _cvData[i + 1]._pos - _cvData[i]._pos;
82 if (h == 0)
83 _cvData[i]._deriv = _cvData[i + 1]._deriv = T();
84 else {
85 T delta = (_cvData[i + 1]._val - _cvData[i]._val) / h;
86 clampCurveSegment(delta, _cvData[i]._deriv, _cvData[i + 1]._deriv);
87 }
88 }
89 }
90}
91
92// TODO: this function and the next could be merged with template magic
93// but it might be simpler to just have two copies!
94template<class T> T Curve<T>::getValue(const double param) const
95{
96 assert(prepared);
97 // find the cv data point index just greater than the desired param
98 const int numPoints = static_cast<int>(_cvData.size());
99 const CV *cvDataBegin = &_cvData[0];
100 int index = static_cast<int>(std::upper_bound(cvDataBegin, cvDataBegin + numPoints, CV(param, T(), kLinear), cvLessThan) - cvDataBegin);
101 index = std::max(1, std::min(index, numPoints - 1));
102
103 const auto t0 = _cvData[index - 1]._pos;
104 const T k0 = _cvData[index - 1]._val;
105 const InterpType interp = _cvData[index - 1]._interp;
106 const auto t1 = _cvData[index]._pos;
107 const T k1 = _cvData[index]._val;
108 switch (interp) {
109 case kNone:
110 return k0;
111 break;
112 case kLinear: {
113 double u = (param - t0) / (t1 - t0);
114 return k0 + u * (k1 - k0);
115 } break;
116 case kSmooth: {
117 double u = (param - t0) / (t1 - t0);
118 return k0 * (u - 1) * (u - 1) * (2 * u + 1) + k1 * u * u * (3 - 2 * u);
119 } break;
120 case kSpline:
121 case kMonotoneSpline: {
122 double x = param - _cvData[index - 1]._pos; // xstart
123 double h = _cvData[index]._pos - _cvData[index - 1]._pos; // xend-xstart
124 T y = _cvData[index - 1]._val; // f(xstart)
125 T delta = _cvData[index]._val - _cvData[index - 1]._val; // f(xend)-f(xstart)
126 T d1 = _cvData[index - 1]._deriv; // f'(xstart)
127 T d2 = _cvData[index]._deriv; // f'(xend)
128 return (x * (delta * (3 * h - 2 * x) * x + h * (-h + x) * (-(d1 * h) + (d1 + d2) * x))) / (h * h * h) + y;
129 } break;
130 default:
131 assert(false);
132 return T();
133 break;
134 }
135}
136
137// TODO: this function and the previous could be merged with template magic
138// but it might be simpler to just have two copies!
139template<class T> double Curve<T>::getChannelValue(const double param, int channel) const
140{
141 assert(prepared);
142 // find the cv data point index just greater than the desired param
143 const int numPoints = static_cast<int>(_cvData.size());
144 const CV *cvDataBegin = &_cvData[0];
145 int index = static_cast<int>(std::upper_bound(cvDataBegin, cvDataBegin + numPoints, CV(param, T(), kLinear), cvLessThan) - cvDataBegin);
146 index = std::max(1, std::min(index, numPoints - 1));
147
148 const auto t0 = _cvData[index - 1]._pos;
149 const double k0 = comp(_cvData[index - 1]._val, channel);
150 const InterpType interp = _cvData[index - 1]._interp;
151 const auto t1 = _cvData[index]._pos;
152 const double k1 = comp(_cvData[index]._val, channel);
153 switch (interp) {
154 case kNone:
155 return k0;
156 break;
157 case kLinear: {
158 double u = (param - t0) / (t1 - t0);
159 return k0 + u * (k1 - k0);
160 } break;
161 case kSmooth:
162 // standard cubic interpolation
163 {
164 double u = (param - t0) / (t1 - t0);
165 return k0 * (u - 1) * (u - 1) * (2 * u + 1) + k1 * u * u * (3 - 2 * u);
166 }
167 break;
168 case kSpline:
169 case kMonotoneSpline: {
170 double x = param - _cvData[index - 1]._pos; // xstart
171 double h = _cvData[index]._pos - _cvData[index - 1]._pos; // xend-xstart
172 double y = comp(_cvData[index - 1]._val, channel); // f(xtart)
173 double delta = comp(_cvData[index]._val, channel) - comp(_cvData[index - 1]._val, channel); // f(xend)-f(xtart)
174 double d1 = comp(_cvData[index - 1]._deriv, channel); // f'(xtart)
175 double d2 = comp(_cvData[index]._deriv, channel); // f'(xend)
176
177 return (x * (delta * (3 * h - 2 * x) * x + h * (-h + x) * (-(d1 * h) + (d1 + d2) * x))) / (h * h * h) + y;
178 } break;
179 default:
180 assert(false);
181 return 0;
182 break;
183 }
184}
185
186template<class T> typename Curve<T>::CV Curve<T>::getLowerBoundCV(const double param) const
187{
188 assert(prepared);
189 const CV *cvDataBegin = &_cvData[0];
190 int numPoints = static_cast<int>(_cvData.size());
191 int index = static_cast<int>(std::upper_bound(cvDataBegin, cvDataBegin + numPoints, CV(param, T(), kLinear), cvLessThan) - cvDataBegin);
192 index = std::max(1, std::min(index, numPoints - 1));
193 if (index - 1 > 0)
194 return _cvData[index - 1];
195 return _cvData[index];
196}
197
199{
200 return interp == kNone || interp == kLinear || interp == kSmooth || interp == kSpline || interp == kMonotoneSpline;
201}
202
203template<> inline void Curve<double>::clampCurveSegment(const double &delta, double &d1, double &d2)
204{
205 if (delta == 0)
206 d1 = d2 = 0;
207 else {
208 d1 = KSeExpr::clamp(d1 / delta, 0, 3) * delta;
209 d2 = KSeExpr::clamp(d2 / delta, 0, 3) * delta;
210 }
211}
212
214{
215 for (int i = 0; i < 3; i++) {
216 if (delta[i] == 0)
217 d1[i] = d2[i] = 0;
218 else {
219 d1[i] = KSeExpr::clamp(d1[i] / delta[i], 0, 3) * delta[i];
220 d2[i] = KSeExpr::clamp(d2[i] / delta[i], 0, 3) * delta[i];
221 }
222 }
223}
224
225template class Curve<Vec3d>;
226template class Curve<double>;
227} // namespace KSeExpr
InterpType
Supported interpolation types.
Definition Curve.h:32
void preparePoints()
Prepares points for evaluation (sorts and computes boundaries, clamps extrema)
Definition Curve.cpp:46
T getValue(double param) const
Evaluates curve and returns full value.
Definition Curve.cpp:94
static bool cvLessThan(const CV &cv1, const CV &cv2)
CV Parameter ordering (cv1._pos < cv2._pos)
Definition Curve.cpp:26
void addPoint(double position, const T &val, InterpType type)
Adds a point to the curve.
Definition Curve.cpp:40
std::vector< CV > _cvData
Definition Curve.h:47
static double comp(const T &val, int i)
Returns a component of the given value.
double getChannelValue(double param, int channel) const
Definition Curve.cpp:139
CV getLowerBoundCV(double param) const
Definition Curve.cpp:186
static bool interpTypeValid(InterpType interp)
Returns whether the given interpolation type is supported.
Definition Curve.cpp:198
void clampCurveSegment(const T &delta, T &d1, T &d2)
Performs hermite derivative clamping in canonical space.
Vec()
Empty constructor (this is invalid for a reference type)
Definition Vec.h:57
static_if< ref, T *, std::array< T, d > >::TYPE x
internal data (either an explicit arary or a pointer to raw data)
Definition Vec.h:33
double clamp(double x, double lo, double hi)
InterpType _interp
Definition Curve.h:43