OGLplus  (0.59.0) a C++ wrapper for rendering APIs

curve.hpp
Go to the documentation of this file.
1 #ifndef EAGINE_MATH_CURVE_HPP
9 #define EAGINE_MATH_CURVE_HPP
10 
11 #include "../assert.hpp"
12 #include "../integer_range.hpp"
13 #include "../memory/span_algo.hpp"
14 #include "../span.hpp"
15 #include "../types.hpp"
16 #include "../valid_if/positive.hpp"
17 #include "functions.hpp"
18 #include <cmath>
19 #include <vector>
20 
21 namespace eagine::math {
22 
34 template <typename Type, typename Parameter, span_size_t Order>
36 public:
38  static auto are_connected(const std::vector<Type>& points) noexcept
39  -> bool {
40  return ((points.size() - 1) != Order) &&
41  ((points.size() - 1) % Order) == 0;
42  }
43 
45  auto is_connected() const noexcept -> bool {
46  return _connected;
47  }
48 
49  static auto are_separated(const std::vector<Type>& points) noexcept
50  -> bool {
51  return (points.size() % (Order + 1)) == 0;
52  }
53 
55  auto is_separated() const noexcept -> bool {
56  return !_connected;
57  }
58 
60  static auto points_are_ok(const std::vector<Type>& points) noexcept
61  -> bool {
62  return !points.empty() &&
63  (are_connected(points) || are_separated(points));
64  }
65 
73  bezier_curves(std::vector<Type> points)
74  : _points{std::move(points)}
75  , _connected{are_connected(_points)} {
76  EAGINE_ASSERT(points_are_ok(_points));
77  }
78 
86  bezier_curves(std::vector<Type> points, bool connected)
87  : _points{std::move(points)}
88  , _connected{connected} {
89  EAGINE_ASSERT(points_are_ok(_points));
90  EAGINE_ASSERT(are_connected(_points) == _connected);
91  }
92 
100  template <typename P, typename S>
102  : _points(points.begin(), points.end())
103  , _connected{are_connected(_points)} {
104  EAGINE_ASSERT(points_are_ok(_points));
105  }
106 
115  template <typename P, typename S>
117  : _points(points.begin(), points.end())
118  , _connected(connected) {
119  EAGINE_ASSERT(points_are_ok(_points));
120  EAGINE_ASSERT(are_connected(_points) == _connected);
121  }
122 
123  auto segment_step() const noexcept -> span_size_t {
124  EAGINE_ASSERT(points_are_ok(_points));
125  return _connected ? Order : Order + 1;
126  }
127 
129  auto segment_count() const noexcept -> span_size_t {
130  EAGINE_ASSERT(points_are_ok(_points));
131 
132  return _connected ? span_size(_points.size() - 1) / Order
133  : span_size(_points.size()) / (Order + 1);
134  }
135 
137  auto control_points() const noexcept -> const std::vector<Type>& {
138  return _points;
139  }
140 
142  static auto wrap(Parameter t) noexcept -> Parameter {
143  const Parameter zero{0};
144  const Parameter one{1};
145  if(t < zero) {
146  t += std::floor(std::fabs(t)) + one;
147  } else if(t > one) {
148  t -= std::floor(t);
149  }
150  EAGINE_ASSERT(t >= zero && t <= one);
151  return t;
152  }
153 
155  auto position01(Parameter t) const noexcept -> Type {
156  const Parameter zero{0};
157  const Parameter one{1};
158 
159  if(t >= one) {
160  t = zero;
161  }
162  EAGINE_ASSERT(t >= zero && t < one);
163 
164  const auto toffs = t * segment_count();
165  const auto t_sub = toffs - std::floor(toffs);
166  const auto poffs = span_size_t(toffs) * segment_step();
167  EAGINE_ASSERT(poffs < span_size(_points.size()) - Order);
168 
169  return _bezier(t_sub, skip(view(_points), poffs));
170  }
171 
173  auto position(Parameter t) const noexcept -> Type {
174  return position01(wrap(t));
175  }
176 
178  void approximate(std::vector<Type>& dest, valid_if_positive<span_size_t> n)
179  const noexcept {
180  const auto sstep = segment_step();
181  const auto s = segment_count();
182 
183  dest.resize(std_size(s * extract(n) + 1));
184 
185  auto p = dest.begin();
186  const Parameter t_step = Parameter(1) / extract(n);
187 
188  for(auto i : integer_range(s)) {
189  const auto poffs = i * sstep;
190  auto t_sub = Parameter(0);
191  for(auto j : integer_range(extract(n))) {
192  EAGINE_MAYBE_UNUSED(j);
193  EAGINE_ASSERT(p != dest.end());
194  *p = Type(_bezier(t_sub, skip(view(_points), poffs)));
195  ++p;
196 
197  t_sub += t_step;
198  }
199  }
200  EAGINE_ASSERT(p != dest.end());
201  *p = _points.back();
202  ++p;
203  EAGINE_ASSERT(p == dest.end());
204  }
205 
208  -> std::vector<Type> {
209  std::vector<Type> result;
210  approximate(result, n);
211  return result;
212  }
213 
215  auto derivative() const noexcept
216  -> bezier_curves<Type, Parameter, Order - 1> {
217  const auto sstep = segment_step();
218  const auto s = segment_count();
219 
220  std::vector<Type> new_points(std_size(s * Order));
221  auto p = new_points.begin();
222 
223  for(auto i : integer_range(s)) {
224  for(auto j : integer_range(Order)) {
225  const auto k = i * sstep + j;
226  EAGINE_ASSERT(p != new_points.end());
227  *p = (_points[std_size(k + 1)] - _points[std_size(k)]) * Order;
228  ++p;
229  }
230  }
231  EAGINE_ASSERT(p == new_points.end());
232 
233  return {std::move(new_points), false};
234  }
235 
236 private:
237  static_assert(Order > 0);
238 
239  std::vector<Type> _points;
241  bool _connected;
242 };
243 
251 template <typename Type, typename Parameter>
252 class cubic_bezier_loop : public bezier_curves<Type, Parameter, 3> {
253 public:
255  template <typename P, typename S>
258  Parameter r = Parameter(1) / Parameter(3))
259  : bezier_curves<Type, Parameter, 3>(_make_cpoints(points, r)) {}
260 
261 private:
262  template <typename P, typename S>
263  static auto
264  _make_cpoints(memory::basic_span<const Type, P, S> points, Parameter r)
265  -> std::vector<Type> {
266  span_size_t i = 0, n = points.size();
267  EAGINE_ASSERT(n != 0);
268 
269  std::vector<Type> result(std_size(n * 3 + 1));
270  auto ir = result.begin();
271 
272  while(i != n) {
273  const auto a = (n + i - 1) % n;
274  const auto b = i;
275  const auto c = (i + 1) % n;
276  const auto d = (i + 2) % n;
277 
278  EAGINE_ASSERT(ir != result.end());
279  *ir = points[b];
280  ++ir;
281  EAGINE_ASSERT(ir != result.end());
282  *ir = Type(points[b] + (points[c] - points[a]) * r);
283  ++ir;
284  EAGINE_ASSERT(ir != result.end());
285  *ir = Type(points[c] + (points[b] - points[d]) * r);
286  ++ir;
287  ++i;
288  }
289  EAGINE_ASSERT(ir != result.end());
290  *ir = points[0];
291  ++ir;
292  EAGINE_ASSERT(ir == result.end());
293 
294  return result;
295  }
296 };
297 
298 } // namespace eagine::math
299 
300 #endif // EAGINE_MATH_CURVE_HPP
auto control_points() const noexcept -> const std::vector< Type > &
Returns the contol points of the curve.
Definition: curve.hpp:137
auto position01(Parameter t) const noexcept -> Type
Gets the point on the curve at position t (must be in (0.0, 1.0)).
Definition: curve.hpp:155
bezier_curves(std::vector< Type > points)
Creates the bezier curves from the control points.
Definition: curve.hpp:73
std::ptrdiff_t span_size_t
Signed span size type used by eagine.
Definition: types.hpp:36
static constexpr auto span_size(T v) noexcept
Converts argument to span size type.
Definition: types.hpp:59
static constexpr auto extract(api_result_value< Result, api_result_validity::never > &) noexcept -> Result &
Overload of extract for api_result_value.
Definition: c_api_wrap.hpp:270
Primary template for conditionally valid values.
Definition: decl.hpp:49
static constexpr auto view(T *addr, S size) noexcept -> const_span< T >
Creates a view starting at the specified pointer and specified length.
Definition: span.hpp:458
A sequence of Bezier curves, possibly connected at end points.
Definition: curve.hpp:35
cubic_bezier_loop(memory::basic_span< const Type, P, S > points, Parameter r=Parameter(1)/Parameter(3))
Creates a loop passing through the sequence of the input points.
Definition: curve.hpp:256
Basic N-dimensional vector implementation template.
Definition: fwd.hpp:19
auto is_connected() const noexcept -> bool
Returns true if the individual curves are connected.
Definition: curve.hpp:45
bezier_curves(memory::basic_span< const Type, P, S > points, bool connected)
Creates the bezier curves from the control points.
Definition: curve.hpp:116
static constexpr auto std_size(T v) noexcept
Converts argument to std size type.
Definition: types.hpp:52
Non-owning view of a contiguous range of memory with ValueType elements.
Definition: flatten_fwd.hpp:16
auto segment_count() const noexcept -> span_size_t
Returns the count of individual curves in the sequence.
Definition: curve.hpp:129
static constexpr auto skip(basic_span< T, P, S > s, L l) noexcept -> basic_span< T, P, S >
Skips a specified count of elements from the front of a span.
Definition: span_algo.hpp:60
Math-related code is placed in this namespace.
Definition: eagine.hpp:48
static auto wrap(Parameter t) noexcept -> Parameter
Wraps the parameter value to [0.0, 1.0].
Definition: curve.hpp:142
static auto zero(basic_span< T, P, S > spn) -> std::enable_if_t< std::is_integral_v< T >||std::is_floating_point_v< T >, basic_span< T, P, S >>
Fills a span with zero value of type T.
Definition: span_algo.hpp:548
static auto are_connected(const std::vector< Type > &points) noexcept -> bool
Returns whether the curves made from points are connected.
Definition: curve.hpp:38
A closed smooth cubic Bezier spline passing through all input points.
Definition: curve.hpp:252
bezier_curves(memory::basic_span< const Type, P, S > points)
Creates the bezier curves from the control points.
Definition: curve.hpp:101
static auto points_are_ok(const std::vector< Type > &points) noexcept -> bool
checks if the sequence of control points is OK for this curve type.
Definition: curve.hpp:60
auto position(Parameter t) const noexcept -> Type
Gets the point on the curve at position t wrapped to [0.0, 1.0].
Definition: curve.hpp:173
integer_range(B, E) -> integer_range< std::common_type_t< B, E >>
Deduction guide for integer_range.
auto derivative() const noexcept -> bezier_curves< Type, Parameter, Order - 1 >
Returns a derivative of this curve.
Definition: curve.hpp:215
auto approximate(valid_if_positive< span_size_t > n) const -> std::vector< Type >
Returns a sequence of points on the curve (n points per segment).
Definition: curve.hpp:207
bezier_curves(std::vector< Type > points, bool connected)
Creates the bezier curves from the control points.
Definition: curve.hpp:86
auto is_separated() const noexcept -> bool
Returns true if the individual curves are disconnected.
Definition: curve.hpp:55
void approximate(std::vector< Type > &dest, valid_if_positive< span_size_t > n) const noexcept
Makes a sequence of points on the curve (n points per segment).
Definition: curve.hpp:178

Copyright © 2015-2021 Matúš Chochlík.
<chochlik -at -gmail.com>
Documentation generated on Tue Apr 13 2021 by Doxygen (version 1.8.17).