Board logo

标题: C语言中显示 点在多边形内 算法 [打印本页]

作者: zw2004    时间: 2008-1-21 17:20     标题: C语言中显示 点在多边形内 算法

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。- S: x. w$ L0 c' u
7 B" I. I# p3 p* N2 h7 p+ G: O
  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
0 ^  A1 u+ q( k) p9 s; ]: ?% l$ Q$ d8 P% z
  首先定义点结构如下:
  S( s. S+ F  Z  c
. E' `! q8 V- V3 f7 m3 ]以下是引用片段:
: E4 n3 a) E* ]" @# T1 X' l* }0 F1 m% b2 D  /* Vertex structure */ 8 @" ~  o( E8 e6 \! p$ j' q
  typedef struct ' T) m' x- Y( C1 O" |
  { 0 d, P9 p/ x# X( ~5 Y" L) X& V4 @+ K$ \
  double x, y; ) G4 T0 G2 U" i! a
  } vertex_t;
. Q, R  g9 w, T/ d+ C- C
7 V( t; j8 \' u8 c' a1 }$ k/ F: z5 v0 [' B8 m$ l/ a. Q+ _
  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:) \8 x/ X! s1 L3 R& A& Q+ a% G
* z, ~% v; ~7 \( O2 R: n& I5 O
以下是引用片段:
' C, ~0 ^0 ]; z! D% j& E6 f6 f8 r- l  /* Vertex list structure – polygon */ & \& t5 E3 W+ q
  typedef struct
+ s; H0 z; x% U* F: K1 `& W# D  {
( g" i/ \! P8 L$ F  int num_vertices; /* Number of vertices in list */
& x$ E6 a& I- Z; ?2 y7 A  vertex_t *vertex; /* Vertex array pointer */ * ~0 I  w8 h; ~" h# P5 x6 u' N
  } vertexlist_t; 9 M5 P: w& v% N  d) D! g( L
* X3 w0 l1 |$ l0 Q2 c: M
- W. U% |* F1 G9 c( M
  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
7 }) v; R9 _8 s/ U
% ^' s1 s5 G' ^. L  Q& y以下是引用片段:
0 Y6 M/ l6 }4 t  /* bounding rectangle type */
4 b# q+ W# q& q  h; }4 J  typedef struct
, m  ?" r. v3 H, C  {
; D  \; r/ Z: y  S8 Y$ _# ?: L  double min_x, min_y, max_x, max_y; $ X0 P: l/ L& X. ?' V6 x
  } rect_t;
( q" P. [% R5 X$ O9 M  /* gets extent of vertices */
$ w6 ]0 ]- h; T# H  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
8 D. m; v- [! _9 i0 q# y  rect_t* rc /* out extent*/ ) 5 K5 n& V: T6 g' W( ~  r* V8 k
  { ! q% i# j: l' Q4 ]  c. _
  int i; 1 I7 S$ L$ f6 D) n0 h" L% C
  if (np > 0){
% [  E  B, [/ }& O- {. q  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; ) [7 |* N% b5 q% m4 _% F8 n8 x
  }else{ " _& B; H8 z: D
  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
% U: B" Z) S5 P  e) |% [, m5 o  } / C! g- _& o  E" A: D
  for(i=1; i  
* m. t4 m" q7 Q% x6 s  {
. w, f" {3 H8 N. J  i  if(vl.x < rc->min_x) rc->min_x = vl.x; , N3 a4 c9 v8 _) i. w
  if(vl.y < rc->min_y) rc->min_y = vl.y; : d: B6 C$ [2 _! o& h# V6 y
  if(vl.x > rc->max_x) rc->max_x = vl.x;
! W2 J  Q9 R1 V' O0 K7 R  if(vl.y > rc->max_y) rc->max_y = vl.y;
: `% {* Q7 v4 x: e* w1 i  }
8 w4 d1 m- r" C  }
6 Q6 {2 [0 U  @
7 E$ `( c) x* W8 _5 V" s3 G+ }2 A7 }( O# F& X
  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。7 w4 x0 R0 x( n. L/ `& J

. {+ }9 O8 Q2 b4 C& j4 V. m" j  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:$ }8 R* L) l& t  L) P
" ?9 ~4 k8 G$ ]8 F8 }
  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
% C) r0 W' P- F; j& d: z+ ~* W
6 _, H# l8 M. y7 ~  J" k  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
1 _: D# U" Z" p4 Z8 S1 n8 C! r( u4 F
5 M2 P4 p6 K5 s0 W5 [( h2 e以下是引用片段:9 ^1 i0 g4 p5 E# H
  /* p, q is on the same of line l */ : B* [9 h: F+ p; q- F
  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
4 }3 d- N- G" G( T) s$ p; o. N  const vertex_t* p, / @/ P/ y( L) h. }8 f
  const vertex_t* q) ! _8 R" I" H7 ^% s. C
  { / f1 k4 _& D* n2 q5 _; b, `
  double dx = l_end->x - l_start->x; : M2 R- `* ?7 _0 |
  double dy = l_end->y - l_start->y; ) N4 Z2 w# |# U" \" {/ J' d* g3 ]
  double dx1= p->x - l_start->x;
% F+ i% J+ h& I6 k0 A% A  double dy1= p->y - l_start->y;
/ |+ H8 _7 @6 S  double dx2= q->x - l_end->x;
" z$ X0 |' Q/ ~# ~  double dy2= q->y - l_end->y; & {. J+ e; k( ?9 A2 M7 n- ]. c
  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); 0 [  ]0 ?8 j" Z
  }
6 T5 |) h, i+ H  /* 2 line segments (s1, s2) are intersect? */
- H1 k# ]" T0 x/ }1 [/ N7 _& J! {  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, 6 P6 T% z" z, Z, B& Q
  const vertex_t* s2_start, const vertex_t* s2_end) 7 I" o  ^- K9 E6 W+ n
  {
3 J5 b3 C3 J+ M8 g' L2 x  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
0 Y: G; H* V7 X1 Z9 ~  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; ( c5 l; ]8 W& x# j! z3 Q
  } ! p" i& p8 R" \5 p, I  ^( F

+ s* Z3 a6 W8 m9 E! U7 d: V' j: G% R: b8 o
  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:# P' ~0 I0 K, x) }: x/ u, {$ J

8 o; E$ ?3 N# {* J+ r8 F* R8 Z' i/ j以下是引用片段:. I0 d: F' g  }7 r
  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ ) R6 W" {& M# f+ Q
  const vertex_t* v)
# n; e1 @$ c( N2 {3 t  { 6 u  f, X; l& e* |/ L
  int i, j, k1, k2, c;
4 [/ {# g) D5 w- i6 b6 p" V  rect_t rc;
) h: ^! Y0 V6 l# M. y+ q' i: z3 G  vertex_t w; + `' l: ~  W& ~
  if (np < 3)
% M. r0 I& F0 U% i) i+ L6 A0 v: u! g  return 0; 8 ~3 E$ v. ~0 ^  W& H6 \5 c2 v
  vertices_get_extent(vl, np, &rc); / @8 t8 t  K- A3 W: G) ?
  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
4 S8 A5 W. w% y, F2 p; x) L$ H  return 0; " j  c# W9 R7 L0 G
  /* Set a horizontal beam l(*v, w) from v to the ultra right */ ! S, @' \) e! I+ I
  w.x = rc.max_x + DBL_EPSILON;
, @; ^: v; Z+ k8 a! R  w.y = v->y; $ Z7 U5 h. |! p7 M
  c = 0; /* Intersection points counter */
$ ?( d: T0 t6 h  u7 M. d$ W# f! M  for(i=0; i  
, z; `+ Y( c7 \; Q- q5 `; T. r  { + i; h" V& ], M: J
  j = (i+1) % np; % h, D, V* M$ v$ j1 W% X- `  f
  if(is_intersect(vl+i, vl+j, v, &w)) $ Q" F( v  t' ~8 s0 u
  {
' n( J5 G( U% J5 S0 q  C++;
$ R( {; I% h8 u( r) z5 l  } ( F4 N3 L3 b- u6 q  \& ?1 F- u
  else if(vl.y==w.y) 0 h5 ?( e+ }; @' i( x2 v
  { # o' a( D( t; _0 l) Y
  k1 = (np+i-1)%np;
) C4 Y/ G( _# `0 o. |: K. v) o  while(k1!=i && vl[k1].y==w.y)
, ~4 w2 K5 t) _0 P  k1 = (np+k1-1)%np; ( v+ E+ r$ ?- C
  k2 = (i+1)%np; 7 F& B/ f: P; @0 k9 t
  while(k2!=i && vl[k2].y==w.y)
$ P* V8 G/ ^4 h: B  k2 = (k2+1)%np;
) e6 q/ \( ~% T$ n8 Z+ F1 w/ y  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
) ~0 q* J- ^+ D2 m  C++; 2 v: a/ Q6 f8 j8 `/ s
  if(k2 <= i) 9 C& X7 C3 Q3 B% M
  break;
: x' c3 ?( W$ d$ S5 F% ~2 z6 z; z  i = k2; 7 u+ q* M( K" ~( R7 J5 m: X
  } 8 R! S# B1 l" h: h# _2 X7 |
  }
1 x, W: v# j' S$ U' W* Z  return c%2;
) F, [; t' `/ s1 U9 b  } 9 K6 Y$ U& e" N; x: `' M
4 z' y* A7 {4 ^/ l) r2 o

7 E( B  n7 F3 F! M8 E; t  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。




欢迎光临 捌玖网络工作室 (http://www.89w.org/) Powered by Discuz! 7.2