返回列表 发帖

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
7 g7 @& a' ]( q7 t7 _7 D3 l
6 [; z( o9 I# |/ b  x+ f" `  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
4 P* ?4 H5 }8 w- L& x6 o
  k! q; S0 H5 e' v# N  首先定义点结构如下:
! [+ n+ @' m! r
8 q' x+ Y% L+ e( [5 Z  j! ~1 `! [以下是引用片段:5 w% G) O! b. @  S, o
  /* Vertex structure */
& [% C, |1 l) O3 K" C' J2 M  typedef struct . v0 Z" I' R6 E  O- z8 a
  { 9 b, M) S( O8 W$ @3 A+ h
  double x, y; # g# |+ F1 q4 A, [
  } vertex_t; & G4 k- [0 ?- c5 C' J( E2 L. l( S

+ i% s$ }  O9 z) a7 D6 q
* u  J% B3 w+ ]4 W2 {1 R  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:) L* s6 J9 H( y) I6 {

& Z  i- [9 }, u  h/ z以下是引用片段:$ t' C- z# O$ M+ p* Z
  /* Vertex list structure – polygon */
5 u+ J1 @( F! o& a  typedef struct - E7 `" |$ G! @( N, I  }
  {
$ t# \+ F7 {. c' P. d  int num_vertices; /* Number of vertices in list */ 0 t( O3 ]7 W7 q- f, i8 a
  vertex_t *vertex; /* Vertex array pointer */
- x7 r2 E. B/ a1 m* V  E) {- ]/ r8 E  } vertexlist_t; - x) B) |9 r- E4 h

9 D8 a8 Z& y; |3 ^6 R! f) \2 T: |4 T6 }  f
  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:% P' Q2 @( v8 [6 w
8 c6 r6 W( [8 k! h  ]/ g
以下是引用片段:5 l9 J  K3 u) e2 w9 z
  /* bounding rectangle type */ * i5 E9 j1 G. j% r# x! D
  typedef struct 9 i6 C+ s, u" e% m/ s* F
  {
+ `( y! n8 q! z+ z0 U8 d+ @  double min_x, min_y, max_x, max_y; * ^% ]7 P$ [* O' H. W2 A4 S
  } rect_t;
( e& x7 K- o! C0 r7 E" Q  /* gets extent of vertices */ 2 Q( T9 E  ?" I+ o
  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ 9 e, ?% o( o/ H0 c( K% g3 c
  rect_t* rc /* out extent*/ )
. Z2 p+ l# q: A+ H8 a; D/ z; I- p( {  { , g/ R! u' Y' J% n
  int i;
& |) L, Y. m  T: m9 z- d  if (np > 0){ 2 ]3 C1 [$ _. d, [6 b. G1 ?
  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
7 ?8 P5 @* H* \1 b+ O  }else{ 2 A( I' \# |: u/ O6 h; Q$ Z
  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
# p9 u/ Q5 t: ?$ B/ W! F8 r  }
* A6 _0 N* S. R+ T& {. F. g3 p  for(i=1; i  
8 _( u+ b/ v' M9 D  { 3 j  V( b. N! T8 t6 v& ~, A
  if(vl.x < rc->min_x) rc->min_x = vl.x; $ F" ^" _( W" l' A
  if(vl.y < rc->min_y) rc->min_y = vl.y; 9 h1 |2 B1 X4 j6 k
  if(vl.x > rc->max_x) rc->max_x = vl.x;
+ ~0 o  h9 H3 G; `( N2 C7 E  if(vl.y > rc->max_y) rc->max_y = vl.y; 6 O' A, }" @( I+ _8 S/ j
  } 3 m  _3 O; C  _' m' D  F' j7 r8 y
  }
) c( R* P4 ]: W3 P
# j4 c: F7 Y6 w& r9 E& t& M( j0 Q) V; H0 u  z" J5 P( j# v
  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。/ e/ `  J1 M) Y, B  M& _4 w; c
+ O8 w' u/ n' M9 q$ H
  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
. F- O' J+ Q# D4 Y
3 I/ P- y! s5 p  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
0 K  Z( h0 }; T3 c6 A7 o
7 d0 g) m9 U' y  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
9 x; n- b2 f' ~* F, d
' [9 \1 w# L9 K' O& m以下是引用片段:
9 T. x$ n. X1 R( [7 j% S% }  /* p, q is on the same of line l */
! W! V/ _, G- s2 P  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
- u* B' i: V' W( ]+ o! l  const vertex_t* p, 5 {/ P- K& }3 T( N1 ?5 x7 D2 [, M
  const vertex_t* q) & U3 y" f0 S+ ~9 u
  { 6 C5 O( v5 k; @4 ], I
  double dx = l_end->x - l_start->x; , D8 l. q  Z! J+ V/ Q; ]
  double dy = l_end->y - l_start->y; 0 ~9 K  x6 b' k- ]7 s& @
  double dx1= p->x - l_start->x;
* G6 c7 B4 D$ T( K, c  double dy1= p->y - l_start->y; : a! d; E$ l' S2 u) k2 s1 q
  double dx2= q->x - l_end->x;
9 y+ j8 l3 g* A  c  double dy2= q->y - l_end->y; " B: O" O1 s' _; B! B
  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
& n( e& W, [* a0 K9 _. M  }
  b5 _" c! q; ^, Q* i0 @  /* 2 line segments (s1, s2) are intersect? */
- m0 t# }0 Z4 W8 L6 Z. a  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, ) b' c. G& R/ L! R
  const vertex_t* s2_start, const vertex_t* s2_end)
) p* y0 w* K  z6 G1 k1 `  { + [3 w, j& H- ]5 s  @, P# o! r
  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && ! v+ H$ {- T( w5 r: }! N
  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; ! K' J9 B) E$ O5 L" m4 D
  } : _9 N: j6 J' |, D% C9 i! G

5 L8 d! s* F+ M% i
* ?2 g8 T9 m9 ^9 [. n5 A# ]! V  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
) B# M. m* s1 l6 N) x$ `7 E! M0 b6 |' u6 c
以下是引用片段:. @4 F) `3 O- T: V8 w! u" Y, k
  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
  t! \1 ~5 j0 w% x% {3 @( S2 M4 g  const vertex_t* v)
. v0 C$ T6 `/ z2 v) N  {
/ L" u0 S! h2 [* m# A7 @2 f, f  int i, j, k1, k2, c;
3 s$ N  q" O: m) t1 K  rect_t rc;
, A, {& _7 ?) q  J6 F/ u  vertex_t w;
; }! C3 U! `5 J  if (np < 3) * R. L+ i, u5 g% I5 ]
  return 0; 5 t" E# w/ h" K' D0 t
  vertices_get_extent(vl, np, &rc);
6 X( O' {) K( l7 Z, z" \" ~  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
# Z' _7 x; D7 O4 L* T$ b& W  return 0;
$ j( L2 p: h  ^+ q  /* Set a horizontal beam l(*v, w) from v to the ultra right */
5 z2 H  A% k8 a; B. ~' F  w.x = rc.max_x + DBL_EPSILON;
$ Z9 T0 b$ N2 r( @) K: a" Y  w.y = v->y;
; M4 D6 B3 _+ G6 E6 [  c = 0; /* Intersection points counter */ 4 ^; U" I" i+ p
  for(i=0; i  
* h8 C  b& ~' R# G( X* T  {
" ]% G$ W4 S% ^  j = (i+1) % np;
: a1 S2 d; _, b8 }% I. z0 u  if(is_intersect(vl+i, vl+j, v, &w))
& X. f$ ]( l3 e- c% J# u. H/ a  { / i4 n6 [! x  q) G+ e+ c2 G
  C++; , P  H! W0 X1 E( u, z! ]( P+ y
  } 7 o; u; Y8 U) {& t- J
  else if(vl.y==w.y) ) U3 i2 w- R9 C+ w$ B- H# d
  { : p5 D2 U. Q1 V& L8 m
  k1 = (np+i-1)%np; + n, s6 J/ \6 j& x9 |
  while(k1!=i && vl[k1].y==w.y)
8 s4 C1 V: Q: L8 G. s; }  k1 = (np+k1-1)%np;
" {- p4 U: o8 f4 \* S7 G. U2 b& Y5 N  k2 = (i+1)%np;
4 M1 y9 `, F1 t0 \! J7 @* x  while(k2!=i && vl[k2].y==w.y)
  C/ [) e4 [3 ]( o  k2 = (k2+1)%np; ( b3 l$ |1 ^: Y3 H; b$ y
  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
3 m1 m5 \4 ~0 j: c! U2 v  C++; + L6 Z# s2 O2 V# Z; {( _7 U
  if(k2 <= i)
4 W6 G$ B$ [5 k0 i) X  break; ; C3 [/ Q' M& t3 X
  i = k2;
1 u& Z3 C- l/ a' _) q  }
7 I; @& J6 t; t4 f: J0 E  } + W7 X8 j7 b% E- R( K. T# h
  return c%2; 9 V4 X- [8 n& n8 P% H) w) {/ B7 x
  }
3 t1 u5 F8 N! b5 l, `# z- D! M0 n! w: I5 \" m: w

5 s3 Q% H+ n/ G/ B0 A  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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