捌玖网络工作室's Archiver

zw2004 发表于 2008-1-21 17:20

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。"\3K"b:^)eK
YXNl Aag+j/L
  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。6V)@D {;A9Tic

A2k5i'Rxn%Z&{B   首先定义点结构如下:@JZE J"v*W l#z
a veT;Cf
以下是引用片段:
e(K E uB {7E:D   /* Vertex structure */ .L@z6Xo
  typedef struct
!Xv@$AYkn6w   { )M`'P,j.U h)t v+X/q!^
  double x, y; k? WS&o0S1BB
  } vertex_t; ^^3t:q5Q7_&sgd

6UAK/{xW5{w|
"`0T/BU ]C2~2_ A   本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:%_4l)MM0r

#[ ?\4D*G,~I$g?W 以下是引用片段:
_ \?Z Ekh/Y+l,d   /* Vertex list structure – polygon */ *EO%yV5w@
  typedef struct
_%yp(}Kad   {
&f9B9GY*D]M   int num_vertices; /* Number of vertices in list */
z \ NpYNU   vertex_t *vertex; /* Vertex array pointer */
C!I |/in,jp M   } vertexlist_t; g b8CV EM"AE,B

;O]?/TOgH?
L'W{H~Iu K a   为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:5E{yK)s;@B:y.Q

sn_ km 以下是引用片段:+WR9a2s]q
  /* bounding rectangle type */
_8b4Py3PI   typedef struct
3k/Kg UWCg   {
&`:wc!`4\7@S*sb   double min_x, min_y, max_x, max_y; "D4|'e D1X9n/?F]
  } rect_t; $t(v ^{*d4_
  /* gets extent of vertices */
3rl3zeY&gz   void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
7Ps`0h`oVQr   rect_t* rc /* out extent*/ )
@y;H&L,nt:b   {
3v*k*v(y5^}l   int i;
1S#gY|.q"g f   if (np > 0){ 0s7M}n&r:hTB
  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; Q P0y.R+~R9p9Q
  }else{ +j%^*U%b#JI2{
  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
3v{w0X9\(~ W3Q   } dEiLW
  for(i=1; i  
4eqfZ/r*Wzg0t%wL   {
3]s T*y\(BR   if(vl[i].x < rc->min_x) rc->min_x = vl[i].x; Ri2t;Jlep0l
  if(vl[i].y < rc->min_y) rc->min_y = vl[i].y; P{Z e?t:G1[
  if(vl[i].x > rc->max_x) rc->max_x = vl[i].x;
-K9kB-ka\s   if(vl[i].y > rc->max_y) rc->max_y = vl[i].y; F/f#h1lv8r
  }
1J8R:U:|(D   }
h)p?Y1b)w
rdH6KukeW3A
qa!m)L)T7XEK   当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。0^Ej(d z!\
0Aq$E-\c'lR*u2X
  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:o|q S+ls#u,@q
/G*w8J6m s^h*I`@
  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;e ?,ps\y

_ L0uUkO'~k   (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
x Iy(p j e-r"o(\
ia9Ng)R|*Y| 以下是引用片段:Mi pna;O"MZ `&\
  /* p, q is on the same of line l */
M_$`T#pw f%n*K   static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ cFXE(uy R tr(y^
  const vertex_t* p, 9jDE8j'TH;o
  const vertex_t* q)
U6t}X Ts|   {
]/K6W[:I t   double dx = l_end->x - l_start->x; !q _8Rg o`Gc^
  double dy = l_end->y - l_start->y;
2XfS!MDpzK   double dx1= p->x - l_start->x; M-}r4lQ2vL
  double dy1= p->y - l_start->y;
}%T%l.?)D2qq   double dx2= q->x - l_end->x;
c U+d/e9w ]\   double dy2= q->y - l_end->y;
p9cVlpBW*K   return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
GIWy'_5OW   }
qPN ~U7y&}   /* 2 line segments (s1, s2) are intersect? */ 5^`6A:v1Fz
  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, {5ZUPVf3o6S U9^ F
  const vertex_t* s2_start, const vertex_t* s2_end)
V u5p/{r;K   {
s c:p*v"PX7l   return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
5E] Gp_eS   is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; 5nbl3oST
  }
|]CO[g-@C T2| M#h o?2M9d
#@J"e:{1M5`&tQUQ
  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:E(TX;e{w6b
4?Nhk8L1zi5S
以下是引用片段:
KE \YwO$h:Qp~   int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
|]4n.I V   const vertex_t* v) )p5Y_.eVp m#o0W
  {
NNuNHd4n Vsm8o"r   int i, j, k1, k2, c;
;nob q'fJmjnk   rect_t rc;
E.Bp:_/dT%KW   vertex_t w;
|S1P| B*fv   if (np < 3)
JsZ"bU$Z P   return 0;
e fPG Bs9_%k   vertices_get_extent(vl, np, &rc); 6Fu?"cLTlP.O
  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
"\!k,`$NTs*y%E#t   return 0;
X3L/i8ufj7n-~ Q   /* Set a horizontal beam l(*v, w) from v to the ultra right */
1fNS/Uls   w.x = rc.max_x + DBL_EPSILON; O4aw uQ'O
  w.y = v->y; [IO&pZ"r\a9g~ L+pS
  c = 0; /* Intersection points counter */
R*RH(l@7i ]&a3yw#r'c   for(i=0; i  
%}b#@/Q.F$BE0K1G   {
.tY|So(}B   j = (i+1) % np; ;Vs/o6MQ9E\a
  if(is_intersect(vl+i, vl+j, v, &w)) ;t~U;g2M?*Tz4G;Jg
  { iG z&i z'Y;co._
  C++; v(j8_h;ya/X.W,G
  } ,g4j B:_K,n^
  else if(vl[i].y==w.y)
IvF8A|E%MRT   {
/c jYVP[9G   k1 = (np+i-1)%np;
H[@Dcf   while(k1!=i && vl[k1].y==w.y) hZ[|l?G/v3k
  k1 = (np+k1-1)%np;
b4v5]&@rlqN   k2 = (i+1)%np;
5ud iT/ju   while(k2!=i && vl[k2].y==w.y)
z:~b8x5Ox%iv   k2 = (k2+1)%np;
,BfVF Ge%f'I G*R!?8p   if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) m1hfmCzT.h
  C++; P?j/| uU
  if(k2 <= i) sc _,Qp{z5Gm~
  break;
!Hv.[&v x   i = k2; D6{"]@TQ
  }
9cgx6F)^ib%q-q   } ,] R3U[a9j0o'P
  return c%2; gJ.j!qMG3vLF
  } ;Sf$t m Q7h U0L&Q

eN#m!}9?.x*g-Ia:e
%cV,_y._ RXp;^#i^t   本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

页: [1]
【捌玖网络】已经运行:


Powered by Discuz! Archiver 7.2  © 2001-2009 Comsenz Inc.