Easy3D 2.5.3
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
60 template<typename Point_t>
62 public:
63 typedef typename Point_t::FT FT;
64 enum Node_e {
65 eUNIFORM,
67 };
68
69 public:
74 explicit SplineCurveFitting(int k = 2, Node_e node_type = eOPEN_UNIFORM);
75
77 void set_ctrl_points(const std::vector<Point_t> &point);
78
80 void get_ctrl_points(std::vector<Point_t> &points) const;
81
83 void set_node_type(Node_e type);
84
90 Point_t eval_f(FT u) const;
91
93 Point_t eval_df(FT u) const;
94
95 int get_order() const { return _k; }
96
103 std::vector<FT> get_equally_spaced_parameters(std::size_t steps) const;
104
105 private:
106 // -------------------------------------------------------------------------
108 // -------------------------------------------------------------------------
109
110 void assert_splines() const;
111
114 void set_nodal_vector();
115
117 void set_node_to_uniform();
118
120 void set_node_to_open_uniform();
121
132 Point_t eval(FT u,
133 const std::vector<Point_t> &point,
134 int k,
135 const std::vector<FT> &node,
136 int off = 0) const;
137
138 Point_t eval_rec(FT u,
139 std::vector<Point_t> p_in,
140 int k,
141 std::vector<FT> node_in) const;
142
143 // -------------------------------------------------------------------------
145 // -------------------------------------------------------------------------
146
147 Node_e _node_type;
148 int _k;
149 std::vector<Point_t> _point;
150 std::vector<Point_t> _vec;
151 std::vector<FT> _node;
152 };
153
154
155 template<typename Point_t>
157 _node_type(node_type),
158 _k(k),
159 _point(_k),
160 _vec(_k - 1),
161 _node(_k + _point.size()) {
162 assert_splines();
163 }
164
165 // -----------------------------------------------------------------------------
166
167 template<typename Point_t>
168 void SplineCurveFitting<Point_t>::set_ctrl_points(const std::vector<Point_t> &point) {
169 _point = point;
170 _vec.resize(_point.size() - 1);
171 for (int i = 0; i < (int) _vec.size(); ++i)
172 _vec[i] = _point[i + 1] - _point[i];
173 set_nodal_vector();
174 assert_splines();
175
176 for (int i = 0; i < (int) _vec.size(); ++i)
177 _vec[i] /= _node[_k + i] - _node[i + 1];
178 }
179
180 // -----------------------------------------------------------------------------
181
182 template<typename Point_t>
183 void SplineCurveFitting<Point_t>::get_ctrl_points(std::vector<Point_t> &points) const {
184 points = _point;
185 }
186
187 // -----------------------------------------------------------------------------
188
190 template<typename Point_t>
192 _node_type = type;
193 set_nodal_vector();
194 assert_splines();
195 }
196
197 // -----------------------------------------------------------------------------
198
199 template<typename Point_t>
201 u = std::max(std::min(u, (FT) 1), (FT) 0); // clamp between [0 1]
202 return eval(u, _point, _k, _node);
203 }
204
205 // -----------------------------------------------------------------------------
206
207 template<typename Point_t>
209 u = std::max(std::min(u, (FT) 1), (FT) 0); // clamp between [0 1]
210 return eval(u, _vec, (_k - 1), _node, 1) * (FT) (_k - 1);
211 }
212
213 // -----------------------------------------------------------------------------
214
215 template<typename Point_t>
216 std::vector<typename Point_t::FT> SplineCurveFitting<Point_t>::get_equally_spaced_parameters(std::size_t steps) const {
217 assert_splines();
218
219 std::vector<FT> lengths(steps, 0);
220 std::vector<FT> parameters(steps, 0);
221 Point_t prev_point;
222 for (std::size_t i = 0; i < steps; ++i) {
223 const FT u = static_cast<FT>(i) / static_cast<FT>(steps - 1);
224 parameters[i] = u;
225 const Point_t pos = eval_f(u);
226 if (i > 0)
227 lengths[i] = lengths[i - 1] + distance(pos, prev_point);
228 prev_point = pos;
229 }
230 FT total_length = lengths.back();
231
232 // given a \c value and a sorted sequence of values, find the two indices such that
233 // sorted_values[left_index] <= value and sorted_values[right_index] >= value.
234 auto find_closest_less_or_equal = [](const std::vector<FT> &sorted_values, FT value,
235 std::size_t start) -> std::size_t {
236 assert(value >= sorted_values.front() && value <= sorted_values.back());
237 std::size_t index = start;
238 while (sorted_values[index] < value)
239 ++index;
240 while (sorted_values[index] > value)
241 --index;
242 return index;
243 };
244
245 std::vector<FT> U(steps);
246 for (std::size_t i = 0; i < steps; ++i) {
247 FT u = static_cast<FT>(i) / static_cast<FT>(steps - 1);
248 FT length = total_length * u;
249 std::size_t left_index = find_closest_less_or_equal(lengths, length, i);
250 std::size_t right_index = (lengths[left_index] == length) ? left_index : left_index + 1;
251 if (lengths[left_index] == lengths[right_index])
252 u = parameters[left_index];
253 else { // linear interpolation
254 FT left_u = parameters[left_index];
255 FT right_u = parameters[right_index];
256 FT w = (length - lengths[left_index]) / (lengths[right_index] - lengths[left_index]);
257 u = (1.0f - w) * left_u + w * right_u;
258 }
259 U[i] = u;
260 }
261 return U;
262 }
263
264 // -----------------------------------------------------------------------------
265
266 template<typename Point_t>
268 assert(_k > 1);
269 assert((int) _point.size() >= _k);
270 assert(_node.size() == (_k + _point.size()));
271 assert(_point.size() == (_vec.size() + 1));
272 }
273
274 // -----------------------------------------------------------------------------
275
276 template<typename Point_t>
277 void SplineCurveFitting<Point_t>::set_nodal_vector() {
278 if (_node_type == eOPEN_UNIFORM)
279 set_node_to_open_uniform();
280 else if (_node_type == eUNIFORM)
281 set_node_to_uniform();
282 }
283
284 // -----------------------------------------------------------------------------
285
286 template<typename Point_t>
287 void SplineCurveFitting<Point_t>::set_node_to_uniform() {
288 const std::size_t n = _point.size() - 1;
289 _node.resize(_k + n + 1);
290
291 FT step = (FT) 1 / (FT) (n - _k + 2);
292 for (std::size_t i = 0; i < _node.size(); ++i) {
293 _node[i] = ((FT) i) * step - step * (FT) (_k - 1);
294 }
295 }
296
297 // -----------------------------------------------------------------------------
298
299 template<typename Point_t>
300 void SplineCurveFitting<Point_t>::set_node_to_open_uniform() {
301 _node.resize(_k + _point.size());
302
303 int acc = 1;
304 for (int i = 0; i < (int) _node.size(); ++i) {
305 if (i < _k)
306 _node[i] = 0.;
307 else if (i >= ((int) _point.size() + 1))
308 _node[i] = 1.;
309 else {
310 _node[i] = (FT) acc / (FT) (_point.size() + 1 - _k);
311 acc++;
312 }
313 }
314 }
315
316 // -----------------------------------------------------------------------------
317
318 template<typename Point_t>
319 Point_t SplineCurveFitting<Point_t>::eval(FT u,
320 const std::vector<Point_t> &point,
321 int k,
322 const std::vector<FT> &node,
323 int off) const {
324 assert(k > 1);
325 assert((int) point.size() >= k);
326 assert_splines();
327 int dec = 0;
328 // TODO: better search with dychotomi ?
329 // TODO: check for overflow
330 while (u > node[dec + k + off])
331 dec++;
332
333 // TODO: use buffers in attributes for better performances ?
334 std::vector<Point_t> p_rec(k, Point_t());
335 for (int i = dec, j = 0; i < (dec + k); ++i, ++j)
336 p_rec[j] = point[i];
337
338 std::vector<FT> node_rec(k + k - 2, (FT) 0);
339 for (int i = (dec + 1), j = 0; i < (dec + k + k - 1); ++i, ++j)
340 node_rec[j] = node[i + off];
341
342 return eval_rec(u, p_rec, k, node_rec);
343 }
344
345 // -----------------------------------------------------------------------------
346
347 template<typename Point_t>
348 Point_t SplineCurveFitting<Point_t>::eval_rec(FT u,
349 std::vector<Point_t> p_in,
350 int k,
351 std::vector<FT> node_in) const {
352 if (p_in.size() == 1)
353 return p_in[0];
354
355 // TODO: use buffers in attributes for better performances ?
356 std::vector<Point_t> p_out(k - 1, Point_t());
357 for (int i = 0; i < (k - 1); ++i) {
358 const FT n0 = node_in[i + k - 1];
359 const FT n1 = node_in[i];
360 const FT f0 = (n0 - u) / (n0 - n1);
361 const FT f1 = (u - n1) / (n0 - n1);
362
363 p_out[i] = p_in[i] * f0 + p_in[i + 1] * f1;
364 }
365
366 std::vector<FT> node_out(node_in.size() - 2);
367 for (int i = 1, j = 0; i < ((int) node_in.size() - 1); ++i, ++j)
368 node_out[j] = node_in[i];
369
370 return eval_rec(u, p_out, (k - 1), node_out);
371 }
372
373} // namespace easy3d
374
375
376#endif // EASY3D_CORE_SPLINE_CURVE_FITTING_H
Spline curve fitting for arbitrary dimensions.
Definition: spline_curve_fitting.h:61
SplineCurveFitting(int k=2, Node_e node_type=eOPEN_UNIFORM)
Constructor.
Definition: spline_curve_fitting.h:156
Point_t eval_df(FT u) const
Evaluates speed of the spline.
Definition: spline_curve_fitting.h:208
void set_ctrl_points(const std::vector< Point_t > &point)
Sets the position of the spline control points.
Definition: spline_curve_fitting.h:168
Node_e
Definition: spline_curve_fitting.h:64
@ eOPEN_UNIFORM
Connected to the first and last control points.
Definition: spline_curve_fitting.h:66
void get_ctrl_points(std::vector< Point_t > &points) const
Gets the control points of the spline.
Definition: spline_curve_fitting.h:183
Point_t eval_f(FT u) const
Evaluates position of the spline.
Definition: spline_curve_fitting.h:200
std::vector< FT > 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:216
void set_node_type(Node_e type)
Sets the the nodal vector type.
Definition: spline_curve_fitting.h:191
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