返回列表 发帖

C语言中显示 点在多边形内 算法

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。! p" u/ k- O$ J) ?) D8 X

0 t% q7 S6 f6 [5 j  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。; {9 P$ o6 i6 K% U" i8 Z4 y) [

3 {, [0 Z9 s+ z  y' w  首先定义点结构如下:+ A. x- |. w: f7 w4 R1 v% ~$ T! `

; W1 z. F! }0 g# c3 A6 @1 w! J以下是引用片段:
( ^$ t* \5 @& f* p$ P& z0 ^  /* Vertex structure */ " Z* l! b' E' L; O( G( Z- d& K
  typedef struct 2 f4 F) I. u6 k# W
  { 3 A2 s4 C2 H+ ^) o# l! z. D
  double x, y;
5 b; M% I+ x( I9 O0 ?* e  } vertex_t;
1 e$ \/ J! F2 o1 M
9 U5 M8 ]; `2 F) s( i
; ]( @+ B5 ?% _  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:  k9 h3 S- w7 k; q0 A# X
' G$ n/ ~- Y1 Q; b: v
以下是引用片段:: q, p. U+ f- I
  /* Vertex list structure – polygon */ * X: d! h7 X. T. O5 H( a! X' w
  typedef struct
" k) \6 m4 w, I7 O# H  {
# [0 f+ Y, \: l  P, L  int num_vertices; /* Number of vertices in list */ ' d7 q% J; y% E
  vertex_t *vertex; /* Vertex array pointer */ 5 p. p* e9 s# l6 }! N! _
  } vertexlist_t;
8 i1 Q: u6 N9 d& g0 H: n+ j+ R* d
0 ]4 s( i- I  k4 ^
# r, X/ r  i/ V1 n/ y$ R5 {1 {  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
# }/ h/ c0 p  H4 I' v; \. q# P0 b5 U
' i" |6 t0 n( ?2 L: C) s以下是引用片段:, q/ ^8 `% B$ H4 Y! A" |
  /* bounding rectangle type */
8 o2 H+ L, ]' P  Z' W2 I# ]  typedef struct - ?* D$ t0 R3 a8 B. F7 y
  {
2 Z5 R& c, h  Z: S6 z  double min_x, min_y, max_x, max_y;
9 g4 n0 L8 t% T! g/ o  } rect_t; + {$ ]8 c- R" K% L
  /* gets extent of vertices */ , w; R! O. X1 N/ o3 |- Q
  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ 0 G6 q$ ~+ d7 ~/ p. W0 ~( q9 ?
  rect_t* rc /* out extent*/ )
" V: g6 p2 C  K2 U' D6 @# ]5 e% e  { 9 i: b  ^* f0 n1 C
  int i; % o6 R2 J- v+ e0 G
  if (np > 0){ / b+ ^9 v3 t3 c4 b9 N3 ]9 K
  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; , V* Z+ Y% ~5 S5 S
  }else{
4 Q/ S6 H3 M* ?5 p  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ + v1 P8 N) X8 t6 X
  }
