KSeExpr  4.0.4.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 
14 namespace KSeExpr
15 {
16 template<> double Curve<double>::comp(const double &val, const int)
17 {
18  return val;
19 }
20 
21 template<> double Curve<Vec3d>::comp(const Vec3d &val, const int i)
22 {
23  return val[i];
24 }
25 
26 template<class T> bool Curve<T>::cvLessThan(const CV &cv1, const CV &cv2)
27 {
28  return cv1._pos < cv2._pos;
29 }
30 
31 template<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 
40 template<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 
46 template<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!
94 template<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!
139 template<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 
186 template<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 
198 template<class T> bool Curve<T>::interpTypeValid(InterpType interp)
199 {
200  return interp == kNone || interp == kLinear || interp == kSmooth || interp == kSpline || interp == kMonotoneSpline;
201 }
202 
203 template<> 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 
213 template<> void Curve<Vec3d>::clampCurveSegment(const Vec3d &delta, Vec3d &d1, Vec3d &d2)
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 
225 template class Curve<Vec3d>;
226 template class Curve<double>;
227 } // namespace KSeExpr
Interpolation curve class for double->double and double->Vec3D.
Definition: Curve.h:27
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.
double max(double x, double y)
Definition: ExprBuiltins.h:74
double min(double x, double y)
Definition: ExprBuiltins.h:78
double clamp(double x, double lo, double hi)
Definition: ExprBuiltins.h:66
double _pos
Definition: Curve.h:41
InterpType _interp
Definition: Curve.h:43