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

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。; @: z4 e: s; b) l' K
) i4 O7 C- g- N5 E, y: ?+ H2 O
  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。4 a2 h  T2 p  Y7 T) x

3 p" a7 R  K5 S* ?8 a# W: B  首先定义点结构如下:/ a% }- K! E2 E- P2 V; S

% `; s( f3 {/ D  r. n4 _以下是引用片段:- q+ }8 Z8 w7 R5 |9 A$ R
  /* Vertex structure */   B- d4 h/ H2 y6 A! S' Y
  typedef struct 4 V  k( E; o1 U  ]$ v
  { % L! s; w0 z+ R  U% Y9 N* F
  double x, y; 0 s- ?% T  h  @: y0 f% J3 {
  } vertex_t;
2 @; U$ N( S2 }& p( b# z
# @) b( u5 ]) s5 c. ~8 G
) ~/ m3 A3 D3 m5 k' |  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
( m1 T) K0 @; |/ J, D- s) v, P: V! B
以下是引用片段:& W! \. O1 \6 h
  /* Vertex list structure – polygon */
- B: @& t+ J' N- F9 q3 G/ t  typedef struct
1 z* K: d4 Y! j+ |. Z  {
, _( ]6 f7 l4 Q. p# X1 }  int num_vertices; /* Number of vertices in list */ ( ^& ~% L1 Q! ?8 Z) b# F" m9 Q
  vertex_t *vertex; /* Vertex array pointer */
! ]. T7 [; A9 o( _* t) U4 A$ ]/ F* b/ A  M  } vertexlist_t;
! p/ d3 J  |& W5 v0 l* t
( ~0 w. W$ p" v' i8 h
7 ]  E6 Y! T) i" z' a  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:8 u( ?. |5 u. ]$ m7 q8 p
  q' n- j) ^8 y1 ^
以下是引用片段:% M  H0 t3 [# [" r8 }$ A
  /* bounding rectangle type */
' q' p+ r: n; ^' f* W! z  typedef struct
/ r: J  ]  |0 P( g/ F; L1 E  { % W7 g1 p* X/ W- V" f
  double min_x, min_y, max_x, max_y;
# a4 `. ]" t& m, ^  } rect_t;
* F1 ?4 m7 z9 D  /* gets extent of vertices */
: ?+ ]; w, I, f$ u0 |  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ / H# C$ h0 P5 K9 ]* a# b
  rect_t* rc /* out extent*/ ) " O' E: n+ A1 _! ?  ]7 ^* H
  { 2 ^1 i8 J9 i! b' F
  int i; 0 j6 d$ v8 r" A
  if (np > 0){ " d8 j0 O. {* Z) K5 h4 j- I, m9 P
  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; + |+ d  n. \' J& M: I# N1 Y
  }else{ # x1 T. L+ P+ U) \& D, O
  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ . H+ k6 _$ L) f! F# s) a
  }
5 {) Q2 E8 _" r$ Y& \  for(i=1; i  
! }$ U3 l+ v0 s9 e  {   Y0 [! Q3 D- Z  }! @& ^3 c
  if(vl.x < rc->min_x) rc->min_x = vl.x;
: h8 U! R5 u) G% K4 B, B+ Q' ~  if(vl.y < rc->min_y) rc->min_y = vl.y; & _! d! Y4 Z, S" F. z0 F9 a
  if(vl.x > rc->max_x) rc->max_x = vl.x; : d( P% y, E) n# Y" f' g# T
  if(vl.y > rc->max_y) rc->max_y = vl.y; 5 S2 K1 n6 H# c+ D6 D' ^0 k9 d% j
  }
3 ~* w( _0 ]/ @8 i  }
5 a9 H5 u& p/ n- I6 M# ^, G" {: b; X' M! I) m
/ F& P5 D- G' r" B3 A6 Y3 K
  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。" e4 o& @' y1 w2 l6 T! S

7 h: F+ r; k7 v  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:' \+ {" j0 M( {  V+ [8 i; v
7 G4 c) }$ X+ K- p# |
  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
