获得本站免费赞助空间请点这里
返回列表 发帖

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
( t' U+ |* q6 B1 H$ H# s2 y* A6 ?( Y1 k3 W/ j1 B
  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
. K7 s) I# W3 T, s3 z; _0 ], J0 a, k. m/ ^& f& ^
  首先定义点结构如下:
' Z0 D6 X  H( K, ~
0 q' ^* t3 d7 H. d以下是引用片段:
9 C3 q8 ]$ y; |! x  /* Vertex structure */
; y) g, K1 X$ Q, g5 f  typedef struct 8 u7 r  ]' @) O5 R( @" j* F
  {
) \3 r( E7 @% p  double x, y; ' i' i) X8 @0 l% w. [: s
  } vertex_t;
4 s9 H2 f' j; M; [8 B! s$ x5 ^
$ Y, |) N# f& v0 u; C2 ^  E5 H0 [  J# Q" `* F8 g& Y( Y: W' Q. d/ @
  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
3 _/ E/ f* A* E+ H/ a* V) L1 \
以下是引用片段:& n, v5 f0 d$ d+ N5 h7 N
  /* Vertex list structure – polygon */ 0 T7 T# q3 ~+ x! T! W
  typedef struct / x/ G7 Y, |, a1 G7 P0 T9 h; \
  {
; c- v. q. q& I5 Q$ T8 \7 U7 w  int num_vertices; /* Number of vertices in list */   s* d+ c8 r2 L
  vertex_t *vertex; /* Vertex array pointer */ , p. ^" l  w/ J- i
  } vertexlist_t; & y3 M" U: f  H/ |+ \" O2 s
