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#include "piecewise_linear_function.h"
7
8namespace cgv {
9namespace math {
10
14template<typename DerivedT, typename ParamT>
16public:
20 ParamT evaluate(ParamT t) const {
21 return static_cast<DerivedT const&>(*this).evaluate(t);
22 }
23
25 ParamT total_length() const {
26 return static_cast<DerivedT const&>(*this).total_length();
27 }
28};
29
33template<typename DerivedT, typename ParamT>
35public:
39 ParamT evaluate(ParamT d) const {
40 return static_cast<DerivedT const&>(*this).evaluate(d);
41 }
42
44 ParamT total_length() const {
45 return static_cast<DerivedT const&>(*this).total_length();
46 }
47};
48
51template<typename... Ts>
53
57template<template<typename> typename DerivedT, typename PointT>
58class parametric_curve<DerivedT<PointT>> {
59public:
60 using point_type = PointT;
61
66 template<typename ParamT = float>
67 PointT evaluate(ParamT t) const {
68 return static_cast<DerivedT<PointT> const&>(*this).evaluate(t);
69 }
70
78 template<typename ParamT = float>
79 std::vector<PointT> sample(size_t num_segments) const {
80 std::vector<PointT> points;
81 points.reserve(num_segments + 1);
82 sample_steps_transform<ParamT>(std::back_inserter(points), [this](ParamT t) { return evaluate(t); }, num_segments);
83 return points;
84 }
85
95 template<template<typename> typename ParameterizationT, typename ParamT = float>
96 std::vector<PointT> sample(size_t num_segments, const curve_parameterization<ParameterizationT<ParamT>, ParamT>& parameterization) const {
97 std::vector<PointT> points;
98 points.reserve(num_segments + 1);
99 sample_steps_transform<ParamT>(std::back_inserter(points), [this, &parameterization](ParamT t) {
100 ParamT d = t * parameterization.total_length();
101 return evaluate(parameterization.evaluate(d));
102 }, num_segments);
103 return points;
104 }
105};
106
110template<template<class> class CurveT, typename T, cgv::type::uint32_type N>
111T arc_length(const parametric_curve<CurveT<fvec<T, N>>>& curve, T t = T(1), int num_segments = 128) {
112 num_segments = std::max(num_segments, 1);
113 T step = T(1) / static_cast<T>(num_segments);
114 T result = T(0);
115
116 fvec<T, N> prev_point = curve.evaluate(T(0));
117 for(int i = 0; i < num_segments; ++i) {
118 ;
119 T t_ = static_cast<T>(i + 1) * step;
120 if(t_ >= t) {
121 fvec<T, N> next_point = curve.evaluate(t);
122 result += length(next_point - prev_point);
123 break;
124 }
125
126 fvec<T, N> next_point = curve.evaluate(t_);
127 result += length(next_point - prev_point);
128 prev_point = next_point;
129 }
130
131 return result;
132}
133
136template<typename T>
137class arc_length_linear_approximation : public curve_arc_length<arc_length_linear_approximation<T>, T> {
138public:
139 template<template<class> class CurveT, cgv::type::uint32_type N>
140 arc_length_linear_approximation(const parametric_curve<CurveT<fvec<T, N>>>& curve, int num_samples = 128) {
141 num_samples = std::max(num_samples, 2);
142 _lengths.values.reserve(num_samples);
143 _lengths.values.push_back(T(0));
144
145 T step = T(1) / static_cast<T>(num_samples - 1);
146
147 fvec<T, N> prev_point = curve.evaluate(T(0));
148 for(int i = 1; i < num_samples; ++i) {
149 T t_ = static_cast<T>(i) * step;
150 fvec<T, N> next_point = curve.evaluate(t_);
151 _lengths.values.push_back(_lengths.values.back() + length(next_point - prev_point));
152 prev_point = next_point;
153 }
154
155 _lengths.domain = { T(0), T(1) };
156 }
157
158 T evaluate(T t) const {
159 return _lengths.evaluate(t);
160 }
161
162 T total_length() const {
163 return _lengths.values.back();
164 }
165
166 const std::vector<T>& lengths() const {
167 return _lengths.values;
168 }
169
170private:
172};
173
176template<typename T>
177class arc_length_bezier_approximation : public curve_arc_length<arc_length_bezier_approximation<T>, T> {
178public:
179 template<template<class> class CurveT, cgv::type::uint32_type N>
180 arc_length_bezier_approximation(const parametric_curve<CurveT<fvec<T, N>>>& curve, int num_segments = 4, int num_segment_subdivisions = 8) {
181 num_segments = std::max(num_segments, 1);
182 num_segment_subdivisions = std::max(num_segment_subdivisions, 1);
183 int num_samples = 3 * num_segments * num_segment_subdivisions;
184
185 std::vector<T> arc_lengths;
186 T t_step = T(1) / static_cast<T>(num_samples);
187 int next_save_index = num_segment_subdivisions - 1;
188
189 fvec<T, N> prev_point = curve.evaluate(T(0));
190 for(int i = 0; i < num_samples; ++i) {
191 T t = static_cast<T>(i + 1) * t_step;
192 fvec<T, N> next_point = curve.evaluate(t);
193 _total_length += length(next_point - prev_point);
194
195 if(i == next_save_index) {
196 arc_lengths.push_back(_total_length);
197 next_save_index += num_segment_subdivisions;
198 }
199
200 prev_point = next_point;
201 }
202
203 _lengths.push_back(T(0));
204
205 for(int i = 0; i < num_segments; ++i) {
206 auto d_prev = _lengths.back();
207 auto d_curr = arc_lengths[3 * i + 2];
208 auto d_diff = d_curr - d_prev;
209
210 if(std::abs(d_diff) < std::numeric_limits<T>::epsilon()) {
211 _y1.push_back(T(0));
212 _y2.push_back(T(0));
213 } else {
214 auto s1_over_3 = arc_lengths[3 * i];
215 auto s1_over_3_scaled = (s1_over_3 - d_prev) / d_diff;
216
217 auto s2_over_3 = arc_lengths[3 * i + 1];
218 auto s2_over_3_scaled = (s2_over_3 - d_prev) / d_diff;
219
220 auto y1 = (T(18) * s1_over_3_scaled - T(9) * s2_over_3_scaled + T(2)) / T(6);
221 auto y2 = (T(-9) * s1_over_3_scaled + T(18) * s2_over_3_scaled - T(5)) / T(6);
222
223 _y1.push_back(y1);
224 _y2.push_back(y2);
225 }
226 _lengths.push_back(d_curr);
227 }
228 }
229
230 T evaluate(T t) const {
231 if(t <= T(0))
232 return T(0);
233
234 if(t >= T(1))
235 return _total_length;
236
237 T num_segments = static_cast<T>(_y1.size());
238 size_t index = static_cast<size_t>(t * num_segments);
239 auto t_step = T(1) / num_segments;
240 auto t_prev = static_cast<T>(index) * t_step;
241 auto t_curr = static_cast<T>(index + 1) * t_step;
242 auto t_diff = t_curr - t_prev;
243
244 auto x = (t - t_prev) / t_diff;
245 auto y = interpolate_cubic_bezier(T(0), _y1[index], _y2[index], T(1), x);
246
247 auto d_prev = _lengths[index];
248 auto d_curr = _lengths[index + 1];
249 auto d_diff = d_curr - d_prev;
250
251 return static_cast<T>(y * d_diff + d_prev);
252 }
253
254 T total_length() const {
255 return _total_length;
256 }
257
258private:
259 std::vector<T> _y1;
260 std::vector<T> _y2;
261 std::vector<T> _lengths;
262 T _total_length = T(0);
263};
264
267template<typename T>
268class arc_length_parameterization_linear_approximation : public curve_parameterization<arc_length_parameterization_linear_approximation<T>, T> {
269public:
270 arc_length_parameterization_linear_approximation(const arc_length_linear_approximation<T>& arc_length) : _lengths(arc_length.lengths()) {}
271
272 T evaluate(T d) const {
273 if(d <= 0)
274 return T(0);
275
276 auto it = std::lower_bound(_lengths.begin(), _lengths.end(), d);
277 if(it == _lengths.end())
278 return T(1);
279
280 // "it" points to the first length that is larger than d
281 T d_prev = *(it - 1);
282 T d_curr = *it;
283 T x = (d - d_prev) / (d_curr - d_prev);
284
285 T num_segments = static_cast<T>(_lengths.size() - 1);
286 size_t index = std::distance(_lengths.begin(), it);
287 T t_step = T(1) / num_segments;
288 T t_prev = static_cast<T>(index - 1) * t_step;
289 T t_curr = static_cast<T>(index) * t_step;
290 return interpolate_linear(t_prev, t_curr, x);
291 }
292
293 T total_length() const {
294 return _lengths.back();
295 }
296
297private:
298 std::vector<T> _lengths;
299};
300
303template<typename T>
304class arc_length_parameterization_fast_linear_approximation : public curve_parameterization<arc_length_parameterization_fast_linear_approximation<T>, T> {
305public:
307 num_samples = std::max(num_samples, 2);
308 _ts.values.reserve(num_samples);
309
310 int num_segments = num_samples - 1;
311
313 sample_steps_transform(std::back_inserter(_ts.values), [&param](T x) {
314 T d = param.total_length() * x;
315 return param.evaluate(d);
316 }, num_segments);
317
318 _ts.domain = { T(0), param.total_length() };
319 }
320
321 T evaluate(T d) const {
322 return _ts.evaluate(d);
323 }
324
325 T total_length() const {
326 return _ts.domain.upper_bound;
327 }
328
329private:
331};
332
335template<typename T>
336struct arc_length_parameterization_bezier_approximation : public curve_parameterization<arc_length_parameterization_bezier_approximation<T>, T> {
337public:
338 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)) {}
339
340 T evaluate(T d) const {
341 T t0 = T(0);
342 T t1 = T(1);
343 for(int i = 0; i < _depth; ++i) {
344 T t = (t0 + t1) / T(2);
345 auto l = _arc_length.evaluate(t);
346 if(d < l)
347 t1 = t;
348 else if(d > l)
349 t0 = t;
350 else
351 break;
352 }
353
354 T l0 = _arc_length.evaluate(t0);
355 T l1 = _arc_length.evaluate(t1);
356 T x = (d - l0) / (l1 - l0);
357
358 return interpolate_linear(t0, t1, x);
359 }
360
361 T total_length() const {
362 return _arc_length.total_length();
363 }
364
365private:
367 int _depth;
368};
369
370} // namespace math
371} // 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:27
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 regularly (equidistant breakpoints) sampled piecewise linear function/a...