返回列表 发帖

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
# J! J1 J& C) t9 p# G+ C/ R2 P. y5 {( V" K" o
  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
$ _8 ^8 F- d4 h- V6 o" L8 B. b4 O. [+ A
  首先定义点结构如下:
7 W6 _: ~. w  U& y8 N  @
  V' D9 U" L+ n6 Z以下是引用片段:
' \! Z4 E# a: G. h2 o2 ^. J9 F5 N0 x  /* Vertex structure */
& ?- \2 V, r- u  typedef struct % y2 ^8 n% K9 ^4 z8 v/ |$ t
  {
7 e! b& w3 h- Z4 E5 J0 |  double x, y; 9 h, S0 d  q2 D  a) V: {% \
  } vertex_t;
& h& h1 i9 }# B# s% {) m# o1 j' U
! K- ?! s! F) a& P* T$ x8 D
1 b4 U4 z7 J* C% ^  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
" y0 d1 k" |8 b9 a' z3 n+ T" ~" u* p% m0 @. K+ a
以下是引用片段:; u+ M/ `/ [2 [2 u7 }5 h
  /* Vertex list structure – polygon */
& i0 s1 V+ C: C+ A' m; U  typedef struct . g2 O2 q# k# u! J3 ^8 S# `9 W
  { ! r% m& r5 U- y
  int num_vertices; /* Number of vertices in list */
- }. ]; F+ r9 S3 {  I2 |7 v  vertex_t *vertex; /* Vertex array pointer */ 4 j" ~( o) j( [! w4 U5 ~
  } vertexlist_t;
6 }$ x! S5 L: P$ t: ]5 F4 h) e% q6 `( ~, b% [/ r
% a8 o, A' k+ U
  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:2 o* I: [' w4 q" f$ R- W

8 F; d6 D& F; e( c以下是引用片段:
. P' A% a" f* C) t4 I  /* bounding rectangle type */
3 q% r9 O7 z( a/ H) K) k  typedef struct
/ U+ S5 _) c7 m  { 8 a( @, z/ C" {1 Y1 m3 }9 G; D+ l" u
  double min_x, min_y, max_x, max_y;
+ R" [4 l: L: H# j  } rect_t; 7 e8 c! c  w$ z8 {1 M% |
  /* gets extent of vertices */ " Z& r$ l" x3 h. m4 v
  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
( c& w7 w: n% X- N8 N9 T  rect_t* rc /* out extent*/ ) " ^& P( V7 I8 O
  {
3 M  E, y' u+ w  O  int i; 1 J7 |9 T$ @+ u' f, t
  if (np > 0){
$ G/ _8 V' F- l# I; Y0 \. q% @  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
; p) [6 v8 B7 ^" Y6 \  }else{
& |3 D% }2 G7 o: {4 @8 M- r  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ 6 q2 Y) f; w6 l* ?$ `5 _* b- x1 `0 J
  }
- l' a4 c3 ~) p9 s+ N" T+ c  for(i=1; i  
2 P, F% _+ P( R/ S" Q2 ?" O  {
% l( f2 L. R( F* V5 z; \. E& C; z  if(vl.x < rc->min_x) rc->min_x = vl.x;
" C* D# E7 ]. g( N* N  if(vl.y < rc->min_y) rc->min_y = vl.y;
4 y) @$ p6 @3 N4 t3 J  if(vl.x > rc->max_x) rc->max_x = vl.x;
& v* s  u; i# T& u/ c- N  if(vl.y > rc->max_y) rc->max_y = vl.y; 1 s7 Z, d# R6 p
  }   r% G+ |2 x$ v) I
  }
  k, ]; y0 F& r0 X( Q7 c! b. j6 V+ K. a/ E' }3 x: `* q
5 V) Z0 o7 P- M
  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
$ y- H& s  y5 m) Y# x
% q( T/ ]+ T; L/ O. F/ _5 j# _2 z5 e  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:# }! w* F! K. ?& F# S, s2 z/ |5 r
' I& S8 G. B  o; [/ k9 i5 M
  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
# y% m1 g3 o* ^5 L1 F4 q' o  e* G% _, M, g
  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
