SeExpr
Curve.cpp
Go to the documentation of this file.
1 /*
2 * Copyright Disney Enterprises, Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License
6 * and the following modification to it: Section 6 Trademarks.
7 * deleted and replaced with:
8 *
9 * 6. Trademarks. This License does not grant permission to use the
10 * trade names, trademarks, service marks, or product names of the
11 * Licensor and its affiliates, except as required for reproducing
12 * the content of the NOTICE file.
13 *
14 * You may obtain a copy of the License at
15 * http://www.apache.org/licenses/LICENSE-2.0
16 */
17 #include <SeExpr2/Expression.h>
18 #include <SeExpr2/ExprBuiltins.h>
19 #include <cfloat>
20 #include <cassert>
21 #include <algorithm>
22 
23 #include "Curve.h"
24 
25 namespace SeExpr2 {
26 
27 template <>
28 double Curve<double>::comp(const double& val, const int) {
29  return val;
30 }
31 
32 template <>
33 double Curve<Vec3d>::comp(const Vec3d& val, const int i) {
34  return val[i];
35 }
36 
37 template <class T>
38 bool Curve<T>::cvLessThan(const CV& cv1, const CV& cv2) {
39  return cv1._pos < cv2._pos;
40 }
41 
42 template <class T>
44  : cacheCV(0), prepared(false) {
45  _cvData.push_back(CV(-FLT_MAX, T(), kNone));
46  _cvData.push_back(CV(FLT_MAX, T(), kNone));
47 }
48 
49 template <class T>
50 void Curve<T>::addPoint(double position, const T& val, InterpType type) {
51  prepared = false;
52  _cvData.push_back(CV(position, val, type));
53 }
54 
55 template <class T>
57  prepared = true;
58  cacheCV = 0;
59  // sort
60  std::sort(_cvData.begin(), _cvData.end(), cvLessThan);
61 
62  // Setup boundary conditions on sentinel values
63  CV& end = *(_cvData.end() - 1);
64  CV& begin = *(_cvData.begin());
65  int realCVs = _cvData.size() - 2;
66  assert(realCVs >= 0);
67  if (realCVs > 0) {
68  begin._val = _cvData[1]._val;
69  begin._deriv = T();
70  begin._interp = kNone;
71  int lastIndex = _cvData.size() - 1;
72  end._val = _cvData[lastIndex - 1]._val;
73  end._deriv = T();
74  end._interp = kNone;
75  } else {
76  begin._pos = end._pos = 0;
77  begin._val = end._val = T();
78  begin._interp = kNone;
79  begin._deriv = end._deriv = T();
80  }
81 
82  // Initialize "Catmull-Rom" derivatives (centered differences)
83  for (unsigned int i = 1; i < _cvData.size() - 1; i++) {
84  _cvData[i]._deriv = (_cvData[i + 1]._val - _cvData[i - 1]._val) / (_cvData[i + 1]._pos - _cvData[i - 1]._pos);
85  }
86 
87  // Fix extrema by going through all intervals
88  for (unsigned int i = 0; i < _cvData.size() - 1; i++) {
89  if (_cvData[i]._interp == kMonotoneSpline) {
90  double h = _cvData[i + 1]._pos - _cvData[i]._pos;
91  if (h == 0)
92  _cvData[i]._deriv = _cvData[i + 1]._deriv = T();
93  else {
94  T delta = (_cvData[i + 1]._val - _cvData[i]._val) / h;
95  clampCurveSegment(delta, _cvData[i]._deriv, _cvData[i + 1]._deriv);
96  }
97  }
98  }
99 }
100 
101 // TODO: this function and the next could be merged with template magic
102 // but it might be simpler to just have two copies!
103 template <class T>
104 T Curve<T>::getValue(const double param) const {
105  assert(prepared);
106  // find the cv data point index just greater than the desired param
107  const int numPoints = _cvData.size();
108  const CV* cvDataBegin = &_cvData[0];
109  int index =
110  std::upper_bound(cvDataBegin, cvDataBegin + numPoints, CV(param, T(), kLinear), cvLessThan) - cvDataBegin;
111  index = std::max(1, std::min(index, numPoints - 1));
112 
113  const float t0 = _cvData[index - 1]._pos;
114  const T k0 = _cvData[index - 1]._val;
115  const InterpType interp = _cvData[index - 1]._interp;
116  const float t1 = _cvData[index]._pos;
117  const T k1 = _cvData[index]._val;
118  switch (interp) {
119  case kNone:
120  return k0;
121  break;
122  case kLinear: {
123  double u = (param - t0) / (t1 - t0);
124  return k0 + u * (k1 - k0);
125  } break;
126  case kSmooth: {
127  double u = (param - t0) / (t1 - t0);
128  return k0 * (u - 1) * (u - 1) * (2 * u + 1) + k1 * u * u * (3 - 2 * u);
129  } break;
130  case kSpline:
131  case kMonotoneSpline: {
132  double x = param - _cvData[index - 1]._pos; // xstart
133  double h = _cvData[index]._pos - _cvData[index - 1]._pos; // xend-xstart
134  T y = _cvData[index - 1]._val; // f(xstart)
135  T delta = _cvData[index]._val - _cvData[index - 1]._val; // f(xend)-f(xstart)
136  T d1 = _cvData[index - 1]._deriv; // f'(xstart)
137  T d2 = _cvData[index]._deriv; // f'(xend)
138  return (x * (delta * (3 * h - 2 * x) * x + h * (-h + x) * (-(d1 * h) + (d1 + d2) * x))) / (h * h * h) + y;
139  } break;
140  default:
141  assert(false);
142  return T();
143  break;
144  }
145 }
146 
147 // TODO: this function and the previous could be merged with template magic
148 // but it might be simpler to just have two copies!
149 template <class T>
150 double Curve<T>::getChannelValue(const double param, int channel) const {
151  assert(prepared);
152  // find the cv data point index just greater than the desired param
153  const int numPoints = _cvData.size();
154  const CV* cvDataBegin = &_cvData[0];
155  int index =
156  std::upper_bound(cvDataBegin, cvDataBegin + numPoints, CV(param, T(), kLinear), cvLessThan) - cvDataBegin;
157  index = std::max(1, std::min(index, numPoints - 1));
158 
159  const float t0 = _cvData[index - 1]._pos;
160  const double k0 = comp(_cvData[index - 1]._val, channel);
161  const InterpType interp = _cvData[index - 1]._interp;
162  const float t1 = _cvData[index]._pos;
163  const double k1 = comp(_cvData[index]._val, channel);
164  switch (interp) {
165  case kNone:
166  return k0;
167  break;
168  case kLinear: {
169  double u = (param - t0) / (t1 - t0);
170  return k0 + u * (k1 - k0);
171  } break;
172  case kSmooth:
173  // standard cubic interpolation
174  {
175  double u = (param - t0) / (t1 - t0);
176  return k0 * (u - 1) * (u - 1) * (2 * u + 1) + k1 * u * u * (3 - 2 * u);
177  }
178  break;
179  case kSpline:
180  case kMonotoneSpline: {
181  double x = param - _cvData[index - 1]._pos; // xstart
182  double h = _cvData[index]._pos - _cvData[index - 1]._pos; // xend-xstart
183  double y = comp(_cvData[index - 1]._val, channel); // f(xtart)
184  double delta =
185  comp(_cvData[index]._val, channel) - comp(_cvData[index - 1]._val, channel); // f(xend)-f(xtart)
186  double d1 = comp(_cvData[index - 1]._deriv, channel); // f'(xtart)
187  double d2 = comp(_cvData[index]._deriv, channel); // f'(xend)
188 
189  return (x * (delta * (3 * h - 2 * x) * x + h * (-h + x) * (-(d1 * h) + (d1 + d2) * x))) / (h * h * h) + y;
190  } break;
191  default:
192  assert(false);
193  return 0;
194  break;
195  }
196 }
197 
198 template <class T>
199 typename Curve<T>::CV Curve<T>::getLowerBoundCV(const double param) const {
200  assert(prepared);
201  const CV* cvDataBegin = &_cvData[0];
202  int numPoints = _cvData.size();
203  int index =
204  std::upper_bound(cvDataBegin, cvDataBegin + numPoints, CV(param, T(), kLinear), cvLessThan) - cvDataBegin;
205  index = std::max(1, std::min(index, numPoints - 1));
206  if (index - 1 > 0) return _cvData[index - 1];
207  return _cvData[index];
208 }
209 
210 template <class T>
212  return interp == kNone || interp == kLinear || interp == kSmooth || interp == kSpline || interp == kMonotoneSpline;
213 }
214 
215 template <>
216 inline void Curve<double>::clampCurveSegment(const double& delta, double& d1, double& d2) {
217  if (delta == 0)
218  d1 = d2 = 0;
219  else {
220  d1 = SeExpr2::clamp(d1 / delta, 0, 3) * delta;
221  d2 = SeExpr2::clamp(d2 / delta, 0, 3) * delta;
222  }
223 }
224 
225 template <>
226 void Curve<Vec3d>::clampCurveSegment(const Vec3d& delta, Vec3d& d1, Vec3d& d2) {
227  for (int i = 0; i < 3; i++) {
228  if (delta[i] == 0)
229  d1[i] = d2[i] = 0;
230  else {
231  d1[i] = SeExpr2::clamp(d1[i] / delta[i], 0, 3) * delta[i];
232  d2[i] = SeExpr2::clamp(d2[i] / delta[i], 0, 3) * delta[i];
233  }
234  }
235 }
236 
237 template class Curve<Vec3d>;
238 template class Curve<double>;
239 }
std::vector< CV > _cvData
Definition: Curve.h:59
void preparePoints()
Prepares points for evaluation (sorts and computes boundaries, clamps extrema)
Definition: Curve.cpp:56
static bool interpTypeValid(InterpType interp)
Returns whether the given interpolation type is supported.
Definition: Curve.cpp:211
static bool cvLessThan(const CV &cv1, const CV &cv2)
CV Parameter ordering (cv1._pos &lt; cv2._pos)
Definition: Curve.cpp:38
Interpolation curve class for double-&gt;double and double-&gt;Vec3D.
Definition: Curve.h:38
InterpType _interp
Definition: Curve.h:55
T getValue(const double param) const
Evaluates curve and returns full value.
Definition: Curve.cpp:104
CV getLowerBoundCV(const double param) const
Definition: Curve.cpp:199
double _pos
Definition: Curve.h:53
double max(double x, double y)
Definition: ExprBuiltins.h:42
The result is computed int int< br >< divstyle="margin-left:40px;"> Picks values randomly between loRange and hiRange based on supplied index(which is automatically hashed).&nbsp
double min(double x, double y)
Definition: ExprBuiltins.h:43
</pre >< h3 > A simple variable reference</h3 > This is not a very interesting subclass of expression until we add some additional variables Variables on some applications may be very dynamic In this we only need x
Definition: tutorial.txt:108
double clamp(double x, double lo, double hi)
Definition: ExprBuiltins.h:40
static double comp(const T &val, const int i)
Returns a component of the given value.
void addPoint(double position, const T &val, InterpType type)
Adds a point to the curve.
Definition: Curve.cpp:50
InterpType
Supported interpolation types.
Definition: Curve.h:43
double getChannelValue(const double param, int channel) const
Definition: Curve.cpp:150
This is the same as the prman cellnoise function< br ></div >< br > float< b > float y< br > float< b > float y
Definition: userdoc.txt:218
void clampCurveSegment(const T &delta, T &d1, T &d2)
Performs hermite derivative clamping in canonical space.