Euphoria
polybezier.cc
Go to the documentation of this file.
1 #include "core/polybezier.h"
2 
3 #include <type_traits>
4 
5 #include "base/range.h"
6 #include "log/log.h"
7 #include "base/cint.h"
8 
9 namespace eu::core { namespace
10 {
11  constexpr size_t indices_between_anchor_points = 3;
12  constexpr size_t indices_between_anchor_pointsi = 3;
13 
14  bool
15  is_anchor_point(size_t i)
16  {
17  return i % indices_between_anchor_points == 0;
18  }
19 
20  bool
21  is_control_point(size_t i)
22  {
23  return !is_anchor_point(i);
24  }
25 
26  template<typename T, bool b> struct Const { using Type = T; };
27  template<typename T> struct Const<T, true> { using Type = std::add_const_t<T>; };
28 
29  template <typename TVec> TVec get_zero_value();
30  template<> vec2f constexpr get_zero_value<vec2f>()
31  {
32  return zero2f;
33  }
34 
35  template<typename Vec, typename Segment, typename Unit, bool is_const>
36  struct PolyBezierWrapper
37  {
38  using TPoints = typename Const<std::vector<Vec>, is_const>::Type;
39  using TBool = typename Const<bool, is_const>::Type;
40 
41  TPoints* points;
42  TBool* is_closed;
44 
45  PolyBezierWrapper
46  (
47  TPoints* arg_points,
48  TBool* arg_is_closed,
49  TBool* arg_is_autoset_enabled
50  )
51  : points(arg_points)
52  , is_closed(arg_is_closed)
53  , is_autoset_enabled(arg_is_autoset_enabled)
54  {
55  }
56 
57  Vec& point_at(int i) { return (*points)[i]; }
58  [[nodiscard]] const Vec& point_at(int i) const { return (*points)[c_sizet_to_int(i)]; }
59  [[nodiscard]] int num_points() const { return c_sizet_to_int(points->size()); }
60 
61  void reset(const Vec& left, const Vec& right, const Vec& up, const Vec& down, const Vec& center, float scale)
62  {
63  *is_autoset_enabled = false;
64  *is_closed = false;
65  points->clear();
66  points->push_back(center + left * scale);
67  points->push_back(center + (left + up) * 0.5f * scale);
68  points->push_back(center + (right + down) * 0.5f * scale);
69  points->push_back(center + right * scale);
70  }
71 
72  void add_point(const Vec& p)
73  {
74  const auto p2 = point_at(num_points() - 2); // control point
75  const auto p3 = point_at(num_points() - 1); // anchor point
76 
77  const auto p4 = p3 + Vec::from_to(p2, p3);
78  const auto p6 = p;
79  const auto p5 = p4 + Vec::from_to(p4, p6);
80 
81  points->push_back(p4);
82  points->push_back(p5);
83  points->push_back(p6);
84 
86  {
87  auto_set_affected_control_points(num_points() - 1);
88  }
89  }
90 
91  void move_point(int i, const Vec& delta)
92  {
93  if(*is_autoset_enabled && is_control_point(i))
94  {
95  // that point is on is_autoset_enabled
96  return;
97  }
98 
99  const auto r = make_range(*points);
100  point_at(i) += delta;
101 
102  if(*is_autoset_enabled)
103  {
104  auto_set_affected_control_points(i);
105  return;
106  }
107 
108  if(is_anchor_point(i))
109  {
110  // anchor point, move control points too
111  if(*is_closed || is_within(r, i + 1))
112  {
113  point_at(loop_index(i + 1)) += delta;
114  }
115  if(*is_closed || is_within(r, i - 1))
116  {
117  point_at(loop_index(i - 1)) += delta;
118  }
119  }
120  else
121  {
122  // point is control point, move the opposite point
123  const int corresponding_control_index = is_anchor_point(i + 1) ? i + 2 : i - 2;
124  const int anchor_index = is_anchor_point(i + 1) ? i + 1 : i - 1;
125  if(*is_closed || is_within(r, corresponding_control_index))
126  {
127  const auto cci = loop_index(corresponding_control_index);
128  const auto ai = loop_index(anchor_index);
129  const auto distance = Vec::from_to(point_at(cci), point_at(ai)).get_length();
130  const auto direction = Vec::from_to(point_at(i), point_at(ai)).get_normalized();
131  point_at(cci) = point_at(ai) + distance * direction;
132  }
133  }
134  }
135 
136  [[nodiscard]] Segment
137  get_segment(int i) const
138  {
139  const auto b = i * 3;
140  return
141  {
142  point_at(b + 0), // anchor
143  point_at(b + 1), // ^ control
144  point_at(loop_index(b+indices_between_anchor_pointsi)), // anchor
145  point_at(b + 2) // ^ control
146  };
147  }
148 
149  [[nodiscard]] int
150  get_number_of_segments() const
151  {
152  return num_points() / indices_between_anchor_points;
153  }
154 
155  void
156  set_closed(bool new_is_closed)
157  {
158  if(*is_closed == new_is_closed)
159  {
160  return;
161  }
162 
163  *is_closed = new_is_closed;
164 
165  if(*is_closed)
166  {
167  // anchor control anchor (again)
168  const auto d = Vec::from_to(point_at(num_points() - 2), point_at(num_points() - 1));
169  const auto p1 = point_at(num_points() - 1) + d;
170  const auto p2 = point_at(0) + Vec::from_to(point_at(1), point_at(0));
171  points->push_back(p1);
172  points->push_back(p2);
173 
174  if(*is_autoset_enabled)
175  {
176  auto_set_anchor_control_points(0);
177  auto_set_anchor_control_points(num_points() - indices_between_anchor_pointsi);
178  }
179  }
180  else
181  {
182  points->pop_back();
183  points->pop_back();
184 
185  if(*is_autoset_enabled)
186  {
187  auto_set_start_and_end_control_points();
188  }
189  }
190  }
191 
192  void
193  toggle_closed()
194  {
195  set_closed(!*is_closed);
196  }
197 
198 
199  void
200  set_auto_set_control_points(bool is_autoset)
201  {
202  if(is_autoset == *is_autoset_enabled)
203  {
204  return;
205  }
206 
207  *is_autoset_enabled = is_autoset;
208 
209  if(*is_autoset_enabled)
210  {
211  auto_set_all_control_points();
212  }
213  }
214 
215  void
216  toggle_auto_set_control_points()
217  {
218  set_auto_set_control_points(!*is_autoset_enabled);
219  }
220 
221 
222  [[nodiscard]] int
223  loop_index(int i) const
224  {
225  const auto s = num_points();
226  return (s + i) % s;
227  }
228 
229  void
230  auto_set_affected_control_points(int updated_anchor_index)
231  {
232  const auto points_range = make_range(*points);
233 
234  const auto update_anchor_index = [&](int anchor_index)
235  {
236  if(*is_closed || is_within(points_range, anchor_index))
237  {
238  auto_set_anchor_control_points(loop_index(anchor_index));
239  }
240  };
241 
242  update_anchor_index(updated_anchor_index - indices_between_anchor_pointsi);
243  update_anchor_index(updated_anchor_index);
244  update_anchor_index(updated_anchor_index + indices_between_anchor_pointsi);
245 
246  // might be affected...
247  auto_set_start_and_end_control_points();
248  }
249 
250  void
251  auto_set_all_control_points()
252  {
253  for(int point_index = 0; point_index < num_points(); point_index += indices_between_anchor_pointsi)
254  {
255  auto_set_anchor_control_points(point_index);
256  }
257  auto_set_start_and_end_control_points();
258  }
259 
260  void
261  auto_set_start_and_end_control_points()
262  {
263  if(*is_closed)
264  {
265  return;
266  }
267 
268  const auto set_index = [&](int r, int a, int b)
269  {
270  point_at(loop_index(r)) = (point_at(loop_index(a)) + point_at(loop_index(b))) * 0.5f;
271  };
272  set_index( 1, 0, 2);
273  set_index(-2, -1, -3);
274  }
275 
276  void
277  auto_set_anchor_control_points(int anchor_index)
278  {
279  const auto points_range = make_range(*points);
280  const auto anchor_pos = point_at(anchor_index);
281 
282  const auto get_distance_and_dir = [&](int index) -> std::pair<float, Vec>
283  {
284  if(*is_closed || is_within(points_range, index))
285  {
286  const auto ft = Vec::from_to(anchor_pos, point_at(loop_index(index)));
287  const auto offset = ft.get_normalized_and_length();
288  const auto dir = offset.normalized.to_vec();
289  return {offset.length, dir};
290  }
291  else
292  {
293  return {0.0f, get_zero_value<Vec>()};
294  }
295  };
296  const auto set_control_point = [&](int control_index, float distance, const Unit& dir)
297  {
298  if(*is_closed || is_within(points_range, control_index))
299  {
300  point_at(loop_index(control_index)) = anchor_pos + dir * distance * 0.5f;
301  }
302  };
303 
304  const auto [distance_from_anchor_to_fst_neighbour, anchor_to_fst_neighbour] = get_distance_and_dir(anchor_index - indices_between_anchor_pointsi);
305  const auto [distance_from_anchor_to_sec_neighbour, anchor_to_sec_neighbour] = get_distance_and_dir(anchor_index + indices_between_anchor_pointsi);
306  const auto dir = Vec::from_to(anchor_to_sec_neighbour, anchor_to_fst_neighbour).get_normalized();
307 
308  set_control_point(anchor_index - 1, distance_from_anchor_to_fst_neighbour, dir);
309  set_control_point(anchor_index + 1, -distance_from_anchor_to_sec_neighbour, dir);
310  }
311  };
312 
313  PolyBezierWrapper<vec2f, BezierSegment2, unit2f, false> make_wrapper(eu::core::PolyBezier2* self)
314  {
315  return
316  {
317  &self->points,
318  &self->is_closed,
319  &self->is_autoset_enabled
320  };
321  }
322 
323  PolyBezierWrapper<vec2f, BezierSegment2, unit2f, true> make_wrapper(const eu::core::PolyBezier2* self)
324  {
325  return
326  {
327  &self->points,
328  &self->is_closed,
329  &self->is_autoset_enabled
330  };
331  }
332 }}
333 
334 
335 namespace eu::core
336 {
338  {
339  const auto left = vec2f(-1, 0);
340  const auto right = vec2f(1, 0);
341  const auto up = vec2f(0, 1);
342  const auto down = vec2f(0, -1);
343 
344  make_wrapper(this).reset(left, right, up, down, center, 50.0f);
345  }
346 
347  [[nodiscard]] StepIteratorCreator<size_t>
349  {
350  return iterate<std::size_t>(0, points.size());
351  }
352 
353  void
355  {
356  make_wrapper(this).add_point(p);
357  }
358 
359  bool
361  {
362  return core::is_anchor_point(i);
363  }
364 
365  bool
367  {
368  return core::is_control_point(i);
369  }
370 
371 
372  void
373  PolyBezier2::move_point(int i, const vec2f& delta)
374  {
375  make_wrapper(this).move_point(i, delta);
376  }
377 
378  [[nodiscard]] StepIteratorCreator<int>
380  {
381  return iterate(0, make_wrapper(this).get_number_of_segments());
382  }
383 
386  {
387  return make_wrapper(this).get_segment(i);
388  }
389 
390  void
391  PolyBezier2::set_closed(bool new_is_closed)
392  {
393  make_wrapper(this).set_closed(new_is_closed);
394  }
395 
396  void
398  {
399  make_wrapper(this).toggle_closed();
400  }
401 
402 
403  void
405  {
406  make_wrapper(this).set_auto_set_control_points(is_autoset);
407  }
408 
409  void
411  {
412  make_wrapper(this).toggle_auto_set_control_points();
413  }
414 }
StepIteratorCreator< T > iterate(const T from, T to, T step=1)
Definition: iterate.h:73
constexpr vec2f zero2f
Definition: vec2.h:68
int c_sizet_to_int(size_t t)
Definition: cint.cc:11
Range< T > make_range(T min, T max)
Definition: range.h:39
bool is_within(const Range< T > &range, T value)
Definition: range.h:108
TBool * is_closed
Definition: polybezier.cc:42
TBool * is_autoset_enabled
Definition: polybezier.cc:43
TPoints * points
Definition: polybezier.cc:41
Anchor and control points.
Definition: polybezier.h:34
Composite Bézier curve or polybezier.
Definition: polybezier.h:45
BezierSegment2 get_segment(int i) const
Definition: polybezier.cc:385
StepIteratorCreator< size_t > iterate_points() const
Definition: polybezier.cc:348
static bool is_anchor_point(size_t i)
Definition: polybezier.cc:360
void set_closed(bool is_closed)
Definition: polybezier.cc:391
static bool is_control_point(size_t i)
Definition: polybezier.cc:366
void set_auto_set_control_points(bool is_autoset)
Definition: polybezier.cc:404
void add_point(const vec2f &p)
Definition: polybezier.cc:354
StepIteratorCreator< int > iterate_segments() const
Definition: polybezier.cc:379
std::vector< vec2f > points
Definition: polybezier.h:46
void move_point(int i, const vec2f &delta)
Definition: polybezier.cc:373
void toggle_auto_set_control_points()
Definition: polybezier.cc:410
PolyBezier2(const vec2f &center)
Definition: polybezier.cc:337
Definition: vec2.h:33