cgv
Loading...
Searching...
No Matches
parametric_curve.h
1#pragma once
2
3#include "fvec.h"
4#include "fmat.h"
5#include "interpolate.h"
6
7namespace cgv {
8namespace math {
9
13template<typename DerivedT, typename ParamT>
15public:
19 ParamT evaluate(ParamT t) const {
20 return static_cast<DerivedT const&>(*this).evaluate(t);
21 }
22
24 ParamT total_length() const {
25 return static_cast<DerivedT const&>(*this).total_length();
26 }
27};
28
32template<typename DerivedT, typename ParamT>
34public:
38 ParamT evaluate(ParamT d) const {
39 return static_cast<DerivedT const&>(*this).evaluate(d);
40 }
41
43 ParamT total_length() const {
44 return static_cast<DerivedT const&>(*this).total_length();
45 }
46};
47
50template<typename... Ts>
52
56template<template<typename> typename DerivedT, typename PointT>
57class parametric_curve<DerivedT<PointT>> {
58public:
59 using point_type = PointT;
60
65 template<typename ParamT = float>
66 PointT evaluate(ParamT t) const {
67 return static_cast<DerivedT<PointT> const&>(*this).evaluate(t);
68 }
69
77 template<typename ParamT = float>
78 std::vector<PointT> sample(size_t num_segments) const {
79 std::vector<PointT> points;
80 points.reserve(num_segments + 1);
81 sequence_transform<ParamT>(std::back_inserter(points), [this](ParamT t) { return evaluate(t); }, num_segments + 1);
82 return points;
83 }
84
94 template<template<typename> typename ParameterizationT, typename ParamT = float>
95 std::vector<PointT> sample(size_t num_segments, const curve_parameterization<ParameterizationT<ParamT>, ParamT>& parameterization) const {
96 std::vector<PointT> points;
97 points.reserve(num_segments + 1);
98 sequence_transform<ParamT>(std::back_inserter(points), [this, &parameterization](ParamT t) {
99 ParamT d = t * parameterization.total_length();
100 return evaluate(parameterization.evaluate(d));
101 }, num_segments + 1);
102 return points;
103 }
104};
105
109template<template<class> class CurveT, typename T, cgv::type::uint32_type N>
110T arc_length(const parametric_curve<CurveT<fvec<T, N>>>& curve, T t = T(1), int num_segments = 128) {
111 num_segments = std::max(num_segments, 1);
112 T step = T(1) / static_cast<T>(num_segments);
113 T result = T(0);
114
115 fvec<T, N> prev_point = curve.evaluate(T(0));
116 for(int i = 0; i < num_segments; ++i) {
117 T t_ = static_cast<T>(i + 1) * step;
118 if(t_ >= t) {
119 fvec<T, N> next_point = curve.evaluate(t);
120 result += length(next_point - prev_point);
121 break;
122 }
123
124 fvec<T, N> next_point = curve.evaluate(t_);
125 result += length(next_point - prev_point);
126 prev_point = next_point;
127 }
128
129 return result;
130}
131
134template<typename T>
135class arc_length_linear_approximation : public curve_arc_length<arc_length_linear_approximation<T>, T> {
136public:
137 template<template<class> class CurveT, cgv::type::uint32_type N>
138 arc_length_linear_approximation(const parametric_curve<CurveT<fvec<T, N>>>& curve, int num_samples = 128) {
139 num_samples = std::max(num_samples, 2);
140 _lengths.breakpoints.reserve(num_samples);
141 _lengths.breakpoints.push_back(T(0));
142
143 T step = T(1) / static_cast<T>(num_samples - 1);
144
145 fvec<T, N> prev_point = curve.evaluate(T(0));
146 for(int i = 1; i < num_samples; ++i) {
147 T t_ = static_cast<T>(i) * step;
148 fvec<T, N> next_point = curve.evaluate(t_);
149 _lengths.breakpoints.push_back(_lengths.breakpoints.back() + length(next_point - prev_point));
150 prev_point = next_point;
151 }
152
153 _lengths.domain = { T(0), T(1) };
154 }
155
156 T evaluate(T t) const {
157 return _lengths.evaluate(t);
158 }
159
160 T total_length() const {
161 return _lengths.breakpoints.back();
162 }
163
164 const std::vector<T>& lengths() const {
165 return _lengths.breakpoints;
166 }
167
168private:
170};
171
174template<typename T>
175class arc_length_bezier_approximation : public curve_arc_length<arc_length_bezier_approximation<T>, T> {
176public:
177 template<template<class> class CurveT, cgv::type::uint32_type N>
178 arc_length_bezier_approximation(const parametric_curve<CurveT<fvec<T, N>>>& curve, int num_segments = 4, int num_segment_subdivisions = 8) {
179 num_segments = std::max(num_segments, 1);
180 num_segment_subdivisions = std::max(num_segment_subdivisions, 1);
181 int num_samples = 3 * num_segments * num_segment_subdivisions;
182
183 std::vector<T> arc_lengths;
184 T t_step = T(1) / static_cast<T>(num_samples);
185 int next_save_index = num_segment_subdivisions - 1;
186
187 fvec<T, N> prev_point = curve.evaluate(T(0));
188 for(int i = 0; i < num_samples; ++i) {
189 T t = static_cast<T>(i + 1) * t_step;
190 fvec<T, N> next_point = curve.evaluate(t);
191 _total_length += length(next_point - prev_point);
192
193 if(i == next_save_index) {
194 arc_lengths.push_back(_total_length);
195 next_save_index += num_segment_subdivisions;
196 }
197
198 prev_point = next_point;
199 }
200
201 _lengths.push_back(T(0));
202
203 for(int i = 0; i < num_segments; ++i) {
204 auto d_prev = _lengths.back();
205 auto d_curr = arc_lengths[3 * i + 2];
206 auto d_diff = d_curr - d_prev;
207
208 if(std::abs(d_diff) < std::numeric_limits<T>::epsilon()) {
209 _y1.push_back(T(0));
210 _y2.push_back(T(0));
211 } else {
212 auto s1_over_3 = arc_lengths[3 * i];
213 auto s1_over_3_scaled = (s1_over_3 - d_prev) / d_diff;
214
215 auto s2_over_3 = arc_lengths[3 * i + 1];
216 auto s2_over_3_scaled = (s2_over_3 - d_prev) / d_diff;
217
218 auto y1 = (T(18) * s1_over_3_scaled - T(9) * s2_over_3_scaled + T(2)) / T(6);
219 auto y2 = (T(-9) * s1_over_3_scaled + T(18) * s2_over_3_scaled - T(5)) / T(6);
220
221 _y1.push_back(y1);
222 _y2.push_back(y2);
223 }
224 _lengths.push_back(d_curr);
225 }
226 }
227
228 T evaluate(T t) const {
229 if(t <= T(0))
230 return T(0);
231
232 if(t >= T(1))
233 return _total_length;
234
235 T num_segments = static_cast<T>(_y1.size());
236 size_t index = static_cast<size_t>(t * num_segments);
237 auto t_step = T(1) / num_segments;
238 auto t_prev = static_cast<T>(index) * t_step;
239 auto t_curr = static_cast<T>(index + 1) * t_step;
240 auto t_diff = t_curr - t_prev;
241
242 auto x = (t - t_prev) / t_diff;
243 auto y = interpolate_cubic_bezier(T(0), _y1[index], _y2[index], T(1), x);
244
245 auto d_prev = _lengths[index];
246 auto d_curr = _lengths[index + 1];
247 auto d_diff = d_curr - d_prev;
248
249 return static_cast<T>(y * d_diff + d_prev);
250 }
251
252 T total_length() const {
253 return _total_length;
254 }
255
256private:
257 std::vector<T> _y1;
258 std::vector<T> _y2;
259 std::vector<T> _lengths;
260 T _total_length = T(0);
261};
262
265template<typename T>
266class arc_length_parameterization_linear_approximation : public curve_parameterization<arc_length_parameterization_linear_approximation<T>, T> {
267public:
268 arc_length_parameterization_linear_approximation(const arc_length_linear_approximation<T>& arc_length) : _lengths(arc_length.lengths()) {}
269
270 T evaluate(T d) const {
271 if(d <= 0)
272 return T(0);
273
274 auto it = std::lower_bound(_lengths.begin(), _lengths.end(), d);
275 if(it == _lengths.end())
276 return T(1);
277
278 // "it" points to the first length that is larger than d
279 T d_prev = *(it - 1);
280 T d_curr = *it;
281 T x = (d - d_prev) / (d_curr - d_prev);
282
283 T num_segments = static_cast<T>(_lengths.size() - 1);
284 size_t index = std::distance(_lengths.begin(), it);
285 T t_step = T(1) / num_segments;
286 T t_prev = static_cast<T>(index - 1) * t_step;
287 T t_curr = static_cast<T>(index) * t_step;
288 return interpolate_linear(t_prev, t_curr, x);
289 }
290
291 T total_length() const {
292 return _lengths.back();
293 }
294
295private:
296 std::vector<T> _lengths;
297};
298
301template<typename T>
302class arc_length_parameterization_fast_linear_approximation : public curve_parameterization<arc_length_parameterization_fast_linear_approximation<T>, T> {
303public:
305 num_samples = std::max(num_samples, 2);
306 _ts.breakpoints.reserve(num_samples);
307
308 int num_segments = num_samples - 1;
309
311 sequence_transform(std::back_inserter(_ts.breakpoints), [&param](T x) {
312 T d = param.total_length() * x;
313 return param.evaluate(d);
314 }, num_segments + 1);
315
316 _ts.domain = { T(0), param.total_length() };
317 }
318
319 T evaluate(T d) const {
320 return _ts.evaluate(d);
321 }
322
323 T total_length() const {
324 return _ts.domain.upper_bound;
325 }
326
327private:
329};
330
333template<typename T>
334struct arc_length_parameterization_bezier_approximation : public curve_parameterization<arc_length_parameterization_bezier_approximation<T>, T> {
335public:
336 arc_length_parameterization_bezier_approximation(const arc_length_bezier_approximation<T>& arc_length, int depth = 8) : _arc_length(arc_length), _depth(std::max(depth, 1)) {}
337
338 T evaluate(T d) const {
339 T t0 = T(0);
340 T t1 = T(1);
341 for(int i = 0; i < _depth; ++i) {
342 T t = (t0 + t1) / T(2);
343 auto l = _arc_length.evaluate(t);
344 if(d < l)
345 t1 = t;
346 else if(d > l)
347 t0 = t;
348 else
349 break;
350 }
351
352 T l0 = _arc_length.evaluate(t0);
353 T l1 = _arc_length.evaluate(t1);
354 T x = (d - l0) / (l1 - l0);
355
356 return interpolate_linear(t0, t1, x);
357 }
358
359 T total_length() const {
360 return _arc_length.total_length();
361 }
362
363private:
365 int _depth;
366};
367
368} // namespace math
369} // namespace cgv
complete implementation of method actions that only call one method when entering a node
Definition action.h:113
Provide arc length information of a parametric curve using an approximation via multiple cubic bezier...
Provide arc length information of a parametric curve using a piecewise linear approximation.
Provide arc length parameterization of a parametric curve using a piecewise linear approximation of t...
Provide arc length parameterization of a parametric curve using binary search on a piecewise linear a...
A vector with zero based index.
Definition fvec.h:29
std::vector< PointT > sample(size_t num_segments, const curve_parameterization< ParameterizationT< ParamT >, ParamT > &parameterization) const
Sample (aka.
PointT evaluate(ParamT t) const
Evalaute the curve.
std::vector< PointT > sample(size_t num_segments) const
Sample (aka.
CRTP base class for parametric curves.
the cgv namespace
Definition print.h:11
Provide arc length parameterization of a parametric curve using binary search on a bezier approximati...
CRTP base class that provides arc length information for a parametric_curve.
ParamT total_length() const
Return the arc length of the complete curve for t from 0 to 1.
ParamT evaluate(ParamT t) const
Evaluate the arc length of the curve.
CRTP base class that provides arc length parameterization information for a parametric_curve.
ParamT total_length() const
Return the arc length of the complete curve for t from 0 to 1.
ParamT evaluate(ParamT d) const
Evaluate the arc length parameterization of the curve.
Template class representing a piecewise linear function with uniformly spaced breakpoints that maps f...
std::vector< Y > breakpoints
The points that define the piecewise intervals and are assumed to be spaced uniformly over the domain...
interval< X > domain
The input parameter domain.
Y evaluate(X x) const
Return the piecewise linear interpolated value at position x.