Library of Assembled Shared Sources
spline_linear.inl
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * *** BEGIN LICENSE INFORMATION ***
6 *
7 * The contents of this file are subject to the Common Public Attribution License
8 * Version 1.0 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://lass.sourceforge.net/cpal-license. The License is based on the
11 * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12 * use of software over a computer network and provide for limited attribution for
13 * the Original Developer. In addition, Exhibit A has been modified to be consistent
14 * with Exhibit B.
15 *
16 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18 * language governing rights and limitations under the License.
19 *
20 * The Original Code is LASS - Library of Assembled Shared Sources.
21 *
22 * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23 * The Original Developer is the Initial Developer.
24 *
25 * All portions of the code written by the Initial Developer are:
26 * Copyright (C) 2004-2011 the Initial Developer.
27 * All Rights Reserved.
28 *
29 * Contributor(s):
30 *
31 * Alternatively, the contents of this file may be used under the terms of the
32 * GNU General Public License Version 2 or later (the GPL), in which case the
33 * provisions of GPL are applicable instead of those above. If you wish to allow use
34 * of your version of this file only under the terms of the GPL and not to allow
35 * others to use your version of this file under the CPAL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and other
37 * provisions required by the GPL License. If you do not delete the provisions above,
38 * a recipient may use your version of this file under either the CPAL or the GPL.
39 *
40 * *** END LICENSE INFORMATION ***
41 */
42
43
44
45#ifndef LASS_GUARDIAN_OF_INCLUSION_NUM_SPLINE_LINEAR_INL
46#define LASS_GUARDIAN_OF_INCLUSION_NUM_SPLINE_LINEAR_INL
47
48#include "num_common.h"
49#include "spline_linear.h"
51
52namespace lass
53{
54namespace num
55{
56
57// --- public --------------------------------------------------------------------------------------
58
59template <typename S, typename D, typename T>
60SplineLinear<S, D, T>::SplineLinear()
61{
62}
63
64
65
66/** construct a spline from a range of control/data pairs.
67 *
68 * Each node consists of a control value and a data value. This contstructor accepts a single
69 * range [iFirst, iLast) of control/data pairs. The iterator type should have two fields
70 * @c first and @c second that contain respectively the control and data values. This is
71 * choosen so that a std::pair can be used as a representatin of the control/data pair.
72 *
73 * @pre
74 * @arg [iFirst, iLast) is a valid range.
75 * @arg @c PairInputIterator has a member @c first containing the control value
76 * @arg @c PairInputIterator has a member @c second containing the data value
77 *
78 * @par complexity:
79 * O(D * log(N)) with
80 * @arg D = a figure that indicates the complexity of operations on data values.
81 * Is most probably linear with the dimension of the data value
82 * @arg N = number of nodes
83 */
84template <typename S, typename D, typename T>
85template <typename PairInputIterator>
86SplineLinear<S, D, T>::SplineLinear(PairInputIterator iFirst,
87 PairInputIterator iLast)
88{
89 while (iFirst != iLast)
90 {
91 Node node;
92 node.x = iFirst->first;
93 node.y = iFirst->second;
94 nodes_.push_back(node);
95 ++iFirst;
96 }
97 init();
98}
99
100
101
102/** construct a spline from seperate ranges.
103 *
104 * Each node consists of a control value and a data value. This contstructor accepts seperate
105 * ranges for control and data values. The control values are given by the range
106 * [iFirstControl , iLastControl). Of the range of the data values, only the iterator
107 * iFirstData to the the first element is given.
108 *
109 * @pre
110 * @arg [iFirstControl, iLastControl) is a valid range.
111 * @arg [iFirstData, iFirstData + (iLastControl - iFirstControl)) is a valid range.
112 *
113 * @par complexity:
114 * O(D * log(N)) with
115 * @arg D = a figure that indicates the complexity of operations on data values.
116 * Is most probably linear with the dimension of the data value
117 * @arg N = number of nodes
118 */
119template <typename S, typename D, typename T>
120template <typename ScalarInputIterator, typename DataInputIterator>
121SplineLinear<S, D, T>::SplineLinear(ScalarInputIterator iFirstControl,
122 ScalarInputIterator iLastControl,
123 DataInputIterator iFirstData)
124{
125 while (iFirstControl != iLastControl)
126 {
127 Node node;
128 node.x = *iFirstControl++;
129 node.y = *iFirstData++;
130 nodes_.push_back(node);
131 }
132 init();
133}
134
135
136
137/** Get the linear interpolated data value that corresponds with constrol value @a iX.
138 *
139 * @pre this->isEmpty() == false
140 *
141 * @par complexity:
142 * O(D * log(N)) with
143 * @arg D = a figure that indicates the complexity of operations on data values.
144 * Is most probably linear with the dimension of the data value
145 * @arg N = number of nodes
146 */
147template <typename S, typename D, typename T>
148const typename SplineLinear<S, D, T>::TData
150{
151 LASS_ASSERT(!isEmpty());
152
153 const typename TNodes::const_iterator n = findNode(iX);
154 const TScalar dx = iX - n->x;
155
156 TData result(n->y);
157 TDataTraits::multiplyAccumulate(result, n->dy, dx);
158 return result;
159}
160
161
162
163/** Get the first derivative of data value that corresponds with constrol value @a iX.
164 *
165 * As long as @a iX is exact on a node, it equals to lim_{dx->0} (*this(iX + dx) - *this(iX)) / dx.
166 * With linear splines, in theory the first derivative does not exist on the nodes. This
167 * function however will return the first derivative on the right of the node. *
168 *
169 * @pre this->isEmpty() == false
170 *
171 * @par complexity:
172 * O(D * log(N)) with
173 * @arg D = a figure that indicates the complexity of operations on data values.
174 * Is most probably linear with the dimension of the data value
175 * @arg N = number of nodes
176 */
177template <typename S, typename D, typename T>
178const typename SplineLinear<S, D, T>::TData
180{
181 LASS_ASSERT(!isEmpty());
182
183 return findNode(iX)->dy;
184}
185
186
187
188/** Get the second derivative of data value that corresponds with constrol value @a iX.
189 *
190 * For a linear spline, the second derivative is always zero, except on the nodes where it
191 * does not exist. This function however will always return zero, even on the nodes.
192 *
193 * @pre this->isEmpty() == false
194 *
195 * @par complexity:
196 * O(D * log(N)) with
197 * @arg D = a figure that indicates the complexity of operations on data values.
198 * Is most probably linear with the dimension of the data value
199 * @arg N = number of nodes
200 */
201template <typename S, typename D, typename T>
202const typename SplineLinear<S, D, T>::TData
203SplineLinear<S, D, T>::derivative2(TScalar /*iX*/) const
204{
205 LASS_ASSERT(!isEmpty());
206
207 TData result;
208 TDataTraits::zero(result, dataDimension_);
209 return result;
210}
211
212
213
214/** Get the integrated data value between control points @a iA and @a iB.
215 *
216 * @pre this->isEmpty() == false
217 *
218 * @par complexity:
219 * O(D * M * log(N)) with
220 * @arg D = a figure that indicates the complexity of operations on data values.
221 * Is most probably linear with the dimension of the data value
222 * @arg M = number of nodes between @a iA and @a iB.
223 * @arg N = total number of nodes in spline
224 */
225template <typename S, typename D, typename T>
226const typename SplineLinear<S, D, T>::TData
227SplineLinear<S, D, T>::integral(TScalar iBegin, TScalar iEnd) const
228{
229 LASS_ASSERT(!isEmpty());
230
231 TNodeConstIterator first = findNode(iBegin);
232 TNodeConstIterator last = findNode(iEnd);
233 if (first == last)
234 {
235 TData result(first->y);
236 TDataTraits::scale(result, iEnd - iBegin);
237 TDataTraits::multiplyAccumulate(result, first->dy,
238 (num::sqr(iEnd - first->x) - num::sqr(iBegin - first->x)) / 2);
239 return result;
240 }
241 else
242 {
243 TScalar multiplier = 1;
244 if (iEnd < iBegin)
245 {
246 std::swap(iBegin, iEnd);
247 std::swap(first, last);
248 multiplier = -1;
249 }
250
251 TNodeConstIterator next = stde::next(first);
252
253 TData result(first->y);
254 TDataTraits::scale(result, next->x - iBegin);
255 TDataTraits::multiplyAccumulate(result, first->dy,
256 (num::sqr(next->x - first->x) - num::sqr(iBegin - first->x)) / 2);
257 first = next++;
258
259 while (first != last)
260 {
261 const TScalar dx = next->x - first->x;
262 TDataTraits::multiplyAccumulate(result, first->y, dx);
263 TDataTraits::multiplyAccumulate(result, first->dy, num::sqr(dx) / 2);
264 first = next++;
265 }
266
267 const TScalar dx = iEnd - first->x;
268 TDataTraits::multiplyAccumulate(result, first->y, dx);
269 TDataTraits::multiplyAccumulate(result, first->dy, num::sqr(dx) / 2);
270
271 TDataTraits::scale(result, multiplier);
272 return result;
273 }
274}
275
276
277
278/** return true if the spline contains any nodes at all.
279 * @par complexity:
280 * O(1)
281 */
282template <typename S, typename D, typename T>
284{
285 return nodes_.empty();
286}
287
288
289
290/** return the range of control values for which the spline can interpolate.
291 * @par complexity:
292 * O(1)
293 */
294template <typename S, typename D, typename T>
295const typename SplineLinear<S, D, T>::TControlRange
297{
298 typedef typename SplineLinear<S, D, T>::TControlRange TRange;
299 return TRange(nodes_.front().x, nodes_.back().x);
300}
301
302
303
304// --- private -------------------------------------------------------------------------------------
305
306template <typename S, typename D, typename T>
307void SplineLinear<S, D, T>::init()
308{
309 // are there any nodes at all? we need at least two!
310 //
311 const typename TNodes::size_type size = nodes_.size();
312 if (size < 2)
313 {
314 LASS_THROW("A linear interpolator needs at least two nodes! This one only has '"
315 << static_cast<unsigned>(nodes_.size()) << "'.");
316 }
317
318 typename TNodes::iterator prev = nodes_.begin();
319 dataDimension_ = TDataTraits::dimension(prev->y);
320 const typename TNodes::iterator end = nodes_.end();
321 for (typename TNodes::iterator i = stde::next(nodes_.begin()); i != end; prev = i++)
322 {
323 // check for increasing control components
324 //
325 if (prev->x >= i->x)
326 {
327 LASS_THROW("Nodes in linear interpolator must have absolutely increasing control "
328 "values.");
329 }
330
331 // check dimension of data
332 //
333 if (TDataTraits::dimension(i->y) != dataDimension_)
334 {
335 LASS_THROW("All data in linear interpolator must be of same dimension.");
336 }
337
338 // calculate derivative of previous node
339 //
340 prev->dy = i->y;
341 TDataTraits::multiplyAccumulate(prev->dy, prev->y, -1);
342 TDataTraits::scale(prev->dy, num::inv(i->x - prev->x));
343 }
344
345 // extend last node with same derivative as last edge.
346 stde::prev(end)->dy = stde::prev(end, 2)->dy;
347}
348
349
350
351/** binary search to find node that belongs to iX
352 *
353 * @return
354 * @arg the index @a i if @a iX is in the interval [@c nodes_[i].x, @c nodes_[i+1].x)
355 * @arg 0 if @a iX is smaller than @c nodes_[0].x</tt>
356 * @arg @c nodes_.size()-2 if @a iX is greater than @c nodes_[nodes_.size()-1].x
357 *
358 * complexity: O(ln N)
359 */
360template <typename S, typename D, typename T>
361const typename SplineLinear<S, D, T>::TNodeConstIterator
362SplineLinear<S, D, T>::findNode(TScalar iX) const
363{
364 LASS_ASSERT(nodes_.size() >= 2);
365
366 // early outs
367 //
368 if (iX < nodes_.front().x)
369 {
370 return nodes_.begin();
371 }
372 if (iX >= nodes_.back().x)
373 {
374 return stde::prev(nodes_.end());
375 }
376
377 // binary search
378 //
379 TNodeConstIterator first = nodes_.begin();
380 TNodeConstIterator last = nodes_.end();
381 while (stde::next(first) != last)
382 {
383 TNodeConstIterator middle = stde::next(first, std::distance(first, last) / 2);
384 LASS_ASSERT(middle != first && middle != last);
385
386 if (middle->x <= iX)
387 {
388 first = middle;
389 }
390 else
391 {
392 last = middle;
393 }
394 LASS_ASSERT(first != last);
395 }
396
397 LASS_ASSERT(first->x <= iX && last->x > iX);
398 return first;
399}
400
401
402
403}
404
405}
406
407#endif
408
409// EOF
const TControlRange controlRange() const override
return the range of control values for which the spline can interpolate.
bool isEmpty() const override
return true if the spline contains any nodes at all.
const TData derivative(TScalar iX) const override
Get the first derivative of data value that corresponds with constrol value iX.
const TData integral(TScalar iA, TScalar iB) const override
Get the integrated data value between control points iA and iB.
const TData operator()(TScalar iX) const override
Get the linear interpolated data value that corresponds with constrol value iX.
T sqr(const T &x)
return x * x
Definition basic_ops.h:162
T inv(const T &x)
return x ^ -1
Definition basic_ops.h:178
numeric types and traits.
Definition basic_ops.h:70
Library for Assembled Shared Sources.
Definition config.h:53