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 "Expression.h"
18#include "ExprBuiltins.h"
19#include <cfloat>
20#include <cassert>
21#include <algorithm>
22
23#include "Curve.h"
24
25namespace SeExpr2 {
26
27template <>
28double Curve<double>::comp(const double& val, const int) {
29 return val;
30}
31
32template <>
33double Curve<Vec3d>::comp(const Vec3d& val, const int i) {
34 return val[i];
35}
36
37template <class T>
38bool Curve<T>::cvLessThan(const CV& cv1, const CV& cv2) {
39 return cv1._pos < cv2._pos;
40}
41
42template <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
49template <class T>
50void Curve<T>::addPoint(double position, const T& val, InterpType type) {
51 prepared = false;
52 _cvData.push_back(CV(position, val, type));
53}
54
55template <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 = static_cast<int>(_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 = static_cast<int>(_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!
103template <class T>
104T 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 = static_cast<int>(_cvData.size());
108 const CV* cvDataBegin = &_cvData[0];
109 int index =
110 static_cast<int>(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 = static_cast<float>(_cvData[index - 1]._pos);
114 const T k0 = _cvData[index - 1]._val;
115 const InterpType interp = _cvData[index - 1]._interp;
116 const float t1 = static_cast<float>(_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!
149template <class T>
150double 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 = static_cast<int>(_cvData.size());
154 const CV* cvDataBegin = &_cvData[0];
155 int index =
156 static_cast<int>(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 = static_cast<float>(_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 = static_cast<float>(_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
198template <class T>
199typename Curve<T>::CV Curve<T>::getLowerBoundCV(const double param) const {
200 assert(prepared);
201 const CV* cvDataBegin = &_cvData[0];
202 int numPoints = static_cast<int>(_cvData.size());
203 int index =
204 static_cast<int>(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
210template <class T>
212 return interp == kNone || interp == kLinear || interp == kSmooth || interp == kSpline || interp == kMonotoneSpline;
213}
214
215template <>
216inline 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
225template <>
226void 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
237template class Curve<Vec3d>;
238template class Curve<double>;
239}
Interpolation curve class for double->double and double->Vec3D.
Definition: Curve.h:38
static double comp(const T &val, const int i)
Returns a component of the given value.
static bool cvLessThan(const CV &cv1, const CV &cv2)
CV Parameter ordering (cv1._pos < cv2._pos)
Definition: Curve.cpp:38
double getChannelValue(const double param, int channel) const
Definition: Curve.cpp:150
CV getLowerBoundCV(const double param) const
Definition: Curve.cpp:199
InterpType
Supported interpolation types.
Definition: Curve.h:43
T getValue(const double param) const
Evaluates curve and returns full value.
Definition: Curve.cpp:104
static bool interpTypeValid(InterpType interp)
Returns whether the given interpolation type is supported.
Definition: Curve.cpp:211
void clampCurveSegment(const T &delta, T &d1, T &d2)
Performs hermite derivative clamping in canonical space.
void addPoint(double position, const T &val, InterpType type)
Adds a point to the curve.
Definition: Curve.cpp:50
std::vector< CV > _cvData
Definition: Curve.h:59
void preparePoints()
Prepares points for evaluation (sorts and computes boundaries, clamps extrema)
Definition: Curve.cpp:56
double clamp(double x, double lo, double hi)
Definition: ExprBuiltins.h:40
double _pos
Definition: Curve.h:53
InterpType _interp
Definition: Curve.h:55
</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
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
The result is computed int int< br >< div style="margin-left: 40px;"> Picks values randomly between loRange and hiRange based on supplied index(which is automatically hashed). &nbsp