Easy3D 2.6.1
Loading...
Searching...
No Matches
spline_curve_fitting.h
1/********************************************************************
2 * Copyright (C) 2015 Liangliang Nan <liangliang.nan@gmail.com>
3 * https://3d.bk.tudelft.nl/liangliang/
4 *
5 * This file is part of Easy3D. If it is useful in your research/work,
6 * I would be grateful if you show your appreciation by citing it:
7 * ------------------------------------------------------------------
8 * Liangliang Nan.
9 * Easy3D: a lightweight, easy-to-use, and efficient C++ library
10 * for processing and rendering 3D data.
11 * Journal of Open Source Software, 6(64), 3255, 2021.
12 * ------------------------------------------------------------------
13 *
14 * Easy3D is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License Version 3
16 * as published by the Free Software Foundation.
17 *
18 * Easy3D is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 ********************************************************************/
26
27#ifndef EASY3D_CORE_SPLINE_CURVE_FITTING_H
28#define EASY3D_CORE_SPLINE_CURVE_FITTING_H
29
30
31#include <vector>
32#include <cassert>
33
34
35namespace easy3d {
36
37
64 template <template <size_t, class> class Point, size_t N, typename T>
66 public:
87
88 public:
95 explicit SplineCurveFitting(int k = 2, Node_e node_type = eOPEN_UNIFORM);
96
101 void set_ctrl_points(const std::vector<Point<N, T>> &points);
102
107 void get_ctrl_points(std::vector<Point<N, T>> &points) const;
108
113 void set_node_type(Node_e type);
114
123 Point<N, T> eval_f(T u) const;
124
130 Point<N, T> eval_df(T u) const;
131
136 int get_order() const { return _k; }
137
148 std::vector<T> get_equally_spaced_parameters(std::size_t steps) const;
149
150 private:
151 // -------------------------------------------------------------------------
153 // -------------------------------------------------------------------------
154
158 void assert_splines() const;
159
161 void set_nodal_vector();
162
164 void set_node_to_uniform();
165
167 void set_node_to_open_uniform();
168
178 Point<N, T> eval(T u,
179 const std::vector<Point<N, T>> &point,
180 int k,
181 const std::vector<T> &node,
182 int off = 0) const;
191 Point<N, T> eval_rec(T u,
192 std::vector<Point<N, T>> p_in,
193 int k,
194 std::vector<T> node_in) const;
195
196 // -------------------------------------------------------------------------
198 // -------------------------------------------------------------------------
199
200 Node_e _node_type;
201 int _k;
202 std::vector<Point<N, T>> _point;
203 std::vector<Point<N, T>> _vec;
204 std::vector<T> _node;
205 };
206
207
208 template <template <size_t, class> class Point, size_t N, typename T>
210 _node_type(node_type),
211 _k(k),
212 _point(_k),
213 _vec(_k - 1),
214 _node(_k + _point.size()) {
215 assert_splines();
216 }
217
218 // -----------------------------------------------------------------------------
219
220 template <template <size_t, class> class Point, size_t N, typename T>
221 void SplineCurveFitting<Point, N, T>::set_ctrl_points(const std::vector<Point<N, T>> &point) {
222 _point = point;
223 _vec.resize(_point.size() - 1);
224 for (int i = 0; i < (int) _vec.size(); ++i)
225 _vec[i] = _point[i + 1] - _point[i];
226 set_nodal_vector();
227 assert_splines();
228
229 for (int i = 0; i < (int) _vec.size(); ++i)
230 _vec[i] /= _node[_k + i] - _node[i + 1];
231 }
232
233 // -----------------------------------------------------------------------------
234
235 template <template <size_t, class> class Point, size_t N, typename T>
236 void SplineCurveFitting<Point, N, T>::get_ctrl_points(std::vector<Point<N, T>> &points) const {
237 points = _point;
238 }
239
240 // -----------------------------------------------------------------------------
241
243 template <template <size_t, class> class Point, size_t N, typename T>
245 _node_type = type;
246 set_nodal_vector();
247 assert_splines();
248 }
249
250 // -----------------------------------------------------------------------------
251
252 template <template <size_t, class> class Point, size_t N, typename T>
254 u = std::max(std::min(u, (T) 1), (T) 0); // clamp between [0 1]
255 return eval(u, _point, _k, _node);
256 }
257
258 // -----------------------------------------------------------------------------
259
260 template <template <size_t, class> class Point, size_t N, typename T>
262 u = std::max(std::min(u, (T) 1), (T) 0); // clamp between [0 1]
263 return eval(u, _vec, (_k - 1), _node, 1) * (T) (_k - 1);
264 }
265
266 // -----------------------------------------------------------------------------
267
268 template <template <size_t, class> class Point, size_t N, typename T>
270 assert_splines();
271
272 std::vector<T> lengths(steps, 0);
273 std::vector<T> parameters(steps, 0);
274 Point<N, T> prev_point;
275 for (std::size_t i = 0; i < steps; ++i) {
276 const T u = static_cast<T>(i) / static_cast<T>(steps - 1);
277 parameters[i] = u;
278 const Point<N, T> pos = eval_f(u);
279 if (i > 0)
280 lengths[i] = lengths[i - 1] + distance(pos, prev_point);
281 prev_point = pos;
282 }
283 T total_length = lengths.back();
284
285 // given a \c value and a sorted sequence of values, find the two indices such that
286 // sorted_values[left_index] <= value and sorted_values[right_index] >= value.
287 auto find_closest_less_or_equal = [](const std::vector<T> &sorted_values, T value,
288 std::size_t start) -> std::size_t {
289 assert(value >= sorted_values.front() && value <= sorted_values.back());
290 std::size_t index = start;
291 while (sorted_values[index] < value)
292 ++index;
293 while (sorted_values[index] > value)
294 --index;
295 return index;
296 };
297
298 std::vector<T> U(steps);
299 for (std::size_t i = 0; i < steps; ++i) {
300 T u = static_cast<T>(i) / static_cast<T>(steps - 1);
301 T length = total_length * u;
302 std::size_t left_index = find_closest_less_or_equal(lengths, length, i);
303 std::size_t right_index = (lengths[left_index] == length) ? left_index : left_index + 1;
304 if (lengths[left_index] == lengths[right_index])
305 u = parameters[left_index];
306 else { // linear interpolation
307 T left_u = parameters[left_index];
308 T right_u = parameters[right_index];
309 T w = (length - lengths[left_index]) / (lengths[right_index] - lengths[left_index]);
310 u = (1.0f - w) * left_u + w * right_u;
311 }
312 U[i] = u;
313 }
314 return U;
315 }
316
317 // -----------------------------------------------------------------------------
318
319 template <template <size_t, class> class Point, size_t N, typename T>
320 void SplineCurveFitting<Point, N, T>::assert_splines() const {
321 assert(_k > 1);
322 assert((int) _point.size() >= _k);
323 assert(_node.size() == (_k + _point.size()));
324 assert(_point.size() == (_vec.size() + 1));
325 }
326
327 // -----------------------------------------------------------------------------
328
329 template <template <size_t, class> class Point, size_t N, typename T>
330 void SplineCurveFitting<Point, N, T>::set_nodal_vector() {
331 if (_node_type == eOPEN_UNIFORM)
332 set_node_to_open_uniform();
333 else if (_node_type == eUNIFORM)
334 set_node_to_uniform();
335 }
336
337 // -----------------------------------------------------------------------------
338
339 template <template <size_t, class> class Point, size_t N, typename T>
340 void SplineCurveFitting<Point, N, T>::set_node_to_uniform() {
341 const std::size_t n = _point.size() - 1;
342 _node.resize(_k + n + 1);
343
344 T step = (T) 1 / (T) (n - _k + 2);
345 for (std::size_t i = 0; i < _node.size(); ++i) {
346 _node[i] = ((T) i) * step - step * (T) (_k - 1);
347 }
348 }
349
350 // -----------------------------------------------------------------------------
351
352 template <template <size_t, class> class Point, size_t N, typename T>
353 void SplineCurveFitting<Point, N, T>::set_node_to_open_uniform() {
354 _node.resize(_k + _point.size());
355
356 int acc = 1;
357 for (int i = 0; i < (int) _node.size(); ++i) {
358 if (i < _k)
359 _node[i] = 0.;
360 else if (i >= ((int) _point.size() + 1))
361 _node[i] = 1.;
362 else {
363 _node[i] = (T) acc / (T) (_point.size() + 1 - _k);
364 acc++;
365 }
366 }
367 }
368
369 // -----------------------------------------------------------------------------
370
371 template <template <size_t, class> class Point, size_t N, typename T>
372 Point<N, T> SplineCurveFitting<Point, N, T>::eval(T u,
373 const std::vector<Point<N, T>> &point,
374 int k,
375 const std::vector<T> &node,
376 int off) const {
377 assert(k > 1);
378 assert((int) point.size() >= k);
379 assert_splines();
380 int dec = 0;
381 // TODO: better search with dychotomi ?
382 // TODO: check for overflow
383 while (u > node[dec + k + off])
384 dec++;
385
386 // TODO: use buffers in attributes for better performances ?
387 std::vector<Point<N, T>> p_rec(k, Point<N, T>());
388 for (int i = dec, j = 0; i < (dec + k); ++i, ++j)
389 p_rec[j] = point[i];
390
391 std::vector<T> node_rec(k + k - 2, (T) 0);
392 for (int i = (dec + 1), j = 0; i < (dec + k + k - 1); ++i, ++j)
393 node_rec[j] = node[i + off];
394
395 return eval_rec(u, p_rec, k, node_rec);
396 }
397
398 // -----------------------------------------------------------------------------
399
400 template <template <size_t, class> class Point, size_t N, typename T>
401 Point<N, T> SplineCurveFitting<Point, N, T>::eval_rec(T u,
402 std::vector<Point<N, T>> p_in,
403 int k,
404 std::vector<T> node_in) const {
405 if (p_in.size() == 1)
406 return p_in[0];
407
408 // TODO: use buffers in attributes for better performances ?
409 std::vector<Point<N, T>> p_out(k - 1, Point<N, T>());
410 for (int i = 0; i < (k - 1); ++i) {
411 const T n0 = node_in[i + k - 1];
412 const T n1 = node_in[i];
413 const T f0 = (n0 - u) / (n0 - n1);
414 const T f1 = (u - n1) / (n0 - n1);
415
416 p_out[i] = p_in[i] * f0 + p_in[i + 1] * f1;
417 }
418
419 std::vector<T> node_out(node_in.size() - 2);
420 for (int i = 1, j = 0; i < ((int) node_in.size() - 1); ++i, ++j)
421 node_out[j] = node_in[i];
422
423 return eval_rec(u, p_out, (k - 1), node_out);
424 }
425
426} // namespace easy3d
427
428
429#endif // EASY3D_CORE_SPLINE_CURVE_FITTING_H
SplineCurveFitting(int k=2, Node_e node_type=eOPEN_UNIFORM)
Constructor.
Definition spline_curve_fitting.h:209
void set_ctrl_points(const std::vector< Point< N, T > > &points)
Sets the positions of the spline control points.
Definition spline_curve_fitting.h:221
void get_ctrl_points(std::vector< Point< N, T > > &points) const
Gets the control points of the spline.
Definition spline_curve_fitting.h:236
Point< N, T > eval_df(T u) const
Evaluates speed of the spline.
Definition spline_curve_fitting.h:261
Point< N, T > eval_f(T u) const
Evaluates position of the spline.
Definition spline_curve_fitting.h:253
Node_e
The nodal vector (or knot vector) type.
Definition spline_curve_fitting.h:83
@ eOPEN_UNIFORM
Open uniform nodal vector.
Definition spline_curve_fitting.h:85
@ eUNIFORM
Uniform nodal vector.
Definition spline_curve_fitting.h:84
void set_node_type(Node_e type)
Sets the nodal vector type.
Definition spline_curve_fitting.h:244
int get_order() const
Gets the order of the spline.
Definition spline_curve_fitting.h:136
std::vector< T > get_equally_spaced_parameters(std::size_t steps) const
Gets parameters such that evaluation of the curve positions using these parameters results in equally...
Definition spline_curve_fitting.h:269
Definition collider.cpp:182
T distance(const Vec< N, T > &v1, const Vec< N, T > &v2)
Computes the distance between two vectors/points.
Definition vec.h:295
T length(const Vec< N, T > &v)
Computes the length/magnitude of a vector.
Definition vec.h:289