! z0 l8 Q' l; B
* m* k- ~. G3 {2 O9 x# \. A6 @; R以下是引用片段:9 A2 o! O. p& U
  /* p, q is on the same of line l */ ( ^- U& n) ~( |( D5 A
  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ 0 G& }# {9 P% L
  const vertex_t* p,
- G4 d1 f5 _$ Q' K+ J  const vertex_t* q)
8 J5 j2 |/ c* p% T1 f  { % Q" t' B3 j' v3 K% C
  double dx = l_end->x - l_start->x; % ~0 S1 y/ Q% s) r
  double dy = l_end->y - l_start->y;
& j( n1 b- @3 h, m: d/ `* L. m$ U  double dx1= p->x - l_start->x; 2 o/ `% ~* ^) Z" ^* }
  double dy1= p->y - l_start->y;   h( w  K' F1 v( s
  double dx2= q->x - l_end->x;
# y1 T* `2 K9 }* S" A3 X0 O" }  double dy2= q->y - l_end->y;
# o% b1 `# P4 E! \# l  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
# i# Y* D: B6 v4 a" q1 t9 q" Q  }
0 [% |- m# p3 h9 S7 Q4 ~3 {5 ~$ U  /* 2 line segments (s1, s2) are intersect? */
3 K! `4 N% m8 v0 N" E3 `- N5 Q  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, 2 F/ ?# [2 [  D& ^! O8 _5 T
  const vertex_t* s2_start, const vertex_t* s2_end)
) I! _% `+ p+ ~  { ( |- F( G" W! |
  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && ; |' e' S) A4 R  V0 {4 K9 Z
  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; ; v- ?# O3 Z: F. Z
  }
) i  d/ T$ Y- ?* ?0 G! k+ Z3 ]* @- t- Q9 `$ m3 m5 @) o2 k

5 P1 u6 J0 G" Z. A* g/ Q  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:" n  X( v3 G1 N2 R5 C
* t8 ]( c+ O; X, g; i: c5 u+ a
以下是引用片段:) C7 T% L* R& K) y& T, J
  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ 5 a' `4 p! ]. k6 A. o, }% u7 d- R
  const vertex_t* v)
, K+ _) Y+ M1 T+ X5 m8 O/ G, C& n  { % K8 p) G+ W3 ~* L2 J
  int i, j, k1, k2, c; " c7 U; z3 \3 I5 _
  rect_t rc; 3 C' p: G3 s1 S- _1 L
  vertex_t w;
: D, a' ?0 I) v+ l* p  if (np < 3) & d) l& N) w9 X5 I+ d# {0 p; X
  return 0; & s3 u4 C# `/ H: }5 M
  vertices_get_extent(vl, np, &rc);
  w+ Y$ g) [/ @  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
' c+ z) F; G7 f% t  g* x' N  return 0;
% g- x) n  S7 l7 u  /* Set a horizontal beam l(*v, w) from v to the ultra right */
, |5 ^3 X% s# Y" L8 ]6 O  w.x = rc.max_x + DBL_EPSILON;   b- s; M: |3 i2 Q' M' y
  w.y = v->y;
% Y6 P  r5 I& I/ z6 u) Y' o  c = 0; /* Intersection points counter */ 7 o8 n- N( n- a& b0 M5 X& v# o% w
  for(i=0; i  
2 S. ~& q/ i3 A! f/ L  {
; A) \% {6 j3 h' h6 X4 c- ?6 D  j = (i+1) % np; $ W. [; J) h- s7 [* {
  if(is_intersect(vl+i, vl+j, v, &w))
/ p# o9 Z! d2 ~8 j  { * ^* @# O& _+ G7 R( k
  C++;
8 M  Y6 t0 h  _( S3 O/ l: \  }
  J& ~# `9 E; Z3 v5 Y  else if(vl.y==w.y)
# E& M: U0 P6 K8 ~" f1 K$ P8 S0 s  { ; C+ d2 M) X- e+ i- D/ D8 w
  k1 = (np+i-1)%np; ; _% I) s" K, k/ l8 a+ c6 i0 c, a2 t
  while(k1!=i && vl[k1].y==w.y)
% r" M! b: C/ {- n1 E$ ~7 }  k1 = (np+k1-1)%np; 8 R. ~# F  _( ]* T  k: ^
  k2 = (i+1)%np;
# ^6 I6 L% B8 T: z) D; q$ `- W0 T$ I  while(k2!=i && vl[k2].y==w.y)
* y$ o* \2 t4 o  k2 = (k2+1)%np;
; T' c! J) W& E6 e5 M  ~  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) / ?- L% d1 n. w
  C++; 7 C# |9 w" Q  C/ A3 B/ H
  if(k2 <= i) & C2 u) ^9 n/ W% t" |  k# J
  break;
0 k( Q4 d3 e" u6 v; a; P6 h  i = k2;
* B6 |9 F+ E2 [  } 0 l# K& G. b3 I* ^/ E
  } " G+ P% e  E" t
  return c%2;
$ A) W* O0 ~1 g8 G. F2 k! E: w  }
4 `# ?0 I$ t: s: Y3 k2 K
' q' M9 a- m1 M$ u% k+ X7 c8 e( t, Y' x7 o. c1 N7 J* W( u% T' ~6 }
  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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