SplineFitting.h
Go to the documentation of this file.
1 // This file is part of Eigen, a lightweight C++ template library
2 // for linear algebra.
3 //
4 // Copyright (C) 20010-2011 Hauke Heibel <hauke.heibel@gmail.com>
5 //
6 // This Source Code Form is subject to the terms of the Mozilla
7 // Public License v. 2.0. If a copy of the MPL was not distributed
8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 
10 #ifndef EIGEN_SPLINE_FITTING_H
11 #define EIGEN_SPLINE_FITTING_H
12 
13 #include <algorithm>
14 #include <functional>
15 #include <numeric>
16 #include <vector>
17 
18 // IWYU pragma: private
19 #include "./InternalHeaderCheck.h"
20 
21 #include "SplineFwd.h"
22 
23 #include "../../../../Eigen/LU"
24 #include "../../../../Eigen/QR"
25 
26 namespace Eigen {
46 template <typename KnotVectorType>
47 void KnotAveraging(const KnotVectorType& parameters, DenseIndex degree, KnotVectorType& knots) {
48  knots.resize(parameters.size() + degree + 1);
49 
50  for (DenseIndex j = 1; j < parameters.size() - degree; ++j) knots(j + degree) = parameters.segment(j, degree).mean();
51 
52  knots.segment(0, degree + 1) = KnotVectorType::Zero(degree + 1);
53  knots.segment(knots.size() - degree - 1, degree + 1) = KnotVectorType::Ones(degree + 1);
54 }
55 
77 template <typename KnotVectorType, typename ParameterVectorType, typename IndexArray>
78 void KnotAveragingWithDerivatives(const ParameterVectorType& parameters, const unsigned int degree,
79  const IndexArray& derivativeIndices, KnotVectorType& knots) {
80  typedef typename ParameterVectorType::Scalar Scalar;
81 
82  DenseIndex numParameters = parameters.size();
83  DenseIndex numDerivatives = derivativeIndices.size();
84 
85  if (numDerivatives < 1) {
86  KnotAveraging(parameters, degree, knots);
87  return;
88  }
89 
90  DenseIndex startIndex;
91  DenseIndex endIndex;
92 
93  DenseIndex numInternalDerivatives = numDerivatives;
94 
95  if (derivativeIndices[0] == 0) {
96  startIndex = 0;
97  --numInternalDerivatives;
98  } else {
99  startIndex = 1;
100  }
101  if (derivativeIndices[numDerivatives - 1] == numParameters - 1) {
102  endIndex = numParameters - degree;
103  --numInternalDerivatives;
104  } else {
105  endIndex = numParameters - degree - 1;
106  }
107 
108  // There are (endIndex - startIndex + 1) knots obtained from the averaging
109  // and 2 for the first and last parameters.
110  DenseIndex numAverageKnots = endIndex - startIndex + 3;
111  KnotVectorType averageKnots(numAverageKnots);
112  averageKnots[0] = parameters[0];
113 
114  int newKnotIndex = 0;
115  for (DenseIndex i = startIndex; i <= endIndex; ++i)
116  averageKnots[++newKnotIndex] = parameters.segment(i, degree).mean();
117  averageKnots[++newKnotIndex] = parameters[numParameters - 1];
118 
119  newKnotIndex = -1;
120 
121  ParameterVectorType temporaryParameters(numParameters + 1);
122  KnotVectorType derivativeKnots(numInternalDerivatives);
123  for (DenseIndex i = 0; i < numAverageKnots - 1; ++i) {
124  temporaryParameters[0] = averageKnots[i];
125  ParameterVectorType parameterIndices(numParameters);
126  int temporaryParameterIndex = 1;
127  for (DenseIndex j = 0; j < numParameters; ++j) {
128  Scalar parameter = parameters[j];
129  if (parameter >= averageKnots[i] && parameter < averageKnots[i + 1]) {
130  parameterIndices[temporaryParameterIndex] = j;
131  temporaryParameters[temporaryParameterIndex++] = parameter;
132  }
133  }
134  temporaryParameters[temporaryParameterIndex] = averageKnots[i + 1];
135 
136  for (int j = 0; j <= temporaryParameterIndex - 2; ++j) {
137  for (DenseIndex k = 0; k < derivativeIndices.size(); ++k) {
138  if (parameterIndices[j + 1] == derivativeIndices[k] && parameterIndices[j + 1] != 0 &&
139  parameterIndices[j + 1] != numParameters - 1) {
140  derivativeKnots[++newKnotIndex] = temporaryParameters.segment(j, 3).mean();
141  break;
142  }
143  }
144  }
145  }
146 
147  KnotVectorType temporaryKnots(averageKnots.size() + derivativeKnots.size());
148 
149  std::merge(averageKnots.data(), averageKnots.data() + averageKnots.size(), derivativeKnots.data(),
150  derivativeKnots.data() + derivativeKnots.size(), temporaryKnots.data());
151 
152  // Number of knots (one for each point and derivative) plus spline order.
153  DenseIndex numKnots = numParameters + numDerivatives + degree + 1;
154  knots.resize(numKnots);
155 
156  knots.head(degree).fill(temporaryKnots[0]);
157  knots.tail(degree).fill(temporaryKnots.template tail<1>()[0]);
158  knots.segment(degree, temporaryKnots.size()) = temporaryKnots;
159 }
160 
170 template <typename PointArrayType, typename KnotVectorType>
171 void ChordLengths(const PointArrayType& pts, KnotVectorType& chord_lengths) {
172  typedef typename KnotVectorType::Scalar Scalar;
173 
174  const DenseIndex n = pts.cols();
175 
176  // 1. compute the column-wise norms
177  chord_lengths.resize(pts.cols());
178  chord_lengths[0] = 0;
179  chord_lengths.rightCols(n - 1) =
180  (pts.array().leftCols(n - 1) - pts.array().rightCols(n - 1)).matrix().colwise().norm();
181 
182  // 2. compute the partial sums
183  std::partial_sum(chord_lengths.data(), chord_lengths.data() + n, chord_lengths.data());
184 
185  // 3. normalize the data
186  chord_lengths /= chord_lengths(n - 1);
187  chord_lengths(n - 1) = Scalar(1);
188 }
189 
194 template <typename SplineType>
196  typedef typename SplineType::KnotVectorType KnotVectorType;
197  typedef typename SplineType::ParameterVectorType ParameterVectorType;
198 
207  template <typename PointArrayType>
208  static SplineType Interpolate(const PointArrayType& pts, DenseIndex degree);
209 
219  template <typename PointArrayType>
220  static SplineType Interpolate(const PointArrayType& pts, DenseIndex degree, const KnotVectorType& knot_parameters);
221 
239  template <typename PointArrayType, typename IndexArray>
240  static SplineType InterpolateWithDerivatives(const PointArrayType& points, const PointArrayType& derivatives,
241  const IndexArray& derivativeIndices, const unsigned int degree);
242 
259  template <typename PointArrayType, typename IndexArray>
260  static SplineType InterpolateWithDerivatives(const PointArrayType& points, const PointArrayType& derivatives,
261  const IndexArray& derivativeIndices, const unsigned int degree,
262  const ParameterVectorType& parameters);
263 };
264 
265 template <typename SplineType>
266 template <typename PointArrayType>
267 SplineType SplineFitting<SplineType>::Interpolate(const PointArrayType& pts, DenseIndex degree,
268  const KnotVectorType& knot_parameters) {
270  typedef typename SplineType::ControlPointVectorType ControlPointVectorType;
271 
273 
274  KnotVectorType knots;
275  KnotAveraging(knot_parameters, degree, knots);
276 
277  DenseIndex n = pts.cols();
279  for (DenseIndex i = 1; i < n - 1; ++i) {
280  const DenseIndex span = SplineType::Span(knot_parameters[i], degree, knots);
281 
282  // The segment call should somehow be told the spline order at compile time.
283  A.row(i).segment(span - degree, degree + 1) = SplineType::BasisFunctions(knot_parameters[i], degree, knots);
284  }
285  A(0, 0) = 1.0;
286  A(n - 1, n - 1) = 1.0;
287 
289 
290  // Here, we are creating a temporary due to an Eigen issue.
291  ControlPointVectorType ctrls = qr.solve(MatrixType(pts.transpose())).transpose();
292 
293  return SplineType(knots, ctrls);
294 }
295 
296 template <typename SplineType>
297 template <typename PointArrayType>
298 SplineType SplineFitting<SplineType>::Interpolate(const PointArrayType& pts, DenseIndex degree) {
299  KnotVectorType chord_lengths; // knot parameters
300  ChordLengths(pts, chord_lengths);
301  return Interpolate(pts, degree, chord_lengths);
302 }
303 
304 template <typename SplineType>
305 template <typename PointArrayType, typename IndexArray>
306 SplineType SplineFitting<SplineType>::InterpolateWithDerivatives(const PointArrayType& points,
307  const PointArrayType& derivatives,
308  const IndexArray& derivativeIndices,
309  const unsigned int degree,
310  const ParameterVectorType& parameters) {
312  typedef typename SplineType::ControlPointVectorType ControlPointVectorType;
313 
315 
316  const DenseIndex n = points.cols() + derivatives.cols();
317 
318  KnotVectorType knots;
319 
320  KnotAveragingWithDerivatives(parameters, degree, derivativeIndices, knots);
321 
322  // fill matrix
324 
325  // Use these dimensions for quicker populating, then transpose for solving.
326  MatrixType b(points.rows(), n);
327 
328  DenseIndex startRow;
329  DenseIndex derivativeStart;
330 
331  // End derivatives.
332  if (derivativeIndices[0] == 0) {
333  A.template block<1, 2>(1, 0) << -1, 1;
334 
335  Scalar y = (knots(degree + 1) - knots(0)) / degree;
336  b.col(1) = y * derivatives.col(0);
337 
338  startRow = 2;
339  derivativeStart = 1;
340  } else {
341  startRow = 1;
342  derivativeStart = 0;
343  }
344  if (derivativeIndices[derivatives.cols() - 1] == points.cols() - 1) {
345  A.template block<1, 2>(n - 2, n - 2) << -1, 1;
346 
347  Scalar y = (knots(knots.size() - 1) - knots(knots.size() - (degree + 2))) / degree;
348  b.col(b.cols() - 2) = y * derivatives.col(derivatives.cols() - 1);
349  }
350 
351  DenseIndex row = startRow;
352  DenseIndex derivativeIndex = derivativeStart;
353  for (DenseIndex i = 1; i < parameters.size() - 1; ++i) {
354  const DenseIndex span = SplineType::Span(parameters[i], degree, knots);
355 
356  if (derivativeIndex < derivativeIndices.size() && derivativeIndices[derivativeIndex] == i) {
357  A.block(row, span - degree, 2, degree + 1) =
358  SplineType::BasisFunctionDerivatives(parameters[i], 1, degree, knots);
359 
360  b.col(row++) = points.col(i);
361  b.col(row++) = derivatives.col(derivativeIndex++);
362  } else {
363  A.row(row).segment(span - degree, degree + 1) = SplineType::BasisFunctions(parameters[i], degree, knots);
364  b.col(row++) = points.col(i);
365  }
366  }
367  b.col(0) = points.col(0);
368  b.col(b.cols() - 1) = points.col(points.cols() - 1);
369  A(0, 0) = 1;
370  A(n - 1, n - 1) = 1;
371 
372  // Solve
374  ControlPointVectorType controlPoints = lu.solve(MatrixType(b.transpose())).transpose();
375 
376  SplineType spline(knots, controlPoints);
377 
378  return spline;
379 }
380 
381 template <typename SplineType>
382 template <typename PointArrayType, typename IndexArray>
383 SplineType SplineFitting<SplineType>::InterpolateWithDerivatives(const PointArrayType& points,
384  const PointArrayType& derivatives,
385  const IndexArray& derivativeIndices,
386  const unsigned int degree) {
387  ParameterVectorType parameters;
388  ChordLengths(points, parameters);
389  return InterpolateWithDerivatives(points, derivatives, derivativeIndices, degree, parameters);
390 }
391 } // namespace Eigen
392 
393 #endif // EIGEN_SPLINE_FITTING_H
int i
Definition: BiCGSTAB_step_by_step.cpp:9
const unsigned n
Definition: CG3DPackingUnitTest.cpp:11
HouseholderQR< MatrixXf > qr(A)
m row(1)
Scalar * b
Definition: benchVecAdd.cpp:17
SCALAR Scalar
Definition: bench_gemm.cpp:45
Matrix< SCALARA, Dynamic, Dynamic, opt_A > A
Definition: bench_gemm.cpp:47
MatrixXf MatrixType
Definition: benchmark-blocking-sizes.cpp:52
LU decomposition of a matrix with complete pivoting, and related features.
Definition: FullPivLU.h:63
cout<< "Here is the matrix m:"<< endl<< m<< endl;Eigen::FullPivLU< Matrix5x3 > lu(m)
Eigen::Map< Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor >, 0, Eigen::OuterStride<> > matrix(T *data, int rows, int cols, int stride)
Definition: common.h:85
void ChordLengths(const PointArrayType &pts, KnotVectorType &chord_lengths)
Computes chord length parameters which are required for spline interpolation.
Definition: SplineFitting.h:171
void KnotAveraging(const KnotVectorType &parameters, DenseIndex degree, KnotVectorType &knots)
Computes knot averages.
Definition: SplineFitting.h:47
void KnotAveragingWithDerivatives(const ParameterVectorType &parameters, const unsigned int degree, const IndexArray &derivativeIndices, KnotVectorType &knots)
Computes knot averages when derivative constraints are present. Note that this is a technical interpr...
Definition: SplineFitting.h:78
Scalar * y
Definition: level1_cplx_impl.h:128
char char char int int * k
Definition: level2_impl.h:374
Namespace containing all symbols from the Eigen library.
Definition: bench_norm.cpp:70
EIGEN_DEFAULT_DENSE_INDEX_TYPE DenseIndex
Definition: Meta.h:75
const Mdouble degree
Definition: ExtendedMath.h:32
double Zero
Definition: pseudosolid_node_update_elements.cc:35
Spline fitting methods.
Definition: SplineFitting.h:195
SplineType::KnotVectorType KnotVectorType
Definition: SplineFitting.h:196
static SplineType InterpolateWithDerivatives(const PointArrayType &points, const PointArrayType &derivatives, const IndexArray &derivativeIndices, const unsigned int degree)
Fits an interpolating spline to the given data points and derivatives.
Definition: SplineFitting.h:383
static SplineType Interpolate(const PointArrayType &pts, DenseIndex degree)
Fits an interpolating Spline to the given data points.
Definition: SplineFitting.h:298
SplineType::ParameterVectorType ParameterVectorType
Definition: SplineFitting.h:197
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2