Blender V5.0
convexhull_2d.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 * SPDX-FileCopyrightText: 2001 softSurfer (http://www.softsurfer.com)
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later */
5
9
10#include <algorithm>
11#include <cstdlib>
12#include <cstring>
13
14#include "MEM_guardedalloc.h"
15
16#include "BLI_bounds_types.hh"
17#include "BLI_convexhull_2d.hh"
18#include "BLI_math_vector.h"
19#include "BLI_math_vector.hh"
21#include "BLI_utildefines.h"
22
23#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
24
41#define USE_CONVEX_CROSS_PRODUCT_ENSURE
42
47// #define USE_BRUTE_FORCE_ASSERT
48
54#define USE_ANGLE_ITER_ORDER_ASSERT
55
56using blender::float2;
57
58/* -------------------------------------------------------------------- */
61
62static float sincos_rotate_cw_x(const float2 &sincos, const float2 &p)
63{
64 return (sincos[0] * p[0]) + (sincos[1] * p[1]);
65}
66
67static float sincos_rotate_cw_y(const float2 &sincos, const float2 &p)
68{
69 return (sincos[1] * p[0]) - (sincos[0] * p[1]);
70}
71
73
74/* -------------------------------------------------------------------- */
77
78/* Copyright 2001, softSurfer (http://www.softsurfer.com)
79 * This code may be freely used and modified for any purpose
80 * providing that this copyright notice is included with it.
81 * SoftSurfer makes no warranty for this code, and cannot be held
82 * liable for any real or imagined damage resulting from its use.
83 * Users of this code must verify correctness for their application.
84 * http://softsurfer.com/Archive/algorithm_0203/algorithm_0203.htm
85 *
86 * NOTE(@ideasman42): additional checks added to ensure the resulting hull is convex,
87 * see the: #USE_CONVEX_CROSS_PRODUCT_ENSURE define.
88 */
89
106static float is_left(const float2 &p0, const float2 &p1, const float2 &p2)
107{
108 return (((p1[0] - p0[0]) * (p2[1] - p0[1])) - ((p2[0] - p0[0]) * (p1[1] - p0[1])));
109}
110
114static void convexhull_2d_stack_finalize(const float2 *points,
115 const int r_points[],
116 blender::int2 &r_points_range)
117{
118#ifdef USE_CONVEX_CROSS_PRODUCT_ENSURE
119 int bot = r_points_range[0];
120 int top = r_points_range[1];
121 while (top - bot >= 2) {
122 /* While the order of checking the beginning/end doesn't matter,
123 * prefer dropping values off the end of `r_points`
124 * since it doesn't require re-ordering. */
125
126 /* See #is_left note on argument order. */
127 if (UNLIKELY(is_left(
128 /* Second last point. */
129 points[r_points[top - 1]],
130 /* First point. */
131 points[r_points[bot]],
132 /* Last point (candidate "ear" to remove). */
133 points[r_points[top]]) >= 0.0f))
134 {
135 /* Concave: drop from the end: `r_points[top]`. */
136 top--;
137 continue;
138 }
139 /* See #is_left note on argument order. */
140 if (UNLIKELY(is_left(
141 /* Last point. */
142 points[r_points[top]],
143 /* Second point. */
144 points[r_points[bot + 1]],
145 /* First point (candidate "ear" to remove). */
146 points[r_points[bot]]) >= 0.0f))
147 {
148 /* Concave: drop from the front: `r_points[bot]`. */
149 bot++;
150 continue;
151 }
152 break;
153 }
154 r_points_range[0] = bot;
155 r_points_range[1] = top;
156#else
157 UNUSED_VARS(points, r_points, r_points_range);
158#endif /* !USE_CONVEX_CROSS_PRODUCT_ENSURE */
159}
160
165static inline void convexhull_2d_stack_push(const float2 *points,
166 int r_points[],
167 int &top,
168 int index)
169{
170#ifdef USE_CONVEX_CROSS_PRODUCT_ENSURE
171 while (
172 UNLIKELY((top >= 2) &&
173 /* See #is_left note on argument order. */
174 (is_left(points[r_points[top - 1]], points[index], points[r_points[top]]) >= 0.0f)))
175 {
176 top--;
177 }
178#else
179 UNUSED_VARS(points);
180#endif /* !USE_CONVEX_CROSS_PRODUCT_ENSURE */
181
182 r_points[++top] = index;
183}
184
188static int convexhull_2d_sorted_impl(const float2 *points, const int points_num, int r_points[])
189{
190 BLI_assert(points_num >= 2); /* Doesn't handle trivial cases. */
191 /* The output array `r_points[]` will be used as the stack. */
192 int bot = 0;
193 /* Indices for bottom and top of the stack. */
194 int top = -1;
195 /* Array scan index. */
196 int i;
197
198 const int minmin = 0;
199 const int maxmax = points_num - 1;
200 int minmax;
201 int maxmin;
202
203 float xmax;
204
205 /* Get the indices of points with min X-coord and min|max Y-coord. */
206 float xmin = points[0][0];
207 for (i = 1; i <= maxmax; i++) {
208 if (points[i][0] != xmin) {
209 break;
210 }
211 }
212
213 minmax = i - 1;
214 if (minmax == maxmax) { /* Degenerate case: all x-coords == X-min. */
215 convexhull_2d_stack_push(points, r_points, top, minmin);
216 if (points[minmax][1] != points[minmin][1]) {
217 /* A nontrivial segment. */
218 convexhull_2d_stack_push(points, r_points, top, minmax);
219 }
220 BLI_assert(top + 1 <= points_num);
221 return top;
222 }
223
224 /* Get the indices of points with max X-coord and min|max Y-coord. */
225
226 xmax = points[maxmax][0];
227 for (i = maxmax - 1; i >= 0; i--) {
228 if (points[i][0] != xmax) {
229 break;
230 }
231 }
232 maxmin = i + 1;
233
234 /* Compute the lower hull on the stack `r_points`. */
235 BLI_assert(top < 2);
236 convexhull_2d_stack_push(points, r_points, top, minmin);
237
238 i = minmax;
239 while (++i <= maxmin) {
240 /* The lower line joins `points[minmin]` with `points[maxmin]`. */
241 if ((i < maxmin) && (is_left(points[minmin], points[maxmin], points[i]) >= 0.0f)) {
242 continue; /* Ignore `points[i]` above or on the lower line. */
243 }
244
245 while (top > 0) { /* There are at least 2 points on the stack. */
246 /* Test if `points[i]` is left of the line at the stack top. */
247 if (is_left(points[r_points[top - 1]], points[r_points[top]], points[i]) > 0.0f) {
248 break; /* `points[i]` is a new hull vertex. */
249 }
250 top--; /* Pop top point off stack. */
251 }
252 convexhull_2d_stack_push(points, r_points, top, i);
253 }
254
255 /* Next, compute the upper hull on the stack `r_points` above the bottom hull. */
256 if (maxmax != maxmin) { /* If distinct `xmax` points. */
257 convexhull_2d_stack_push(points, r_points, top, maxmax);
258 }
259
260 bot = top; /* The bottom point of the upper hull stack. */
261 i = maxmin;
262 while (--i >= minmax) {
263 /* The upper line joins `points[maxmax]` with `points[minmax]`. */
264 if ((i > minmax) && (is_left(points[maxmax], points[minmax], points[i]) >= 0.0f)) {
265 continue; /* Ignore points[i] below or on the upper line. */
266 }
267
268 while (top > bot) { /* At least 2 points on the upper stack. */
269 /* Test if `points[i]` is left of the line at the stack top. */
270 if (is_left(points[r_points[top - 1]], points[r_points[top]], points[i]) > 0.0f) {
271 break; /* `points[i]` is a new hull vertex. */
272 }
273 top--; /* Pop top point off stack. */
274 }
275
276 if (points[i][0] == points[r_points[0]][0] && points[i][1] == points[r_points[0]][1]) {
277 BLI_assert(top + 1 <= points_num);
278 return top; /* Special case (mgomes). */
279 }
280 convexhull_2d_stack_push(points, r_points, top, i);
281 }
282
283 if (minmax != minmin && r_points[0] != minmin) {
284 /* Push joining endpoint onto stack. */
285 convexhull_2d_stack_push(points, r_points, top, minmin);
286 }
287
288 BLI_assert(top + 1 <= points_num);
289 return top;
290}
291
300 const int points_num,
301 int r_points[])
302{
303 const int top = convexhull_2d_sorted_impl(points, points_num, r_points);
304 blender::int2 points_range = {0, top};
305 convexhull_2d_stack_finalize(points, r_points, points_range);
306 return points_range;
307}
308
309int BLI_convexhull_2d(blender::Span<float2> points, int r_points[])
310{
311 const int points_num = int(points.size());
312 BLI_assert(points_num >= 0);
313 if (points_num < 2) {
314 if (points_num == 1) {
315 r_points[0] = 0;
316 }
317 return points_num;
318 }
319 int *points_map = MEM_malloc_arrayN<int>(size_t(points_num), __func__);
320 float2 *points_sort = MEM_malloc_arrayN<float2>(size_t(points_num), __func__);
321
322 for (int i = 0; i < points_num; i++) {
323 points_map[i] = i;
324 }
325
326 /* Sort the points by X, then by Y. */
327 std::sort(points_map, points_map + points_num, [points](const int &a_index, const int &b_index) {
328 const float *a = points[a_index];
329 const float *b = points[b_index];
330 if (a[1] > b[1]) {
331 return false;
332 }
333 if (a[1] < b[1]) {
334 return true;
335 }
336
337 if (a[0] > b[0]) {
338 return false;
339 }
340 if (a[0] < b[0]) {
341 return true;
342 }
343 return false;
344 });
345
346 for (int i = 0; i < points_num; i++) {
347 copy_v2_v2(points_sort[i], points[points_map[i]]);
348 }
349
350 const blender::int2 points_hull_range = convexhull_2d_sorted(points_sort, points_num, r_points);
351
352 /* Map back to the unsorted index values. */
353 for (int i = points_hull_range[0]; i <= points_hull_range[1]; i++) {
354 r_points[i] = points_map[r_points[i]];
355 }
356
357 MEM_freeN(points_map);
358 MEM_freeN(points_sort);
359
360 const int points_hull_num = (points_hull_range[1] - points_hull_range[0]) + 1;
361 BLI_assert(points_hull_num <= points_num);
362 return points_hull_num;
363}
364
366
367/* -------------------------------------------------------------------- */
370
371#if defined(USE_BRUTE_FORCE_ASSERT) && !defined(NDEBUG)
372static float2 convexhull_aabb_fit_hull_2d_brute_force(const float (*points_hull)[2],
373 int points_hull_num)
374{
375 float area_best = std::numeric_limits<float>::max();
376 float2 sincos_best = {0.0f, 1.0f}; /* Track the best angle as a unit vector, delaying `atan2`. */
377
378 for (int i = 0; i < points_hull_num; i++) {
379 const int i_next = (i + 1) % points_hull_num;
380 /* 2D rotation matrix. */
381 float dvec_length = 0.0f;
383 float2(points_hull[i_next]) - float2(points_hull[i]), dvec_length);
384 if (UNLIKELY(dvec_length == 0.0f)) {
385 continue;
386 }
387
389 {std::numeric_limits<float>::max(), -std::numeric_limits<float>::max()},
390 {std::numeric_limits<float>::max(), -std::numeric_limits<float>::max()},
391 };
392 float area_test;
393
394 for (int j = 0; j < points_hull_num; j++) {
395 const float2 tvec = {
396 sincos_rotate_cw_x(sincos, points_hull[j]),
397 sincos_rotate_cw_y(sincos, points_hull[j]),
398 };
399
400 bounds[0].min = blender::math::min(bounds[0].min, tvec[0]);
401 bounds[0].max = blender::math::max(bounds[0].max, tvec[0]);
402 bounds[1].min = blender::math::min(bounds[1].min, tvec[1]);
403 bounds[1].max = blender::math::max(bounds[1].max, tvec[1]);
404
405 area_test = (bounds[0].max - bounds[0].min) * (bounds[1].max - bounds[1].min);
406 if (area_test > area_best) {
407 break;
408 }
409 }
410
411 if (area_test < area_best) {
412 area_best = area_test;
413 sincos_best = sincos;
414 }
415 }
416
417 return sincos_best;
418}
419#endif
420
422
423/* -------------------------------------------------------------------- */
429
434static float2 sincos_canonical(const float2 &sincos)
435{
436
437 /* Normalize doesn't ensure a `sin` / `cos` of 1.0/-1.0 ensures the other value is zero.
438 * Without the check for both 0.0 and 1.0, iteration may not be ordered. */
440 if (sincos[0] < 0.0f) {
441 if (sincos[1] < 0.0f) {
442 result[0] = -sincos[0];
443 result[1] = -sincos[1];
444 }
445 else if ((sincos[0] == -1.0f) && (sincos[1] == 0.0f)) {
446 result[0] = -sincos[0];
447 result[1] = sincos[1];
448 }
449 else {
450 result[0] = sincos[1];
451 result[1] = -sincos[0];
452 }
453 }
454 else {
455 if (sincos[1] < 0.0f) {
456 result[0] = -sincos[1];
457 result[1] = sincos[0];
458 }
459 else if ((sincos[0] == 0.0f) && (sincos[1] == 1.0f)) {
460 result[0] = sincos[1];
461 result[1] = sincos[0];
462 }
463 else {
464 result = sincos;
465 }
466 }
467
468 /* The range is [1.0, 0.0], it will approach but never return [0.0, 1.0],
469 * as the canonical version of this value gets flipped to [1.0, 0.0]. */
470 BLI_assert(result[0] > 0.0f);
471 BLI_assert(result[1] >= 0.0f);
472 return result;
473}
474
486
488{
489 if (a.sincos_canonical[0] < b.sincos_canonical[0]) {
490 return -1;
491 }
492 if (a.sincos_canonical[0] > b.sincos_canonical[0]) {
493 return 1;
494 }
495 /* Flipped intentionally. */
496 if (a.sincos_canonical[1] > b.sincos_canonical[1]) {
497 return -1;
498 }
499 if (a.sincos_canonical[1] < b.sincos_canonical[1]) {
500 return 1;
501 }
502
503 /* Flipped intentionally. */
504 if (a.index > b.index) {
505 return -1;
506 }
507 if (a.index < b.index) {
508 return 1;
509 }
510 return 0;
511}
512
528
541
543{
544 HullAngleStep **prev_p = &hiter.axis_ordered;
545 HullAngleStep *iter = hiter.axis_ordered;
546 while (iter && hull_angle_canonical_cmp(iter->angle, insert->angle) > 0) {
547 prev_p = &iter->next;
548 iter = iter->next;
549 }
550 *prev_p = insert;
551 insert->next = iter;
552}
553
555{
556 BLI_assert(hstep.index != -1);
557 while (hstep.index != hstep.index_max) {
558 const int i_curr = hstep.index;
559 const int i_next = (hstep.index + 1) % hiter.points_hull_num;
560 const float2 dir = float2(hiter.points_hull[i_next]) - float2(hiter.points_hull[i_curr]);
561 float dir_length = 0.0f;
562 const float2 sincos_test = blender::math::normalize_and_get_length(dir, dir_length);
563 hstep.index = i_next;
564 if (LIKELY(dir_length != 0.0f)) {
565 hstep.angle.sincos = sincos_test;
566 hstep.angle.sincos_canonical = sincos_canonical(sincos_test);
567 hstep.angle.index = i_curr;
568 return true;
569 }
570 }
571
572 /* Reached the end, signal this axis shouldn't be stepped over. */
573 hstep.index = -1;
574 return false;
575}
576
577static HullAngleIter convexhull_2d_angle_iter_init(const float (*points_hull)[2],
578 const int points_hull_num)
579{
580 const int points_hull_num_minus_1 = points_hull_num - 1;
581 HullAngleIter hiter = {};
582 /* Aligned with `hiter.axis`. */
583 float range[2][2];
584 /* Initialize min-max range from the first point. */
585 for (int axis = 0; axis < 2; axis++) {
586 range[axis][0] = points_hull[0][axis];
587 range[axis][1] = points_hull[0][axis];
588 }
589 /* Expand from all other points.
590 *
591 * NOTE: Don't attempt to pick either side when there are multiple equal points.
592 * Walking backwards while checking `sincos_canonical` handles that. */
593 for (int i = 1; i < points_hull_num; i++) {
594 const float *p = points_hull[i];
595 for (int axis = 0; axis < 2; axis++) {
596 if (range[axis][0] < p[axis]) {
597 range[axis][0] = p[axis];
598 hiter.axis[axis][0].index = i;
599 }
600 if (range[axis][1] > p[axis]) {
601 range[axis][1] = p[axis];
602 hiter.axis[axis][1].index = i;
603 }
604 }
605 }
606
607 /* Step backwards, compute the actual `sincos_canonical` because it's possible
608 * an edge which is not *exactly* axis aligned normalizes to a value which is.
609 * Instead of attempting to guess when this might happen,
610 * simply calculate the value and walk backwards for a long as the canonical angle
611 * has a `sin` of 1.0 (which must always come first). */
612 for (int axis = 0; axis < 2; axis++) {
613 for (int i = 0; i < 2; i++) {
614 const int i_orig = hiter.axis[axis][i].index;
615 int i_curr = i_orig, i_prev;
616 /* Prevent an eternal loop (incredibly unlikely).
617 * In virtually all cases this will step back once
618 * (in the case of an axis-aligned edge) or not at all. */
619 while ((i_prev = (i_curr + points_hull_num_minus_1) % points_hull_num) != i_orig) {
620 float dir_length = 0.0f;
622 float2(points_hull[i_curr]) - float2(points_hull[i_prev]), dir_length);
623 if (LIKELY(dir_length != 0.0f)) {
624 /* Account for 90 degree corners that may also have an axis-aligned canonical angle. */
625 if (blender::math::abs(sincos_test[axis]) > 0.5f) {
626 break;
627 }
628 const float2 sincos_test_canonical = sincos_canonical(sincos_test);
629 if (LIKELY(sincos_test_canonical[0] != 1.0f)) {
630 break;
631 }
632 }
633 i_curr = i_prev;
634 hiter.axis[axis][i].index = i_curr;
635 }
636 }
637 }
638
639 /* Setup counter-clockwise limits. */
640 hiter.axis[0][0].index_max = hiter.axis[1][0].index; /* West to south. */
641 hiter.axis[1][0].index_max = hiter.axis[0][1].index; /* South to east. */
642 hiter.axis[0][1].index_max = hiter.axis[1][1].index; /* East to north. */
643 hiter.axis[1][1].index_max = hiter.axis[0][0].index; /* North to west. */
644
645 hiter.points_hull = points_hull;
646 hiter.points_hull_num = points_hull_num;
647
648 for (int axis = 0; axis < 2; axis++) {
649 for (int i = 0; i < 2; i++) {
650 hiter.axis[axis][i].angle.index = hiter.axis[axis][i].index;
651 if (convexhull_2d_angle_iter_step_on_axis(hiter, hiter.axis[axis][i])) {
652 hull_angle_insert_ordered(hiter, &hiter.axis[axis][i]);
653 }
654 }
655 }
656
657 return hiter;
658}
659
661{
662 HullAngleStep *hstep = hiter.axis_ordered;
663#ifdef USE_ANGLE_ITER_ORDER_ASSERT
664 const AngleCanonical angle_prev = hstep->angle;
665#endif
666
667 hiter.axis_ordered = hiter.axis_ordered->next;
668 if (convexhull_2d_angle_iter_step_on_axis(hiter, *hstep)) {
669 hull_angle_insert_ordered(hiter, hstep);
670 }
671
672#ifdef USE_ANGLE_ITER_ORDER_ASSERT
673 if (hiter.axis_ordered) {
674 hstep = hiter.axis_ordered;
675 BLI_assert(hull_angle_canonical_cmp(angle_prev, hiter.axis_ordered->angle) > 0);
676 UNUSED_VARS_NDEBUG(angle_prev);
677 }
678#endif
679}
680
682
683/* -------------------------------------------------------------------- */
686
692template<int Axis, int AxisSign>
693static float convexhull_2d_compute_extent_on_axis(const float (*points_hull)[2],
694 const int points_hull_num,
695 const float2 &sincos,
696 int *index_p)
697{
698 /* NOTE(@ideasman42): Use a forward search instead of attempting a search strategy
699 * computing upper & lower bounds (similar to a binary search). The rotating calipers
700 * are ensured to test ordered rotations between 0-90 degrees, meaning any cases where
701 * this function needs to step over many points will be limited to a small number of cases.
702 * Since scanning forward isn't expensive it shouldn't pose a problem. */
703 BLI_assert(*index_p >= 0);
704 const int index_init = *index_p;
705 int index_best = index_init;
706 float value_init = (Axis == 0) ? sincos_rotate_cw_x(sincos, points_hull[index_best]) :
707 sincos_rotate_cw_y(sincos, points_hull[index_best]);
708 float value_best = value_init;
709 /* Simply scan up the array. */
710 for (int count = 1; count < points_hull_num; count++) {
711 const int index_test = (index_init + count) % points_hull_num;
712 const float value_test = (Axis == 0) ? sincos_rotate_cw_x(sincos, points_hull[index_test]) :
713 sincos_rotate_cw_y(sincos, points_hull[index_test]);
714 if ((AxisSign == -1) ? (value_test > value_best) : (value_test < value_best)) {
715 break;
716 }
717 value_best = value_test;
718 index_best = index_test;
719 }
720
721 *index_p = index_best;
722 return value_best;
723}
724
725static float convexhull_aabb_fit_hull_2d(const float (*points_hull)[2], int points_hull_num)
726{
727 float area_best = std::numeric_limits<float>::max();
728 float2 sincos_best = {0.0f, 1.0f}; /* Track the best angle as a unit vector, delaying `atan2`. */
729 int index_best = std::numeric_limits<int>::max();
730
731 /* Initialize to zero because the first pass uses the first index to set the bounds. */
732 blender::Bounds<int> bounds_index[2] = {{0, 0}, {0, 0}};
733
734 HullAngleIter hull_iter = convexhull_2d_angle_iter_init(points_hull, points_hull_num);
735
736 /* Use the axis aligned bounds as starting points. */
737 bounds_index[0].min = hull_iter.axis[0][1].angle.index;
738 bounds_index[0].max = hull_iter.axis[0][0].angle.index;
739 bounds_index[1].min = hull_iter.axis[1][0].angle.index;
740 bounds_index[1].max = hull_iter.axis[1][1].angle.index;
741 while (const HullAngleStep *hstep = hull_iter.axis_ordered) {
742 /* Step the calipers to the new rotation `sincos`, returning the bounds at the same time. */
743 blender::Bounds<float> bounds_test[2] = {
745 points_hull, points_hull_num, hstep->angle.sincos_canonical, &bounds_index[0].min),
747 points_hull, points_hull_num, hstep->angle.sincos_canonical, &bounds_index[0].max)},
749 points_hull, points_hull_num, hstep->angle.sincos_canonical, &bounds_index[1].min),
751 points_hull, points_hull_num, hstep->angle.sincos_canonical, &bounds_index[1].max)},
752 };
753
754 const float area_test = (bounds_test[0].max - bounds_test[0].min) *
755 (bounds_test[1].max - bounds_test[1].min);
756
757 if (area_test < area_best ||
758 /* Use the index as a tie breaker, this simply matches the behavior of checking
759 * all edges in-order and only overwriting past results when they're an improvement. */
760 ((area_test == area_best) && (hstep->angle.index < index_best)))
761 {
762 area_best = area_test;
763 sincos_best = hstep->angle.sincos;
764 index_best = hstep->angle.index;
765 }
766
768 }
769
770 const float angle = (area_best != std::numeric_limits<float>::max()) ?
771 atan2(sincos_best[0], sincos_best[1]) :
772 0.0f;
773
774#if defined(USE_BRUTE_FORCE_ASSERT) && !defined(NDEBUG)
775 {
776 /* Ensure the optimized result matches the brute-force version. */
777 const float2 sincos_test = convexhull_aabb_fit_hull_2d_brute_force(points_hull,
778 points_hull_num);
779 if (sincos_best != sincos_test) {
780 BLI_assert(sincos_best == sincos_test);
781 }
782 }
783#endif
784
785 return angle;
786}
787
789{
790 const int points_num = int(points.size());
791 BLI_assert(points_num >= 0);
792 float angle = 0.0f;
793
794 int *index_map = MEM_malloc_arrayN<int>(size_t(points_num), __func__);
795
796 int points_hull_num = BLI_convexhull_2d(points, index_map);
797
798 if (points_hull_num > 1) {
799 float (*points_hull)[2] = MEM_malloc_arrayN<float[2]>(size_t(points_hull_num), __func__);
800 for (int j = 0; j < points_hull_num; j++) {
801 copy_v2_v2(points_hull[j], points[index_map[j]]);
802 }
803
804 angle = convexhull_aabb_fit_hull_2d(points_hull, points_hull_num);
805 MEM_freeN(points_hull);
806 }
807
808 MEM_freeN(index_map);
809
810 return angle;
811}
812
#define BLI_assert(a)
Definition BLI_assert.h:46
void BLI_kdtree_nd_ insert(KDTree *tree, int index, const float co[KD_DIMS]) ATTR_NONNULL(1
MINLINE void copy_v2_v2(float r[2], const float a[2])
#define UNUSED_VARS(...)
#define UNUSED_VARS_NDEBUG(...)
#define UNLIKELY(x)
#define LIKELY(x)
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
constexpr int64_t size() const
Definition BLI_span.hh:252
nullptr float
static int hull_angle_canonical_cmp(const AngleCanonical &a, const AngleCanonical &b)
static HullAngleIter convexhull_2d_angle_iter_init(const float(*points_hull)[2], const int points_hull_num)
static float convexhull_aabb_fit_hull_2d(const float(*points_hull)[2], int points_hull_num)
static void hull_angle_insert_ordered(HullAngleIter &hiter, HullAngleStep *insert)
static float convexhull_2d_compute_extent_on_axis(const float(*points_hull)[2], const int points_hull_num, const float2 &sincos, int *index_p)
static float2 sincos_canonical(const float2 &sincos)
static void convexhull_2d_stack_finalize(const float2 *points, const int r_points[], blender::int2 &r_points_range)
static float sincos_rotate_cw_x(const float2 &sincos, const float2 &p)
static void convexhull_2d_angle_iter_step(HullAngleIter &hiter)
int BLI_convexhull_2d(blender::Span< float2 > points, int r_points[])
static float sincos_rotate_cw_y(const float2 &sincos, const float2 &p)
static bool convexhull_2d_angle_iter_step_on_axis(const HullAngleIter &hiter, HullAngleStep &hstep)
static int convexhull_2d_sorted_impl(const float2 *points, const int points_num, int r_points[])
float BLI_convexhull_aabb_fit_points_2d(blender::Span< float2 > points)
static blender::int2 convexhull_2d_sorted(const float2 *points, const int points_num, int r_points[])
static float is_left(const float2 &p0, const float2 &p1, const float2 &p2)
static void convexhull_2d_stack_push(const float2 *points, int r_points[], int &top, int index)
uint top
int count
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float3 atan2(const float3 y, const float3 x)
QuaternionBase< T > normalize_and_get_length(const QuaternionBase< T > &q, T &out_length)
T min(const T &a, const T &b)
T max(const T &a, const T &b)
T abs(const T &a)
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
#define min(a, b)
Definition sort.cc:36
const float(* points_hull)[2]
HullAngleStep axis[2][2]
HullAngleStep * axis_ordered
HullAngleStep * next
AngleCanonical angle
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251