6 k8 A- f+ q( E; R& }* E) K
& e- m# m( t% X+ a2 R: ^% {  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
: k* t1 b, G: m6 K6 D5 q" _1 v, \3 H8 `: Z& q  M# @: F
以下是引用片段:
, e1 I' b; R# T, G4 T- p' w& l& e( N  /* p, q is on the same of line l */
. W3 a/ Y$ y( c  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
+ U) E" U2 r, r  const vertex_t* p,
  J( L: \$ z, w  z' H  R( i/ X  const vertex_t* q)
, k$ `% U) Q9 t% u- F  {
0 q! S' a  Q8 R. M  double dx = l_end->x - l_start->x; % P" x; n0 s% v1 \* l8 B7 h
  double dy = l_end->y - l_start->y;
. V& n: X$ i/ I  double dx1= p->x - l_start->x;
) m+ e& S8 h+ u# I  double dy1= p->y - l_start->y;
+ o6 l7 t& a/ U% ]8 ]" ^  double dx2= q->x - l_end->x;   U0 W9 h9 L- B$ U& c2 R3 F
  double dy2= q->y - l_end->y; + ]2 ^( y' b/ B* P; ?1 V  g
  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); ( u. O3 f. X6 [6 m! o
  } " q  l) c  z) b
  /* 2 line segments (s1, s2) are intersect? */
  m  m. I5 F- B( R' u  b  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
- Q7 e  Q5 i  {; j" {) {1 A# `9 r  const vertex_t* s2_start, const vertex_t* s2_end) " r/ H: `& O# n. {
  {
5 G" x& U- W: ]  ]  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && % K3 P& Y1 }4 I; T3 @" t0 x
  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; " _, v: |  \# ]2 ]4 t0 h$ R) _
  }
4 Z; ?7 {8 Q7 D6 d  h! D4 I! M! a
- b& Z0 _: D" m- n" Z' O  }. e. k
  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:* L  R9 u+ d7 v' k+ _7 U. u

+ D3 O0 A* a- S: \& D- i" r5 W以下是引用片段:
7 D0 s9 L4 i; U+ d; y4 q6 x  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ $ Y  T1 Q1 F) Z. [' Y! h) x4 {% h
  const vertex_t* v)
$ @, U: c4 q! y* M3 t  { # S7 |' K$ X* L2 q" r8 C
  int i, j, k1, k2, c; 5 X1 v- ^1 E) u& g2 l& S" ?
  rect_t rc; ; S3 U1 v- {2 K
  vertex_t w;
& n# h1 s* ^8 S$ w  if (np < 3)
( \7 W  W8 I' y& j' S5 _& h) Y- u  return 0; . ?+ `6 b  W8 m  R. W' z
  vertices_get_extent(vl, np, &rc);
1 @3 e: k7 d$ l/ ]9 R% p  v  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) * w: c3 f: c' s- ]. ~7 }9 _
  return 0; 9 N* Q4 q! Z, I7 P  z" q- W$ J
  /* Set a horizontal beam l(*v, w) from v to the ultra right */ * d$ c3 w7 }" r0 Y3 K9 z) i
  w.x = rc.max_x + DBL_EPSILON;
& K/ y/ O* a" q: l4 X  w.y = v->y;
8 m! Q: V2 O6 R9 y0 A, x  c = 0; /* Intersection points counter */
- {# o# Z$ e2 o/ ?  for(i=0; i  
7 Q, ^" V1 P( _0 u' G( U8 q& g' I9 [  { " L. j  @1 u0 p8 R
  j = (i+1) % np;
, B) n- W6 Z! n$ A: R  if(is_intersect(vl+i, vl+j, v, &w))
/ }" t4 P8 u  h! r* F  { 7 G: H9 p0 }+ `1 m0 G- e
  C++; 5 J3 W# f! |) w7 o
  }
3 c: ~3 ^+ K0 T0 b3 F  else if(vl.y==w.y) 1 l7 j) ]6 z6 P; M
  {
4 d# H) q7 n! j4 F; `  k1 = (np+i-1)%np; 7 ?$ L( W! u2 z% K, |' u, x
  while(k1!=i && vl[k1].y==w.y)
& b1 Z0 |2 p. ]  k1 = (np+k1-1)%np;
  @, I3 _$ w9 G  k2 = (i+1)%np; 6 m; N: H6 k: O) ?
  while(k2!=i && vl[k2].y==w.y)
% N) h5 m& U, d" X' c* O. D  k2 = (k2+1)%np;
# [# p8 N' u9 x  U0 Y  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
$ E" V; O* t8 r0 C4 A4 \( Q) D6 I  C++;
3 l: h5 e5 V5 r  if(k2 <= i) " x$ _1 y) ?! Z3 [& `, |5 g
  break; . @. I1 }8 U% W- V' S( s
  i = k2;
! k2 p- F: H1 M9 N5 Y" P! ^  }
2 ^( L) a3 }- P5 M  } . X6 f$ _5 o% w# D- o* U
  return c%2; ( e8 n4 Y7 c* o) p4 p: Q! Z8 ~
  }
8 V% W% E+ c8 D: t5 v! Y+ y
* }- [( \6 y! W/ E/ V, w  F$ r4 y9 f$ o7 `
  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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