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

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。9 o4 p) l7 G6 l4 c
! z; y' V* t1 M" T9 t1 v1 F; s
  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
) `) U" Y* g. j; e; I: v
9 C8 K5 ~. @- F* G& d& L$ w; l1 ]  首先定义点结构如下:
6 W% T' N7 a6 O$ F3 X9 `( C' Y' w' |$ @# j; Q
以下是引用片段:
9 k9 [+ }$ Z( {3 U; @- b* m  /* Vertex structure */
& f0 i7 D9 ^) B  G( u; I  typedef struct , f; {; x: H7 w. ]2 Y
  { , e$ b0 i  N5 S! m* a. H9 k; P
  double x, y;
/ t0 @$ f- G$ B" W  M  } vertex_t; 8 `- K) H& ^% V) H

* N  b* q: G; Z7 R
5 n- ^+ a+ E; R, i& z& b( P  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
. k( m6 e% x) b/ Z0 Q: |! s' e7 R2 i# c3 l5 [
以下是引用片段:
0 d4 `/ I' U* {  /* Vertex list structure – polygon */ ; i+ u* I. D# t. U7 p
  typedef struct & Q/ ^/ X, I3 ]; z# D9 P9 ^9 X
  { & `6 y8 k( F8 o4 b4 [0 W
  int num_vertices; /* Number of vertices in list */
0 F, }, O# N" X% c& S  vertex_t *vertex; /* Vertex array pointer */
) F* S$ {8 @5 `$ y+ }% I  } vertexlist_t; : U  b7 C  `/ ^
$ J7 o0 t; L8 M* Q" `. S2 M
# l( Y3 K0 v" V) X' F
  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
7 ^. }% R: C' ~
8 ?+ S: c& f- V0 ~) L8 A以下是引用片段:
+ n" G1 P: A7 h  _7 i) |  /* bounding rectangle type */ . J: V* w: m% w
  typedef struct
( y9 V! E5 [6 E  { # F$ D5 i: x1 o3 D8 S4 m: j- d
  double min_x, min_y, max_x, max_y;
. m+ i6 Y. K4 C8 }& J  } rect_t;
. k( N3 V: I3 A3 J+ ?! h  /* gets extent of vertices */
& d. U! m, g! t4 z2 W: I9 [' v  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
5 Z4 ?! b# n% n. c+ Q; R  rect_t* rc /* out extent*/ )
+ b, _0 N: t( j  { 0 O1 c% l( {! b: \
  int i; 1 q2 B* I/ A+ e* A+ O4 s1 f
  if (np > 0){
) Z! ?* F0 r* Y* \2 X/ H- C% E7 d  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; # K! B/ z0 g/ a) ~" B8 e+ }% Y
  }else{
) T6 O$ K1 ?! I7 o$ {$ U. g9 j2 {% `  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ " T# J/ h  o' j9 M2 I$ }& P
  } 5 ~9 X2 ~- e9 e* O9 l
  for(i=1; i  
, ^3 K8 \/ P' X% V1 v) {  {
2 V1 }8 W% O- A+ m  if(vl.x < rc->min_x) rc->min_x = vl.x;
4 o. q) K: M: D  x. a1 @8 B0 u  if(vl.y < rc->min_y) rc->min_y = vl.y;
; }  K' h3 r7 B" G6 Q6 ?% D  if(vl.x > rc->max_x) rc->max_x = vl.x;
0 I& c& w: F( K6 P# V; z6 a  if(vl.y > rc->max_y) rc->max_y = vl.y;
! p- j* o$ C) _# n  }
0 L) _0 n$ n4 s0 F! j  }
5 r9 a8 n& B# A9 r1 W5 D; ~' _, Z5 v8 `& d: |

' G# b3 L4 t# q$ `5 J4 \" N) w2 i  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
* C! O5 \7 v% O' ~& j. a2 O% n* |5 n! M
  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
1 l% S: F0 Z, k+ y. C7 J. E( H/ p& w  l) n: m& t: d& c
  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;/ F) e/ E6 U, \0 w( S
$ V) ^, Z6 C) r2 W# j' J
  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
' y, [1 q1 D1 _. W
( I4 I8 H5 k0 P" L: v以下是引用片段:. H' I& ^! j/ W" V* A, L
  /* p, q is on the same of line l */
* U" \5 `( S4 Z# L, |/ R  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
0 d8 L5 ]2 E6 j  const vertex_t* p,
2 |- h, U. u2 X! x( u  const vertex_t* q)
; ^( S8 ^9 z! w, _9 [/ p  { 0 C& P+ q+ D3 u. I' u2 j
  double dx = l_end->x - l_start->x; ( H8 A0 d# \% }1 ~1 b' r0 h
  double dy = l_end->y - l_start->y;
' }, H! Z/ E# J5 ^1 S3 o0 s4 Q  double dx1= p->x - l_start->x; ! B$ r$ B0 v6 r
  double dy1= p->y - l_start->y; + o4 u( y7 O' ~: [+ Z% b& p: f
  double dx2= q->x - l_end->x; : E% Q, h" o  ~% n( V. V* A
  double dy2= q->y - l_end->y;   [( V! p% G7 s: o$ @, N- _
  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
; u0 N- f& W& o( x" n9 u4 Z  } * x4 t. l: N1 E, x& Z
  /* 2 line segments (s1, s2) are intersect? */
$ {' a& p. C1 ~; t; D, g6 y  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
9 Z, p+ a+ q0 T$ E2 q. f4 c  const vertex_t* s2_start, const vertex_t* s2_end) 7 ~4 s9 S3 r- r- X# E  @+ ~" Y0 u
  {
- L# y% k  P$ h6 ?3 I; s, H* e- B  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
. W4 N: j3 L% G) U  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
5 X' q( m: Y* f" p0 z- v) s! E0 `. l! F  } 3 ^6 Y2 e: o9 U. A- T. d

2 `) m+ K- d% t8 {; C! C0 @$ O. u! @, _8 |( {: @0 n; d  D
  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
& J3 K% }- D- W& Y+ W
% y' X* y9 m/ h1 T) G以下是引用片段:. J& x2 e4 _$ w
  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
0 U2 a8 |0 S6 D  const vertex_t* v) ( [7 b/ E$ q. ~6 Z/ T6 ^
  {
" y, v* A0 Z) G) v4 @; [  int i, j, k1, k2, c;
2 i0 `- Z% j1 T; u) T9 w  rect_t rc;
, f+ r- I, q2 n  vertex_t w;
. z; O: L- ~! o  if (np < 3) + M% s, H( g$ n2 @* D& e+ T
  return 0; , w5 g9 C7 |! ?3 \3 q3 n! }
  vertices_get_extent(vl, np, &rc); ( O7 ]2 p+ _% b1 k! L9 x$ [
  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
' n0 r8 b  C& ~1 c1 J  return 0; 2 o* n% W+ u8 s: W
  /* Set a horizontal beam l(*v, w) from v to the ultra right */
9 b3 `$ Q; X3 F* J  w.x = rc.max_x + DBL_EPSILON;
) \8 I. a$ _9 \# E+ R' N& j  w.y = v->y;
2 H: H% o* t& W) v7 r  c = 0; /* Intersection points counter */ $ [& T5 G2 M( p, \: |* T4 A- R
  for(i=0; i  
' O$ M! }* l9 C  { & r7 X* v, K! F9 m) B. Z
  j = (i+1) % np; - A7 }0 O9 X0 {* S9 u/ z
  if(is_intersect(vl+i, vl+j, v, &w))
# L" E0 l, O5 H9 W. ^  {
) ?  O/ Z/ B* Z  A/ V* ~  C++; . q# X) g! N8 J) Y0 ?
  }
2 M+ _7 W. D6 m9 a  B  else if(vl.y==w.y)
7 [" T3 w& m( v; g; N$ @  {
: Z8 y, o4 N- _$ Q  k1 = (np+i-1)%np; 7 P- h' {% e4 T; W  @
  while(k1!=i && vl[k1].y==w.y) / Q% s  d  k& _3 c" ~5 [
  k1 = (np+k1-1)%np;
# ~+ A- C7 e$ G# c7 _' ?8 L) N  k2 = (i+1)%np;
7 }* ~5 r. }9 O9 A  i  while(k2!=i && vl[k2].y==w.y)
& U2 w, I3 Y+ {# z0 I$ I  k2 = (k2+1)%np;
" r& A" J* `5 S+ V* s' u* {3 w  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
4 {' G. m0 O  r7 _! Y/ G) h9 ]5 f) c$ b  C++;
. w$ L( i1 T1 G& Q4 P& j' t  if(k2 <= i) ' w% T* P; q% o2 l/ p
  break; " }& ?% h1 p( g' k# O' v1 f9 k
  i = k2;
* w: c* @9 j  l0 x! w# E  L  } ; P# ~$ c2 m5 m  _" ]
  }
3 @! m  t4 R6 n' K8 b/ j  return c%2; 3 f; J9 E9 o! P+ L( N
  } 5 U! s1 m- A4 a) m1 t  O

, i9 w) D) T& t2 [7 ^
6 g2 b+ y. n2 X4 ^) i. w: u8 C  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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