Mapper
pointpoly.c
Go to the documentation of this file.
1 /*
2 Here it is, code for the angle test, the barycentric test, and the two
3 crossings tests, with a main program to test their speeds. You may need to
4 use something other than srand()/drand48() for your random number generator,
5 and the times() command for timing. You'll also want to change the
6 *_TEST_TIMES constants if you're using something slower than an HP 720
7 workstation. Feel free to complain about the slowness of any of the code -
8 I'm always interested in new hacks.
9 
10 
11  * Point in polygon inside/outside tester. Angle summation, barycentric
12  * coordinates, and ray along x-axis (crossings testing) compared.
13  *
14  * copyright 1992 by Eric Haines, 3D/Eye Inc, erich@eye.com
15  */
16 
17 #include <math.h>
18 #include <sys/types.h>
19 #include <sys/param.h>
20 #include <sys/times.h>
21 
22 #define X 0
23 #define Y 1
24 
25 double drand48() ;
26 
31 
32 /* minimum & maximum number of polygon vertices to generate */
33 #define MIN_VERTS 3
34 #define MAX_VERTS 7
35 
36 /* number of different polygons to try */
37 #define TEST_POLYGONS 30
38 
39 /* number of different intersection points to try */
40 #define TEST_POINTS 30
41 
42 /* number of times to try a single point vs. a polygon */
43 /* this should be > 1 / ( HZ * approx. single test time in seconds ) */
44 #define ANGLE_TEST_TIMES 1000
45 #define BARYCENTRIC_TEST_TIMES 10000
46 #define CROSSINGS_TEST_TIMES 10000
47 #define MACMARTIN_TEST_TIMES 10000
48 
49 main(argc,argv)
50 int argc; char *argv[];
51 {
52 int i, j, numverts, inside_tot ;
53 int angle_flag, barycentric_flag, crossings_flag, macmartin_flag ;
54 double pgon[MAX_VERTS][2] ;
55 double point[2] ;
56 
57  srand( 12345 ) ;
58 
59  AngleTimeTotal = 0.0 ;
60  BarycentricTimeTotal = 0.0 ;
61  CrossingsTimeTotal = 0.0 ;
62  MacmartinTimeTotal = 0.0 ;
63  inside_tot = 0 ;
64 
65  for ( i = 0 ; i < TEST_POLYGONS ; i++ ) {
66 
67 #ifdef CENTERED_SQUARE
68  /* for debugging purposes, test against a square */
69  numverts = 4 ;
70  pgon[0][X] = 0.2 ;
71  pgon[0][Y] = 0.2 ;
72  pgon[1][X] = 0.8 ;
73  pgon[1][Y] = 0.2 ;
74  pgon[2][X] = 0.8 ;
75  pgon[2][Y] = 0.8 ;
76  pgon[3][X] = 0.2 ;
77  pgon[3][Y] = 0.8 ;
78 #else
79  /* make an arbitrary polygon fitting 0-1 range in x and y */
81  (int)(drand48() * (double)(MAX_VERTS-MIN_VERTS+1)) ;
82  for ( j = 0 ; j < numverts ; j++ ) {
83  pgon[j][X] = drand48() ;
84  pgon[j][Y] = drand48() ;
85  }
86 #endif
87 
88  /* now try # of points against it */
89  for ( j = 0 ; j < TEST_POINTS ; j++ ) {
90  point[X] = drand48() ;
91  point[Y] = drand48() ;
92  angle_flag = angletest( pgon, numverts, point ) ;
93  barycentric_flag = barycentrictest( pgon, numverts, point ) ;
94  crossings_flag = crossingstest( pgon, numverts, point ) ;
95  macmartin_flag = macmartintest( pgon, numverts, point ) ;
96 
97  /* reality check */
98  if ( angle_flag != crossings_flag ) {
99  printf( "angle test says %s, crossings test says %s\n",
100  angle_flag ? "INSIDE" : "OUTSIDE",
101  crossings_flag ? "INSIDE" : "OUTSIDE" ) ;
102  printf( "point %g %g\n", (float)point[X], (float)point[Y] ) ;
103  printf( "polygon:\n" ) ;
104  for ( j = 0 ; j < numverts ; j++ ) {
105  printf( " %g %g\n", (float)pgon[j][X], (float)pgon[j][Y]);
106  }
107  }
108  if ( barycentric_flag != macmartin_flag ) {
109  printf( "barycentric test says %s, macmartin test says %s\n",
110  barycentric_flag ? "INSIDE" : "OUTSIDE",
111  macmartin_flag ? "INSIDE" : "OUTSIDE" ) ;
112  printf( "point %g %g\n", (float)point[X], (float)point[Y] ) ;
113  printf( "polygon:\n" ) ;
114  for ( j = 0 ; j < numverts ; j++ ) {
115  printf( " %g %g\n", (float)pgon[j][X], (float)pgon[j][Y]);
116  }
117  }
118 
119  inside_tot += crossings_flag ;
120  }
121  }
122  printf( "angle test time: %g microseconds per test\n",
123  (float)( AngleTimeTotal/(double)(TEST_POINTS*TEST_POLYGONS) ) ) ;
124  printf( "barycentric test time: %g microseconds per test\n",
125  (float)( BarycentricTimeTotal/(double)(TEST_POINTS*TEST_POLYGONS) ) ) ;
126  printf( "crossings test time: %g microseconds per test\n",
127  (float)( CrossingsTimeTotal/(double)(TEST_POINTS*TEST_POLYGONS) ) ) ;
128  printf( "macmartin crossings test time: %g microseconds per test\n",
129  (float)( MacmartinTimeTotal/(double)(TEST_POINTS*TEST_POLYGONS) ) ) ;
130 
131  printf( "%g %% of all points were inside polygons\n",
132  (float)inside_tot * 100.0 / (float)(TEST_POINTS*TEST_POLYGONS) ) ;
133 }
134 
135 /* sum angles of vtxN - point - vtxN+1, check if in PI to 3*PI range */
136 int
138 double pgon[MAX_VERTS][2] ;
139 int numverts ;
140 double point[2] ;
141 {
142 int i, j, inside_flag ;
143 struct tms timebuf ;
144 long timestart ;
145 long timestop ;
146 double *vtx0, *vtx1, angle, len, vec0[2], vec1[2] ;
147 
148  /* do the test a bunch of times to get a useful time reading */
149  timestart = times( &timebuf ) ;
150  for ( i = 0 ; i < ANGLE_TEST_TIMES ; i++ ) {
151  /* sum the angles and see if answer mod 2*PI > PI */
152  vtx0 = pgon[numverts-1] ;
153  vec0[X] = vtx0[X] - point[X] ;
154  vec0[Y] = vtx0[Y] - point[Y] ;
155  if ( (len = hypot( vec0[X], vec0[Y] )) <= 0.0 ) {
156  /* point and vertex coincide */
157  return( 1 ) ;
158  }
159  vec0[X] /= len ;
160  vec0[Y] /= len ;
161 
162  angle = 0.0 ;
163  for ( j = 0 ; j < numverts ; j++ ) {
164  vtx1 = pgon[j] ;
165  vec1[X] = vtx1[X] - point[X] ;
166  vec1[Y] = vtx1[Y] - point[Y] ;
167  if ( (len = hypot( vec1[X], vec1[Y] )) <= 0.0 ) {
168  /* point and vertex coincide */
169  return( 1 ) ;
170  }
171  vec1[X] /= len ;
172  vec1[Y] /= len ;
173 
174  /* check if vec1 is to "left" or "right" of vec0 */
175  if ( vec0[X] * vec1[Y] - vec1[X] * vec0[Y] >= 0.0 ) {
176  /* add angle due to dot product of vectors */
177  angle += acos( vec0[X] * vec1[X] + vec0[Y] * vec1[Y] ) ;
178  } else {
179  /* subtract angle due to dot product of vectors */
180  angle -= acos( vec0[X] * vec1[X] + vec0[Y] * vec1[Y] ) ;
181  }
182 
183  /* get to next point */
184  vtx0 = vtx1 ;
185  vec0[X] = vec1[X] ;
186  vec0[Y] = vec1[Y] ;
187  }
188  /* test if between PI and 3*PI, 5*PI and 7*PI, etc */
189  /* if we care about is winding number > 0, then just:
190  inside_flag = fabs(angle) > M_PI ;
191  */
192  inside_flag = fmod( fabs(angle) + M_PI, 4.0*M_PI ) > 2.0*M_PI ;
193  }
194  timestop = times( &timebuf ) ;
195  /* time in microseconds */
196  AngleTimeTotal += 1000000.0 * (double)(timestop - timestart) /
197  (double)(HZ * ANGLE_TEST_TIMES) ;
198 
199  return (inside_flag) ;
200 }
201 
202 /* barycentric, a la Gems I, with a little efficiency tuning */
203 int
205 double pgon[MAX_VERTS][2] ;
206 int numverts ;
207 double point[2] ;
208 {
209 int i, tris_hit, inside_flag, p1, p2 ;
210 struct tms timebuf ;
211 long timestart ;
212 long timestop ;
213 double tx, ty, u0, u1, u2, v0, v1, alpha, beta, denom ;
214 
215  /* do the test a bunch of times to get a useful time reading */
216  timestart = times( &timebuf ) ;
217  for ( i = 0 ; i < BARYCENTRIC_TEST_TIMES ; i++ ) {
218 
219  tx = point[X] ;
220  ty = point[Y] ;
221 
222  tris_hit = 0 ;
223 
224  for ( p1 = 1, p2 = 2 ; p2 < numverts ; p1++, p2++ ) {
225 
226  if ( ( u1 = pgon[0][X] - pgon[p2][X] ) == 0.0 ) {
227 
228  /* zero area test - optional */
229  if ( ( u2 = pgon[p1][X] - pgon[p2][X] ) == 0.0 ) {
230  goto NextTri;
231  }
232 
233  /* Compute intersection point */
234  if ( ( ( beta = ( tx - pgon[p2][X] ) / u2 ) < 0.0 ) ||
235  ( beta > 1.0 ) ) {
236 
237  goto NextTri;
238  }
239 
240  if ( ( v1 = pgon[0][Y] - pgon[p2][Y] ) == 0.0 ) {
241  goto NextTri;
242  }
243 
244  if ( ( alpha = ( ty - pgon[p2][Y] - beta *
245  ( pgon[p1][Y] - pgon[p2][Y] ) ) / v1 ) < 0.0 ) {
246  goto NextTri;
247  }
248 
249  } else {
250  if ( ( denom = ( pgon[p1][Y] - pgon[p2][Y] ) * u1 -
251  ( u2 = pgon[p1][X] - pgon[p2][X] ) *
252  ( v1 = pgon[0][Y] - pgon[p2][Y] ) ) == 0.0 ) {
253 
254  goto NextTri;
255  }
256 
257  /* Compute intersection point & subtract 0 vertex */
258  u0 = tx - pgon[p2][X] ;
259  v0 = ty - pgon[p2][Y] ;
260 
261  if ( ( ( ( beta = ( v0 * u1 - u0 * v1 ) / denom ) ) < 0.0 ) ||
262  ( beta > 1.0 ) ) {
263 
264  goto NextTri;
265  }
266  if ( ( alpha = ( u0 - beta * u2 ) / u1 ) < 0.0 ) {
267  goto NextTri;
268  }
269  }
270 
271  /* check gamma */
272  if ( alpha + beta <= 1.0 ) {
273  /* survived */
274  tris_hit++ ;
275  }
276 
277  NextTri: ;
278  }
279  inside_flag = tris_hit & 0x1 ;
280  }
281  timestop = times( &timebuf ) ;
282  /* time in microseconds */
283  BarycentricTimeTotal += 1000000.0 * (double)(timestop - timestart) /
284  (double)(HZ * BARYCENTRIC_TEST_TIMES) ;
285 
286  return (inside_flag) ;
287 }
288 
289 /* shoot a test ray along +X axis - slower version, less messy */
290 int
292 double pgon[MAX_VERTS][2] ;
293 int numverts ;
294 double point[2] ;
295 {
296 int i, j, inside_flag, xflag0 ;
297 struct tms timebuf ;
298 long timestart ;
299 long timestop ;
300 double *vtx0, *vtx1, dv0 ;
301 int crossings, yflag0, yflag1 ;
302 
303  /* do the test a bunch of times to get a useful time reading */
304  timestart = times( &timebuf ) ;
305  for ( i = 0 ; i < CROSSINGS_TEST_TIMES ; i++ ) {
306 
307  vtx0 = pgon[numverts-1] ;
308  /* get test bit for above/below Y axis */
309  yflag0 = ( dv0 = vtx0[Y] - point[Y] ) >= 0.0 ;
310 
311  crossings = 0 ;
312  for ( j = 0 ; j < numverts ; j++ ) {
313  /* cleverness: bobble between filling endpoints of edges, so
314  * that the previous edge's shared endpoint is maintained.
315  */
316  if ( j & 0x1 ) {
317  vtx0 = pgon[j] ;
318  yflag0 = ( dv0 = vtx0[Y] - point[Y] ) >= 0.0 ;
319  } else {
320  vtx1 = pgon[j] ;
321  yflag1 = ( vtx1[Y] >= point[Y] ) ;
322  }
323 
324  /* check if points not both above/below X axis - can't hit ray */
325  if ( yflag0 != yflag1 ) {
326  /* check if points on same side of Y axis */
327  if ( ( xflag0 = ( vtx0[X] >= point[X] ) ) ==
328  ( vtx1[X] >= point[X] ) ) {
329 
330  if ( xflag0 ) crossings++ ;
331  } else {
332  /* compute intersection of pgon segment with X ray, note
333  * if > point's X.
334  */
335  crossings += (vtx0[X] -
336  dv0*( vtx1[X]-vtx0[X])/(vtx1[Y]-vtx0[Y])) >= point[X] ;
337  }
338  }
339  }
340  /* test if crossings is odd */
341  /* if we care about is winding number > 0, then just:
342  inside_flag = crossings > 0 ;
343  */
344  inside_flag = crossings & 0x01 ;
345  }
346  timestop = times( &timebuf ) ;
347  /* time in microseconds */
348  CrossingsTimeTotal += 1000000.0 * (double)(timestop - timestart) /
349  (double)(HZ * CROSSINGS_TEST_TIMES) ;
350 
351  return (inside_flag) ;
352 }
353 
354 /* shoot a test ray along +X axis - macmartinized by me, a bit messier */
355 int
357 double pgon[MAX_VERTS][2] ;
358 int numverts ;
359 double point[2] ;
360 {
361 int i, inside_flag, xflag0 ;
362 struct tms timebuf ;
363 long timestart ;
364 long timestop ;
365 double *p, *stop ;
366 int crossings ;
367 double tx, ty, y ;
368 
369  /* do the test a bunch of times to get a useful time reading */
370  timestart = times( &timebuf ) ;
371  for ( i = 0 ; i < MACMARTIN_TEST_TIMES ; i++ ) {
372 
373  crossings = 0 ;
374 
375  tx = point[X] ;
376  ty = point[Y] ;
377  y = pgon[numverts-1][Y] ;
378 
379  p = (double *)pgon + 1 ;
380  if ( ( y >= ty ) != ( *p >= ty ) ) {
381  /* x crossing */
382  if ( ( xflag0 = ( pgon[numverts-1][X] >= tx ) ) ==
383  ( *(double *)pgon >= tx ) ) {
384 
385  if ( xflag0 ) crossings++ ;
386  } else {
387  /* compute intersection of pgon segment with X ray, note
388  * if > point's X.
389  */
390  crossings += ( pgon[numverts-1][X] -
391  (y-ty)*( *(double *)pgon - pgon[numverts-1][X])/(*p-y)) >= tx ;
392  }
393  }
394 
395  stop = pgon[numverts] ;
396 
397  for ( y=*p,p += 2 ; p < stop ; y=*p,p+=2) {
398 
399  if ( y >= ty ) {
400  while ( (p < stop) && (*p >= ty) ) p+=2 ;
401  if ( p >= stop ) goto Exit ;
402  /* y crosses */
403  if ( ( xflag0 = ( *(p-3) >= tx ) ) ==
404  ( *(p-1) >= tx ) ) {
405 
406  if ( xflag0 ) crossings++ ;
407  } else {
408  /* compute intersection of pgon segment with X ray, note
409  * if > point's X.
410  */
411  crossings += ( *(p-3) -
412  (*(p-2)-ty)*( *(p-1)-*(p-3))/(*p-*(p-2))) >= tx ;
413  }
414  } else {
415  while ( (p < stop) && (*p < ty)) p+=2 ;
416  if ( p >= stop ) goto Exit ;
417  /* y crosses */
418  if ( ( xflag0 = ( *(p-3) >= tx ) ) ==
419  ( *(p-1) >= tx ) ) {
420 
421  if ( xflag0 ) crossings++ ;
422  } else {
423  /* compute intersection of pgon segment with X ray, note
424  * if > point's X.
425  */
426  crossings += ( *(p-3) -
427  (*(p-2)-ty)*( *(p-1)-*(p-3))/(*p-*(p-2))) >= tx ;
428  }
429  }
430  }
431 
432  Exit:
433  /* test if crossings is odd */
434  /* if we care about is winding number > 0, then just:
435  inside_flag = crossings > 0 ;
436  */
437  inside_flag = crossings & 0x01 ;
438  }
439  timestop = times( &timebuf ) ;
440  /* time in microseconds */
441  MacmartinTimeTotal += 1000000.0 * (double)(timestop - timestart) /
442  (double)(HZ * MACMARTIN_TEST_TIMES) ;
443 
444  return (inside_flag) ;
445 }
double pgon[5000][2]
Definition: build_hrap.c:5
static int i
printf("fbuf is %s\n", fbuf)
int j
Definition: mapp2h.h:48
int numverts
Definition: mapper.c:91
double drand48()
main(int argc, argv)
Definition: pointpoly.c:49
#define X
Definition: pointpoly.c:22
double CrossingsTimeTotal
Definition: pointpoly.c:29
double BarycentricTimeTotal
Definition: pointpoly.c:28
int crossingstest(pgon, numverts, point)
Definition: pointpoly.c:291
int angletest(pgon, numverts, point)
Definition: pointpoly.c:137
#define MAX_VERTS
Definition: pointpoly.c:34
double AngleTimeTotal
Definition: pointpoly.c:27
#define Y
Definition: pointpoly.c:23
#define TEST_POINTS
Definition: pointpoly.c:40
double MacmartinTimeTotal
Definition: pointpoly.c:30
int macmartintest(pgon, numverts, point)
Definition: pointpoly.c:356
#define MACMARTIN_TEST_TIMES
Definition: pointpoly.c:47
#define TEST_POLYGONS
Definition: pointpoly.c:37
int barycentrictest(pgon, numverts, point)
Definition: pointpoly.c:204
#define CROSSINGS_TEST_TIMES
Definition: pointpoly.c:46
#define BARYCENTRIC_TEST_TIMES
Definition: pointpoly.c:45
#define ANGLE_TEST_TIMES
Definition: pointpoly.c:44
#define MIN_VERTS
Definition: pointpoly.c:33
Definition: mapp2h.h:29