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

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
' x' i' ?6 h, w8 s3 c7 e/ n+ E. @
  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。- l* t! I, ^+ v3 ~- S# i) b
* t3 E/ Q; G* K$ Y1 x- W
  首先定义点结构如下:
' Q) `0 ^; P6 O9 v% i8 H" ^) A3 w9 I: e* F6 [7 z
以下是引用片段:
. X4 }2 k+ ^) h  /* Vertex structure */ 5 J5 ?9 a' ]" w4 s; L
  typedef struct
* l" l2 B  b: ]* p5 a4 ?1 B  {
. S9 L$ Z- p5 Q  e+ G  double x, y; 2 O8 T! p- R+ `8 K
  } vertex_t; * [3 ^' z8 Z2 D; _, I2 B8 [% j
2 H& a2 X) W! e: m7 I
1 D' J# C/ {; B4 p/ w. a- M. p
  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
0 e; }3 ?8 V6 c/ Y1 M- q  }' _6 d
1 M# A0 v: W) j以下是引用片段:
' e# S& {$ a5 u% x  /* Vertex list structure – polygon */
* V6 ^- t( G+ E8 `6 }  typedef struct . e  P2 _; d2 H( b
  {
7 M( p" A& ?" q/ P  int num_vertices; /* Number of vertices in list */
! }" u' [; D$ g/ N+ p# ]* j  vertex_t *vertex; /* Vertex array pointer */ 7 D. D6 B4 Q) r+ |
  } vertexlist_t;
( k" M' _2 k- e, y
# I) r2 ?9 ~6 `, V
2 e% G9 M# D) `. H6 z) V, K  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:  ~3 n# Q+ U2 j% v8 f
8 l4 m1 k* o9 ?  A; z, ^
以下是引用片段:  Y8 D+ L' W2 E. q- `% \
  /* bounding rectangle type */
( J- I  j) W2 k0 a" A- J  typedef struct
( {' R( B6 F% g. L$ Z  { # G* |0 a0 N# H$ d8 E  z
  double min_x, min_y, max_x, max_y;
; U. A( c4 I! T) f% M2 r* x2 g  } rect_t;
& b# s/ V) n( {1 |  /* gets extent of vertices */
# ^) S9 x) Q: {4 n2 C9 a  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
6 D$ i3 q8 W0 {, c  rect_t* rc /* out extent*/ ) $ o: _( r1 g3 y% d5 g5 \+ K
  {
' x2 b4 o5 @0 G1 _$ k8 j  int i;
! o4 a6 a5 k; i! ~% d. a0 O# s! R  if (np > 0){
6 |: _3 H9 [% J0 h3 B8 ^% s  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
$ y1 c6 k* v( ?# X8 I  }else{
! x* k( t$ w0 y9 X, c. G1 l  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
, y9 {0 ^8 K  w5 b  } 6 M. m: |' K1 T6 d% f0 z( z: e
  for(i=1; i  7 J6 G( x/ h3 w% R3 s
  { * E9 w* m9 i; z. |7 `
  if(vl.x < rc->min_x) rc->min_x = vl.x; ; J( p& a% S2 s! X; I. E+ h: G) X
  if(vl.y < rc->min_y) rc->min_y = vl.y;
- O2 t  g4 c6 L/ j8 t8 O  if(vl.x > rc->max_x) rc->max_x = vl.x;
7 B- V7 p4 i  r8 i  {  if(vl.y > rc->max_y) rc->max_y = vl.y;
2 n9 ]! b$ P1 S  } + w( u/ q$ j; @% [" V
  }
& W/ A( q$ k( c" B* }' u
8 G' J0 E/ }8 k6 t9 T
- i2 o: a) R( v! a+ w3 @) |9 A4 q  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
" \) `3 V6 ^. r8 @" V: X8 J! B" q1 i) s1 V4 [: f7 a
  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:$ f) Z" O4 U& S" }' q
6 J6 e/ o. l6 Z- k! e
  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;! W: c, g5 Y3 H

5 w4 W- `" i& T. ~. a6 R: U  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;. n/ I7 Q( l# D) W% Q6 w
  j' z% F! V  Z3 n5 V" l
以下是引用片段:
* _' I# b1 H7 y/ D9 T3 P& {  /* p, q is on the same of line l */ 0 U  C- h3 b2 G7 x
  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ - ^( v* k, w; c8 T* H! ?
  const vertex_t* p,
. Q9 O- f% A7 ~# X, \: l  const vertex_t* q) , h0 S7 b5 g4 i" d- T* V( B# n/ _
  {
3 M* F  H* f' w$ k( D  double dx = l_end->x - l_start->x; 1 u) g9 Y* s0 _* u% R
  double dy = l_end->y - l_start->y; ! l3 o6 r3 G$ G8 L2 }
  double dx1= p->x - l_start->x;
! O# r3 }$ S$ L/ ~6 L  double dy1= p->y - l_start->y;
- K5 P2 y9 I. B; Z7 d- }" s  double dx2= q->x - l_end->x; * h; h- @# C2 }! F
  double dy2= q->y - l_end->y;
' ]( A. K6 A$ G  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);   X( c3 H' w5 K" y
  }
/ Z/ j/ Y  e3 g( N+ ~/ r" h  /* 2 line segments (s1, s2) are intersect? */
  ]( j( c2 B( m- t3 A* K  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
, Y+ q  {. u7 E' J  const vertex_t* s2_start, const vertex_t* s2_end)
$ H4 `8 f6 @1 j  { : e! ?; @; Q7 S
  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
. Y- w! L( o+ D: A7 u( y* p  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
* H( K$ w4 J8 i& @: o  w& H  }
: Y% Q# Q( k: C/ c- y7 t2 x1 q1 Q: [% D/ u: A5 \: L  d

" Y3 x+ U# Z: H' |$ n  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:- a8 u& @8 I. R4 q* D1 z
% J2 n4 ^; B% H4 c' J0 z
以下是引用片段:3 @$ d5 M; `+ G6 L8 ~$ E5 s
  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ 0 M* s3 o9 J7 k* R: T0 F4 F
  const vertex_t* v) & z4 J# P- y$ f% Y8 z
  {
( ^" A" Y  h& y  q* z  int i, j, k1, k2, c;
; I- m1 K0 f: w  rect_t rc;
8 D% ~) O9 K! U( s  F  vertex_t w;
4 D8 J' Q' @7 A7 c3 @4 O$ p  if (np < 3)
1 O% J6 a6 Q% k, _! n  return 0;
7 e' N2 g0 G* I% ]: F; R0 d1 I  vertices_get_extent(vl, np, &rc);
) b% i( P6 E$ t$ U  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
( P4 X4 C7 R0 f6 ]% J# N0 B# c  return 0;
. K  u6 b/ {& ~% y- T) s* ^  /* Set a horizontal beam l(*v, w) from v to the ultra right */ ! _" D. S# x% R+ F
  w.x = rc.max_x + DBL_EPSILON; ) z- L  x: [3 J" Q/ x; }: S
  w.y = v->y;
$ |- |  k/ E# k* W+ ~: S$ c. z  c = 0; /* Intersection points counter */ 3 B9 P( w$ k  A# V. D
  for(i=0; i  
4 v/ E# b' \- u! U. ]  { - d, p  Q4 \2 z
  j = (i+1) % np;
& }9 d. e, `! Y. e  if(is_intersect(vl+i, vl+j, v, &w)) * V- @$ ]# y  L* y
  { % ]9 ]2 c* K& ~" D5 w
  C++;
9 F# x8 |6 k+ u- z  } , ~$ d; M4 t" f/ m" r$ h
  else if(vl.y==w.y) ! _# J# q" a8 Q( q
  {
. y1 D; e! G& p- P+ M! a7 _4 }  k1 = (np+i-1)%np; ( ]0 w1 ?6 `% S3 r1 L
  while(k1!=i && vl[k1].y==w.y) ; l8 W5 P* P5 R, e! v
  k1 = (np+k1-1)%np;
9 `! r+ b, N- f  \9 |  k2 = (i+1)%np;
7 `! s9 o( t% Y2 B0 x  while(k2!=i && vl[k2].y==w.y) " C8 x6 [2 M: y# b. @: p3 u* L; ~
  k2 = (k2+1)%np; + [0 M! [' V& R% e$ M' @
  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
' v4 ~$ h/ n8 p% T. d/ {  C++; 7 C/ H; h* D+ O6 v4 o1 |) {
  if(k2 <= i)
( c, F! ^) e* u# I! u  y# k  break;
: h: p9 [6 U( ~3 k1 M' L7 ^  i = k2;
* A" E" i) x) t/ Q" u6 x  }
: P. o: p5 F+ \! A6 N  } : t& Q  L# D) C6 I& V# e9 [
  return c%2; ! `3 h8 |/ E4 \0 K% ]2 f
  }
& u* {/ l0 r* i9 N. T0 @% k) h7 `3 {! |3 {- J

8 g3 `8 L: A- b! x: t3 p; i  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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