/ {3 [7 |3 U+ f
& D: k# j7 ]5 B* n5 x0 {
  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:$ M4 ]- h3 w, X/ `& }9 Y8 v7 x

$ T& h# s& K1 T以下是引用片段:( ~  Z  a  C  U1 G& {- y- x# J
  /* bounding rectangle type */
, U; b" {/ ?" j. {1 b  typedef struct
5 W' ^+ |# X' K% F  { , K" g. }8 g( T# Y* ~0 z: b% M& |
  double min_x, min_y, max_x, max_y;
- |1 k5 F' C* K  } rect_t; + @+ ~: n) t' A% M4 M+ q
  /* gets extent of vertices */
$ b7 l5 y* f( b9 X3 s! R  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ 9 n! u  s0 ?# C# u; @3 Q2 Z
  rect_t* rc /* out extent*/ ) ; h7 Z  l, U# z: k1 C
  {
* {+ J! F. n  R( b" X: G9 V  int i;
" `; A0 Q( c8 h  \6 ^2 X  if (np > 0){
' i4 z) A& A8 m0 u  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
+ t# `. z8 e: J  }else{
% u6 c+ t1 s! z, s* u  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ 7 i" z2 v" U2 Q6 }5 G/ B
  }
' b' u. L: U9 f+ y  for(i=1; i  - @+ N+ d9 F7 w5 z
  {
+ e* p+ P2 x- u* m2 M; Z  if(vl.x < rc->min_x) rc->min_x = vl.x;
$ ^* D/ m" o$ B7 {  if(vl.y < rc->min_y) rc->min_y = vl.y; . y/ Q8 i2 P! A
  if(vl.x > rc->max_x) rc->max_x = vl.x; 8 l( U) M: E: y5 p5 C( |$ i. Z$ l
  if(vl.y > rc->max_y) rc->max_y = vl.y; 7 P+ S5 M3 X5 r6 @0 l
  } 0 E/ P* ?. q0 y2 b
  } ; ~. ^+ Y, V' q& l9 O3 Z. ~: k2 i
8 K: q7 L  T) j5 B  v: O+ c. |2 r
3 \- \9 P5 C4 q
  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
# S3 t" o5 Q: n8 D
* q; n1 r0 P, `. K6 d; l$ J% Y  R. I  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
; ~( K+ Q) W  q: c
3 ]3 }8 ]+ L* k/ V2 ]- ?0 U  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
$ G* O6 p4 @" L% z: L$ V' U
2 R8 Z: ^; f" ]8 o) O  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;; ^' m( }* g2 {3 K3 k$ X

9 L3 A" q' S# ]) b) Z& ~- ]以下是引用片段:
* ?' v* S$ x) I8 ]$ o0 T  /* p, q is on the same of line l */
7 N" S( s/ A: w/ }  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ 2 F. [- T8 `* H
  const vertex_t* p,
4 ?! r7 V  C+ z6 b/ u# l  const vertex_t* q)
+ z! @9 c9 c) P' M1 R' [2 b  {
4 n# f3 T" b5 c* _% k/ K! P  double dx = l_end->x - l_start->x;
, @- g0 X* W2 j# S" Y- K' Z3 w  double dy = l_end->y - l_start->y;
# k( F0 H& Y# X  double dx1= p->x - l_start->x; 6 R+ B: x8 l: w& q& _2 [8 y) w
  double dy1= p->y - l_start->y; ' `) s  `3 Y5 S% Z: b6 o
  double dx2= q->x - l_end->x;
1 c' v; q$ {3 R, ]5 p  double dy2= q->y - l_end->y;
" }( |1 W" c6 Q  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
; F/ [4 W/ b* f  } & v, Y# C2 `% @- Z# i3 D6 m% {
  /* 2 line segments (s1, s2) are intersect? */ 6 H/ u& i8 z. i" j6 X
  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
3 w# p, Q4 Y) ~; h6 b  const vertex_t* s2_start, const vertex_t* s2_end)
# h  P! a) @! N& ?  {
; `. L: L$ J8 W4 H* D5 Y3 [  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && ; T% G9 b' H% A, \
  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
/ X$ F% b4 m- A3 i- x  }
/ z( I: H4 ~0 m4 @* I2 R$ j3 ^* O# c, K1 G

' x# J( B( s$ l- l, D+ n4 w" K9 ?  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
3 ~. j5 p. P+ a8 ~! f7 s9 F2 I* N0 |4 s7 k0 ]3 ]
以下是引用片段:
" C4 I' X& R* T4 x  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ 6 g$ |: q8 S: [, m' L& m' [
  const vertex_t* v) . W/ m) E" m: U' C- K3 H% I0 e, b9 Y
  { 4 n1 M1 o/ b% e1 k! n; A9 |9 O
  int i, j, k1, k2, c; , R1 m$ {: q$ x) b, x# K; ~* X! x, Y
  rect_t rc; ; Y$ P7 |5 u$ m4 h$ K+ h
  vertex_t w;
* Q! Z7 Z# k' c: d& ]% Z  if (np < 3) ! T5 a+ r. w1 s; X5 p
  return 0;
2 ~& p# A) i; t$ c  vertices_get_extent(vl, np, &rc);
. f' I% B9 Y, ~5 b/ X& B: s8 Q$ _% ?  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) ; x( w& q/ T& `5 P- u. h
  return 0; # @& u. r5 d0 b/ J9 z( U7 u
  /* Set a horizontal beam l(*v, w) from v to the ultra right */
, k; i2 H9 u+ h" C$ x9 ?/ x8 N3 z  w.x = rc.max_x + DBL_EPSILON; , z8 W2 ^1 \7 W1 V
  w.y = v->y; ! O2 s- R3 b0 c/ k7 |  Q$ q6 D: r
  c = 0; /* Intersection points counter */
* y0 F2 o, L  M% I+ Q4 Q2 x" X: i$ s  for(i=0; i  9 e# l2 `- e1 `" T7 G( G% M
  {
- ?+ M3 U: j# L/ l  j = (i+1) % np; . A$ n: b& Y+ ?
  if(is_intersect(vl+i, vl+j, v, &w))
5 k9 O( C0 d3 |! J# J8 J  {
8 q% e, u& Z" I- R3 ]: J  C++; 0 ^/ [6 j6 ~! ^2 {5 }
  }
( i( q& k, @- |/ J" A" L% P  else if(vl.y==w.y) 5 e: d9 [% k  ?6 O
  {
+ ~- v& Z# s" l4 {  k1 = (np+i-1)%np;
1 b; z* z/ N! k2 E' O# {1 P; D' \9 ?  while(k1!=i && vl[k1].y==w.y) 9 l9 v- x( i% \( x9 i" X2 z, y
  k1 = (np+k1-1)%np; / Y- H1 B  ~: E3 d
  k2 = (i+1)%np;
0 m1 ^- e0 }: v  L0 [+ |, W6 B  while(k2!=i && vl[k2].y==w.y) $ ^7 V1 P! N9 z+ F# l/ K
  k2 = (k2+1)%np;
. A+ _! }$ G( n! V  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) $ Z) X( q9 Q2 [- x: o; }
  C++;
/ M' t) U; C- g- _- f  if(k2 <= i) 2 k9 e9 g; P* J( z
  break; ( r, g% |! j$ X( g& x* X
  i = k2;
3 w; ^1 B. R8 @# R. n1 E  }
  M, g' m8 ?6 e" g$ l  m  } % o( h( l0 c+ Z, w! D
  return c%2; # L4 |' S* S8 S& t! f6 {/ p
  }
& e; P( r4 Z8 X+ v$ y1 H/ U# i8 k9 m

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

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