Blender  V2.93
convexhull_2d.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "MEM_guardedalloc.h"
25 
26 #include "BLI_convexhull_2d.h"
27 #include "BLI_math.h"
28 #include "BLI_strict_flags.h"
29 #include "BLI_utildefines.h"
30 
31 /* Copyright 2001, softSurfer (http://www.softsurfer.com)
32  * This code may be freely used and modified for any purpose
33  * providing that this copyright notice is included with it.
34  * SoftSurfer makes no warranty for this code, and cannot be held
35  * liable for any real or imagined damage resulting from its use.
36  * Users of this code must verify correctness for their application.
37  * http://softsurfer.com/Archive/algorithm_0203/algorithm_0203.htm
38  */
39 
40 /* -------------------------------------------------------------------- */
51 static float is_left(const float p0[2], const float p1[2], const float p2[2])
52 {
53  return (p1[0] - p0[0]) * (p2[1] - p0[1]) - (p2[0] - p0[0]) * (p1[1] - p0[1]);
54 }
55 
64 int BLI_convexhull_2d_sorted(const float (*points)[2], const int n, int r_points[])
65 {
66  /* the output array r_points[] will be used as the stack */
67  int bot = 0;
68  int top = -1; /* indices for bottom and top of the stack */
69  int i; /* array scan index */
70  int minmin, minmax;
71  int maxmin, maxmax;
72  float xmax;
73 
74  /* Get the indices of points with min x-coord and min|max y-coord */
75  float xmin = points[0][0];
76  for (i = 1; i < n; i++) {
77  if (points[i][0] != xmin) {
78  break;
79  }
80  }
81 
82  minmin = 0;
83  minmax = i - 1;
84  if (minmax == n - 1) { /* degenerate case: all x-coords == xmin */
85  r_points[++top] = minmin;
86  if (points[minmax][1] != points[minmin][1]) {
87  /* a nontrivial segment */
88  r_points[++top] = minmax;
89  }
90  r_points[++top] = minmin; /* add polygon endpoint */
91  return top + 1;
92  }
93 
94  /* Get the indices of points with max x-coord and min|max y-coord */
95 
96  maxmax = n - 1;
97  xmax = points[n - 1][0];
98  for (i = n - 2; i >= 0; i--) {
99  if (points[i][0] != xmax) {
100  break;
101  }
102  }
103  maxmin = i + 1;
104 
105  /* Compute the lower hull on the stack r_points */
106  r_points[++top] = minmin; /* push minmin point onto stack */
107  i = minmax;
108  while (++i <= maxmin) {
109  /* the lower line joins points[minmin] with points[maxmin] */
110  if (is_left(points[minmin], points[maxmin], points[i]) >= 0 && i < maxmin) {
111  continue; /* ignore points[i] above or on the lower line */
112  }
113 
114  while (top > 0) { /* there are at least 2 points on the stack */
115  /* test if points[i] is left of the line at the stack top */
116  if (is_left(points[r_points[top - 1]], points[r_points[top]], points[i]) > 0.0f) {
117  break; /* points[i] is a new hull vertex */
118  }
119  top--; /* pop top point off stack */
120  }
121 
122  r_points[++top] = i; /* push points[i] onto stack */
123  }
124 
125  /* Next, compute the upper hull on the stack r_points above the bottom hull */
126  if (maxmax != maxmin) { /* if distinct xmax points */
127  r_points[++top] = maxmax; /* push maxmax point onto stack */
128  }
129 
130  bot = top; /* the bottom point of the upper hull stack */
131  i = maxmin;
132  while (--i >= minmax) {
133  /* the upper line joins points[maxmax] with points[minmax] */
134  if (is_left(points[maxmax], points[minmax], points[i]) >= 0 && i > minmax) {
135  continue; /* ignore points[i] below or on the upper line */
136  }
137 
138  while (top > bot) { /* at least 2 points on the upper stack */
139  /* test if points[i] is left of the line at the stack top */
140  if (is_left(points[r_points[top - 1]], points[r_points[top]], points[i]) > 0.0f) {
141  break; /* points[i] is a new hull vertex */
142  }
143  top--; /* pop top point off stack */
144  }
145 
146  if (points[i][0] == points[r_points[0]][0] && points[i][1] == points[r_points[0]][1]) {
147  return top + 1; /* special case (mgomes) */
148  }
149 
150  r_points[++top] = i; /* push points[i] onto stack */
151  }
152 
153  if (minmax != minmin) {
154  r_points[++top] = minmin; /* push joining endpoint onto stack */
155  }
156 
157  return top + 1;
158 }
159 
160 struct PointRef {
161  const float *pt; /* 2d vector */
162 };
163 
164 static int pointref_cmp_yx(const void *a_, const void *b_)
165 {
166  const struct PointRef *a = a_;
167  const struct PointRef *b = b_;
168 
169  if (a->pt[1] > b->pt[1]) {
170  return 1;
171  }
172  if (a->pt[1] < b->pt[1]) {
173  return -1;
174  }
175 
176  if (a->pt[0] > b->pt[0]) {
177  return 1;
178  }
179  if (a->pt[0] < b->pt[0]) {
180  return -1;
181  }
182  return 0;
183 }
184 
195 int BLI_convexhull_2d(const float (*points)[2], const int n, int r_points[])
196 {
197  struct PointRef *points_ref = MEM_mallocN(sizeof(*points_ref) * (size_t)n, __func__);
198  float(*points_sort)[2] = MEM_mallocN(sizeof(*points_sort) * (size_t)n, __func__);
199  int *points_map;
200  int tot, i;
201 
202  for (i = 0; i < n; i++) {
203  points_ref[i].pt = points[i];
204  }
205 
206  /* Sort the points by X, then by Y (required by the algorithm) */
207  qsort(points_ref, (size_t)n, sizeof(struct PointRef), pointref_cmp_yx);
208 
209  for (i = 0; i < n; i++) {
210  memcpy(points_sort[i], points_ref[i].pt, sizeof(float[2]));
211  }
212 
213  tot = BLI_convexhull_2d_sorted(points_sort, n, r_points);
214 
215  /* map back to the original index values */
216  points_map = (int *)points_sort; /* abuse float array for temp storage */
217  for (i = 0; i < tot; i++) {
218  points_map[i] = (int)((const float(*)[2])points_ref[r_points[i]].pt - points);
219  }
220 
221  memcpy(r_points, points_map, (size_t)tot * sizeof(*points_map));
222 
223  MEM_freeN(points_ref);
224  MEM_freeN(points_sort);
225 
226  return tot;
227 }
228 
231 /* Helper functions */
232 
233 /* -------------------------------------------------------------------- */
247 float BLI_convexhull_aabb_fit_hull_2d(const float (*points_hull)[2], unsigned int n)
248 {
249  unsigned int i, i_prev;
250  float area_best = FLT_MAX;
251  float dvec_best[2]; /* best angle, delay atan2 */
252 
253  i_prev = n - 1;
254  for (i = 0; i < n; i++) {
255  const float *ev_a = points_hull[i];
256  const float *ev_b = points_hull[i_prev];
257  float dvec[2]; /* 2d rotation matrix */
258 
259  sub_v2_v2v2(dvec, ev_a, ev_b);
260  if (normalize_v2(dvec) != 0.0f) {
261  /* rotation matrix */
262  float min[2] = {FLT_MAX, FLT_MAX}, max[2] = {-FLT_MAX, -FLT_MAX};
263  unsigned int j;
264  float area;
265 
266  for (j = 0; j < n; j++) {
267  float tvec[2];
268  mul_v2_v2_cw(tvec, dvec, points_hull[j]);
269 
270  min[0] = min_ff(min[0], tvec[0]);
271  min[1] = min_ff(min[1], tvec[1]);
272 
273  max[0] = max_ff(max[0], tvec[0]);
274  max[1] = max_ff(max[1], tvec[1]);
275 
276  area = (max[0] - min[0]) * (max[1] - min[1]);
277  if (area > area_best) {
278  break;
279  }
280  }
281 
282  if (area < area_best) {
283  area_best = area;
284  copy_v2_v2(dvec_best, dvec);
285  }
286  }
287 
288  i_prev = i;
289  }
290 
291  return (area_best != FLT_MAX) ? atan2f(dvec_best[0], dvec_best[1]) : 0.0f;
292 }
293 
299 float BLI_convexhull_aabb_fit_points_2d(const float (*points)[2], unsigned int n)
300 {
301  int *index_map;
302  int tot;
303 
304  float angle;
305 
306  index_map = MEM_mallocN(sizeof(*index_map) * n * 2, __func__);
307 
308  tot = BLI_convexhull_2d(points, (int)n, index_map);
309 
310  if (tot) {
311  float(*points_hull)[2];
312  int j;
313 
314  points_hull = MEM_mallocN(sizeof(*points_hull) * (size_t)tot, __func__);
315  for (j = 0; j < tot; j++) {
316  copy_v2_v2(points_hull[j], points[index_map[j]]);
317  }
318 
319  angle = BLI_convexhull_aabb_fit_hull_2d(points_hull, (unsigned int)tot);
320  MEM_freeN(points_hull);
321  }
322  else {
323  angle = 0.0f;
324  }
325 
326  MEM_freeN(index_map);
327 
328  return angle;
329 }
330 
typedef float(TangentPoint)[2]
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
MINLINE void mul_v2_v2_cw(float r[2], const float mat[2], const float vec[2])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float normalize_v2(float r[2])
Strict compiler flags for areas of code we want to ensure don't do conversions without us knowing abo...
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble top
Read Guarded memory(de)allocation.
SIMD_FORCE_INLINE btScalar angle(const btVector3 &v) const
Return the angle between this and another vector.
Definition: btVector3.h:356
static int pointref_cmp_yx(const void *a_, const void *b_)
float BLI_convexhull_aabb_fit_points_2d(const float(*points)[2], unsigned int n)
int BLI_convexhull_2d_sorted(const float(*points)[2], const int n, int r_points[])
Definition: convexhull_2d.c:64
float BLI_convexhull_aabb_fit_hull_2d(const float(*points_hull)[2], unsigned int n)
int BLI_convexhull_2d(const float(*points)[2], const int n, int r_points[])
static float is_left(const float p0[2], const float p1[2], const float p2[2])
Definition: convexhull_2d.c:51
#define atan2f(x, y)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
static unsigned a[3]
Definition: RandGen.cpp:92
static void area(int d1, int d2, int e1, int e2, float weights[2])
#define min(a, b)
Definition: sort.c:51
const float * pt
float max