& K9 J- a  I+ W1 ?% \* h1 Y  for(i=1; i  ! a  d: R( `, K
  { - h6 C2 ^$ i  j) U* t* n2 j3 Y
  if(vl.x < rc->min_x) rc->min_x = vl.x;
4 T9 g6 G' [0 F" R! ~' x: \/ r& ]  if(vl.y < rc->min_y) rc->min_y = vl.y;
" o/ }) F1 N5 B+ n- Q0 @  if(vl.x > rc->max_x) rc->max_x = vl.x;
* p6 H* c  `% N  if(vl.y > rc->max_y) rc->max_y = vl.y; $ `$ B3 d( j- L0 e7 @, T; M- \
  }
+ b  b; ^1 g! q4 T! h& z  } ! D! t( _" B6 L6 Q

& h( j/ J( P3 X) m7 j' w7 s3 s5 m( d# y2 o5 u; W/ H. E+ Z+ p% p/ X
  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。) V6 v% _7 e+ I+ Z( y# q
5 X9 ?# R2 R% v6 ?" x
  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
6 y) R1 x( r" i! ?- z
6 l8 ~; K: }& v2 g) Q& F  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
' `% [# E3 a+ G9 t& y, n% ?( H- ^' y; {$ ^
  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;7 s, G! ~( T* L3 i. _4 M

2 Q2 d7 {8 E: S' Z以下是引用片段:5 t  U" |* G3 c% v8 ^4 O' u0 w
  /* p, q is on the same of line l */
, t; U) C; \4 _$ l  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
# c5 l$ H# R- G: P# l  const vertex_t* p,
" z# J$ a/ Z) I  const vertex_t* q) 2 S2 t) X- J# r6 E$ A/ N
  {
! X. o/ W8 L" H* b- _9 t" N  double dx = l_end->x - l_start->x;
5 m. K  B% M$ x5 V8 b+ _, k  double dy = l_end->y - l_start->y;
( P( A4 y# Z+ K2 ^, z) {2 b  double dx1= p->x - l_start->x; 2 U0 |* R  |5 @! g
  double dy1= p->y - l_start->y; 2 C3 [2 B* U: [9 Q- {! x5 J
  double dx2= q->x - l_end->x; 3 E- T8 x$ U5 Y
  double dy2= q->y - l_end->y;
" E' @8 v3 l" k  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); 2 z6 V' F9 _+ l5 X
  } 7 T! F5 N" |4 {2 p& Z6 G
  /* 2 line segments (s1, s2) are intersect? */
2 X# E8 f; `" A1 T  m8 t) ?1 _2 b  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, ) }$ |: k6 b6 ]/ j
  const vertex_t* s2_start, const vertex_t* s2_end) / [7 @* K2 d2 q. m) [
  {
; D0 t) h, `0 B$ x: i- I+ P7 i0 p  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
1 R2 K7 q7 h9 J1 c5 V% G  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; 0 w) x# y- b% l: X, y6 O: y
  }
! Y- u, N+ f' ?( ], Q6 @1 C$ U) |0 p: ~( P/ W' `
4 V. j4 `+ Y* u/ z1 m
  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
6 j  M- y: T, J+ q( k6 S
$ E3 a& j6 a% Y以下是引用片段:
# a2 @3 ?! y( V1 I# X$ q8 L, ?  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ & v! c9 ?, a  e' B. h, a; Z' c9 x
  const vertex_t* v) / c' O5 G2 {# `- R) q' b
  {   c( A! n' G2 \. N6 g
  int i, j, k1, k2, c; 3 B* `/ }4 r. @0 f+ ?5 _) k
  rect_t rc;
( l( {% G0 A+ \6 f  vertex_t w; 2 s. n' ]! h3 M4 Z6 G( ]$ N
  if (np < 3) / ^- F. i9 Z7 [4 Y( b  a$ v( L2 t
  return 0;
. ]  Y6 \- t) x2 B  vertices_get_extent(vl, np, &rc);
0 y9 ], t+ R7 i/ i  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) ) p% a' W4 M6 A
  return 0; ! m- T. k, Q& R0 l1 l3 B% j0 |
  /* Set a horizontal beam l(*v, w) from v to the ultra right */
1 Y4 }' g6 d9 Q  w.x = rc.max_x + DBL_EPSILON;
0 @$ |- U. K' v& t2 ~4 b  w.y = v->y; ) V% K% ^8 h$ Q  h* z
  c = 0; /* Intersection points counter */ ) f% W3 o! Q* x* `  p! a" n7 q; I
  for(i=0; i  
. F0 d% E2 m1 ~2 V3 K8 G  { ! _3 _0 p8 y% H; r3 d, Z" g, j
  j = (i+1) % np;
* w2 }' G8 b  Q# \# E5 j5 q  if(is_intersect(vl+i, vl+j, v, &w))
1 Q; T% p# D  s  d2 G  { : f% L, P7 b$ Q$ b( r% X
  C++;
, I3 I! ^2 ^6 B0 E! q: t9 |  }
! F" N: g% E4 g3 ?! \: }" i* J  else if(vl.y==w.y) 7 z" _! V3 y6 |; f
  { ! h* M# C( q; G3 l. x
  k1 = (np+i-1)%np; 8 S. `/ J$ s# Q* y8 _; p
  while(k1!=i && vl[k1].y==w.y)
/ X/ W0 s. h; q, F  k1 = (np+k1-1)%np; : `5 s! i8 }$ R2 {8 K, P
  k2 = (i+1)%np;
  I9 Z' E! m1 @% d6 U- T6 w  while(k2!=i && vl[k2].y==w.y) 7 _3 ]1 c" U: _* l
  k2 = (k2+1)%np; 8 J* j/ A7 y7 u+ j; a3 I
  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) ! @+ w5 f% {. b1 ]! Y
  C++; ) \" N* G% g' E  m9 Y2 A
  if(k2 <= i) 1 ]! o& j1 _  {: X
  break; 2 O& A" C9 U3 K& X
  i = k2; . x. |, C! R* U' M% v% _
  }
4 U( f$ B# Q. B  }
% k; V  H# x8 ~1 T1 t$ r7 \  return c%2;
: d, s% e3 \- r  }
- e1 x4 ^+ `3 i
, L( ~/ L7 M/ M. O
2 s6 ^( x4 F5 a0 l  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

返回列表
【捌玖网络】已经运行: