Board logo

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

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。& b, }0 ^" ]- Q% W8 c2 i
4 A& i# M# {+ d
  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
0 _" O0 C0 H2 F8 F
& T$ t9 [. v3 v- k  p" b  首先定义点结构如下:
/ q) G1 y, Y7 D3 ~$ u$ `0 u  P; R# l! E
以下是引用片段:
0 N& n$ ~1 L& w1 s, u- b  /* Vertex structure */ # H5 |! I5 A+ `
  typedef struct ) p0 [. v* ]  \* B; d  l
  {
/ G" m) d# _" g- f* o' B( Q. A7 d  double x, y; ( p6 D+ d6 q3 k& _( S
  } vertex_t; ' W! X) D. U( z/ V
- S/ z. `8 M+ B6 f& [+ }

+ A& }# d3 p+ L" Z  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
) e: r8 M& c" d/ A3 [1 _+ q) Z0 O1 M+ y: f9 ?2 o2 [
以下是引用片段:0 X- P8 p6 V7 _) b
  /* Vertex list structure – polygon */
, `: j/ _4 J5 f/ T# l5 `3 e, m  typedef struct
' U7 v& Q6 K8 S# F  {
# w& ?7 j; D4 }. _& ^( P9 B: C+ G  int num_vertices; /* Number of vertices in list */ 5 ~* ]9 d4 D: ~% e9 z/ ]! N$ L( e, W
  vertex_t *vertex; /* Vertex array pointer */
8 ]' Z3 s1 t" S8 l* J  } vertexlist_t;
7 D2 R) I3 d* r9 _# C$ Q# |3 H
* g, U$ n2 z) Q! z! ^
+ J0 t% O: P8 b+ S6 V7 T% }  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:( q+ F: H5 r- Y% s: n
6 }" b" V3 K+ |- n. q5 N
以下是引用片段:
, `4 ]0 t# O4 c, B& z3 V! Z# d  /* bounding rectangle type */
; P5 q. N5 c$ u  typedef struct
- ?4 f" h+ r# Z6 w( t  { & z( k' Y+ D! D  j( i
  double min_x, min_y, max_x, max_y; 3 k6 Y6 b# u0 d
  } rect_t;
: |: S# d* N' `4 m; T  /* gets extent of vertices */
/ u: v* \' I' t; u8 d  f8 f  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ / L* x$ ^7 A8 t
  rect_t* rc /* out extent*/ ) * ?8 d& q# }$ j) G1 c- r& o" Z
  { 7 r$ o* ~; Y( ~/ m
  int i; , Z7 L: P# v& g, @% ]* W$ r$ t
  if (np > 0){ # i- a' W1 z6 _
  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; % _$ V8 W: y3 ]6 ~, Y; k( r% y
  }else{ - f7 m5 Y4 p, L/ B' ~( t
  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ 9 K1 _& Y% w( y  g( B
  } 4 ~1 X* @8 e9 A+ j3 i
  for(i=1; i  + \* R+ E+ a5 h' H
  {
4 E0 X0 |1 D7 ]/ l  if(vl.x < rc->min_x) rc->min_x = vl.x; 5 \6 m# p0 O$ W; S
  if(vl.y < rc->min_y) rc->min_y = vl.y; : F' h' p9 L$ r/ i' M( J8 D
  if(vl.x > rc->max_x) rc->max_x = vl.x;
0 P/ E! l& B0 s6 U1 }* ]& c  if(vl.y > rc->max_y) rc->max_y = vl.y; ; i8 j0 D6 d, x7 X. r! D
  }
/ f# z5 Q! v  A  }   A) F1 }- k2 X: d2 j5 E; G
( G+ y, n* {, U& F
6 T+ ~* p( N7 E
  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
5 i) R4 ~/ M/ t9 G) a6 |0 ]& S/ q  R( \
  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:& S: c( \6 v" v3 n9 i

- S) z* n, ?+ w7 }* ^$ J$ F  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
  x. u2 e2 L/ |& F! e& L
% g0 D# _, Q- C; W6 B  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
) {5 T$ `3 c0 o5 J0 o- x
" L4 }. W# R# X$ K  X9 q1 L" Z以下是引用片段:
9 M; d+ {" g7 c# }  /* p, q is on the same of line l */
- C( y) g+ i+ X  u  a" o: h  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ 2 {8 o9 I! @4 h1 Y  d
  const vertex_t* p, 3 h$ R. m' J# _% G
  const vertex_t* q) % D4 C0 K  J8 P# i/ o. ?  C9 w
  {
% [/ H% |' l% t' s$ H  double dx = l_end->x - l_start->x; 7 J8 z8 x/ v* h8 M
  double dy = l_end->y - l_start->y;
* _) R5 q" Y7 L. c  double dx1= p->x - l_start->x;
2 a& h% f) V  `/ X. _/ W# u  double dy1= p->y - l_start->y; . j' F) w% R4 k) J* T2 c3 ?
  double dx2= q->x - l_end->x;
5 b" e  h8 \: r: o; X2 Y  double dy2= q->y - l_end->y;
+ j6 S# W+ S" U0 O. ~' `- T  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); / s3 {6 F0 G; J* }9 @1 ]. C: Z
  } + L9 \: L; ]" x0 g' h
  /* 2 line segments (s1, s2) are intersect? */
4 {/ i% W& w' @, S1 o  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,   T! c' E/ B7 ^  }# s1 g, Y
  const vertex_t* s2_start, const vertex_t* s2_end)
  M4 h, L+ F$ I6 W! t7 [/ I9 d  {
5 E% A2 }6 Z. I2 p5 {  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
* P: Z1 J/ h  z1 {- `+ c  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
: ~, w! l( E' _  }
1 l0 N$ _7 q+ K
+ _' ^* z8 r) k2 n- T* M0 j8 m$ E
  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
% Z) h4 ^( ^* t1 C2 }/ Q% [) w( |
以下是引用片段:. x1 S) Z) t" d7 b0 u
  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ ! W- Z5 W5 p2 U
  const vertex_t* v) ) H$ Q$ [" x+ m1 O( ^2 \
  {
# F3 m3 ?& A6 ~9 E. I# h0 y; L  int i, j, k1, k2, c;
7 K6 @0 E- ?. i- d$ b5 X( F9 F+ k  rect_t rc; , }8 u- Y9 T4 [) V
  vertex_t w; ! _8 Q5 A9 n5 I/ y, p& v* Z; p1 L
  if (np < 3)
. ~1 ^" F9 \6 ^" d  return 0; & p% t# L8 I7 V- H9 I- ^! e3 t
  vertices_get_extent(vl, np, &rc); 4 ?8 A4 x+ e$ ^
  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) * j9 J, O; W% R
  return 0;
# F6 X7 L) N! k1 r2 M, N  /* Set a horizontal beam l(*v, w) from v to the ultra right */
* H; E! M5 u' E9 C0 l  w.x = rc.max_x + DBL_EPSILON; : D* g; x2 R; w# l9 I
  w.y = v->y; 6 `' C! T: \% e1 }( |
  c = 0; /* Intersection points counter */ ; @6 x* I% Q7 U4 ^
  for(i=0; i  
. H) s  d2 c9 W1 B3 d9 `  { - c) H$ v( N; B
  j = (i+1) % np; * }) x( r$ {; K6 o
  if(is_intersect(vl+i, vl+j, v, &w))
2 F& a6 d6 w2 G" o5 J( E: |$ g7 X  { ; E( Q$ f3 H5 c, n1 _$ V
  C++; 1 N) Y8 `- B$ w# P+ b* w# N: R' ~
  }
5 P# p1 N' I# x9 P5 X  else if(vl.y==w.y)
9 C1 n6 p4 Z/ p3 B% u9 T. F3 q  {
" D$ [5 z. I2 o7 f) q& v8 [  k1 = (np+i-1)%np;
1 z4 B6 l$ ]$ n( A, _  while(k1!=i && vl[k1].y==w.y)
1 _; D7 }8 Y& w6 a  k1 = (np+k1-1)%np; $ P& h' {+ b) y1 o5 H9 X
  k2 = (i+1)%np;
) [9 `' a$ Q/ ~  while(k2!=i && vl[k2].y==w.y)
: M) I4 p% X. }& J4 b  k2 = (k2+1)%np; % v, w! [3 e- e" f, }
  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) : l( I7 W9 Z" t
  C++;
  s% J# d1 t0 D6 ~  if(k2 <= i) + k, F+ b7 o. n3 k, O8 [
  break;   ^8 A$ L" J, l$ }! M( |1 ^
  i = k2;
3 q: H2 W; d3 Q  q) u1 @  } " N8 b3 h  q9 w3 ~8 B) I
  }
0 Y3 l& ~+ ~& W" K: M  return c%2;
3 `, `  Z" i7 D1 d3 F1 v/ K  } ' u6 a$ r; r0 N

# G& M( y# o& o/ j4 Q0 c5 v6 h
3 r" g& F2 |" h( ]' h5 Q  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。




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