返回列表 发帖

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
( n$ Y& _/ m5 d* w
: g7 n  w" V2 K  o. q% L% D  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
: D8 C1 D! j. Y( ~; u# Y5 X  R( _* Y( s8 M
  首先定义点结构如下:7 D, B! Q9 r2 A0 P

9 ^' n3 s  q" X; W5 Y5 H' J2 A1 o以下是引用片段:. M; B: H3 w, t& K6 o3 j
  /* Vertex structure */ ( x9 a# ?+ Z8 y
  typedef struct   F& M' O" D1 o, i) d
  {
7 P$ |( U2 M: u8 v8 M# d5 s  double x, y; 8 }& e. ~* y; z$ M$ t
  } vertex_t;
- X6 w% M1 \" m* _; m$ ~8 o  K, J8 a
* ~6 M( ^+ B/ K9 Y9 ^/ v8 q0 `
  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:0 D7 m3 m. |0 A  X" e9 n. t
' L# _' ]$ F4 d. w0 Z8 h
以下是引用片段:5 B- `, W; R- ]0 u* H6 J5 {$ k
  /* Vertex list structure – polygon */ 3 v, s8 X" S; S/ c3 |' ~8 G: b
  typedef struct # O. b- S  m; \1 n8 h
  {
/ C, A  _6 R1 h, u* e' D  int num_vertices; /* Number of vertices in list */ , s3 r2 e# N+ X0 p7 y, e
  vertex_t *vertex; /* Vertex array pointer */ $ @4 H& b2 A: e" d, C- X& }, Y( W6 K
  } vertexlist_t; 4 [  ]6 X6 R; b2 R' O  E, C$ Z

9 B  A0 @2 c' B; p; K: S8 Q' C8 {5 I* g# h: |& N$ Y7 P5 m, E, y
  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
1 o0 a; l& l$ j) D9 m1 B; n6 v
8 K& f4 L, w" h7 c# U, ^以下是引用片段:
' o, J7 C" _! s/ N8 \2 ~; K  /* bounding rectangle type */
$ i" b# j- ^+ V; U& X- J/ N5 X1 d  typedef struct
" L1 E% I% l# }9 `1 e  { # m5 a+ r$ o8 ?6 U6 Z8 b
  double min_x, min_y, max_x, max_y;
6 v! T& m5 @; L( p1 u( D, J- ?  } rect_t;
' l6 m+ m1 y9 l  /* gets extent of vertices */
. n$ E# G' C8 S$ s$ S& _  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ $ V  P- e) W. Y: N
  rect_t* rc /* out extent*/ )
$ X. z& A! X5 ?7 K. ^. L; T  {
- Z5 W% b% F! I  int i; * `# u) u2 |# @9 ?9 ^; f/ [+ L
  if (np > 0){ 5 h# S4 A" @# S  \$ Y1 ^( ~& l1 ?1 g
  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; # h" y) n- G% V, b+ \, G
  }else{
- O& F" I* X0 ]  a, H/ G8 C: ]  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
% k2 D  ]9 `6 L  }
) z2 l$ l* d' E0 g. n: x  for(i=1; i  5 r) d5 q9 Y. z( o* Y
  {   n6 x, }" V# J. T
  if(vl.x < rc->min_x) rc->min_x = vl.x; 7 Z2 y' I3 j4 c
  if(vl.y < rc->min_y) rc->min_y = vl.y;   C. B( k3 S9 F4 G! A+ G' Y
  if(vl.x > rc->max_x) rc->max_x = vl.x; - Z$ ^* ^* b7 T$ _" q
  if(vl.y > rc->max_y) rc->max_y = vl.y; 8 I7 v# o- _: X6 r* T% B
  }
6 p/ r! b1 u7 e, [# L3 Z4 Y  } * D- I4 C1 f1 i( Y; q  [
6 B0 c, ~" s$ m+ G4 Z) ?

8 U/ R8 `- j/ d/ L  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。3 A5 d' ~# B3 g& s7 E7 t

, w, z/ t1 o: V7 q' u  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:5 {) E7 p$ ^4 d* `. H  [
8 R) v; d7 D; g6 t/ P8 |
  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;% c0 Q4 C% y! E  c4 Z4 w

% M2 c. W* j! s$ J, w7 c. K, o6 B  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;/ p5 `2 N: N% b9 I1 o6 \# V
" m& U3 c$ u; a+ f% A0 I- O1 h# ^
以下是引用片段:
2 m9 t/ o  m- b  X. p8 u3 x, U  /* p, q is on the same of line l */
+ Z7 ^9 g/ T+ `" v; _+ H  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ 0 z$ A1 H# e$ M, A4 Y( ^" g
  const vertex_t* p,
. o$ `5 N( X! K* J$ Q1 u: ]5 d  const vertex_t* q)   i, R' j2 b+ S. j8 R4 L
  { + E8 t" v% Q6 V2 m' H/ V0 E; _6 b4 T
  double dx = l_end->x - l_start->x;   F1 c" I- z  p8 V5 j$ H
  double dy = l_end->y - l_start->y; 2 d( z# R1 O2 P) b6 O
  double dx1= p->x - l_start->x;
4 ]7 \8 v4 ?+ r) W+ X/ D  double dy1= p->y - l_start->y;
& C3 K6 s' |0 G9 A. A  double dx2= q->x - l_end->x;
: f; }. L' w% Y7 x8 h5 R' d4 `  double dy2= q->y - l_end->y;
, G9 O3 `) U1 M& z# J( X  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); , r- t) W" N, M& ^8 w6 w( f, K
  } + S9 k# `2 b+ e
  /* 2 line segments (s1, s2) are intersect? */ & y# Y, C' _' I0 m5 N1 K' C# s  k
  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, 7 H; U' a5 P2 Q- W- I5 h" H/ v
  const vertex_t* s2_start, const vertex_t* s2_end) * A- W* I' H: @) O" ~# ?
  {
5 A, w5 O# v* A0 o" u5 X  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && 9 t4 K" W6 u* r  W0 w' |
  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; 3 R! e. o$ J5 z4 R, q; A9 N
  } ' l* Z% C' x  c9 l4 k+ R, i
/ k' `. K: v! i+ M
  o4 A3 Q) K$ v
  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:( V1 O6 L( r3 x4 V* x

* A; ]8 ^9 u* v% @以下是引用片段:+ E4 U* d  U  o1 x4 G8 K7 V
  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ % ]$ |7 k: b2 O2 `& ?
  const vertex_t* v)
, O$ o6 Q2 T" `2 u3 Y' D  {
, A" t6 v5 x$ n4 j) \3 n3 s  int i, j, k1, k2, c;
' ]3 z1 Z9 j" d& h6 u" _) L/ c  rect_t rc;
: d+ r  r8 H0 R& U  vertex_t w;   ]1 ?# z) I! ?9 |
  if (np < 3)
; \7 W, ]% N. a+ D  return 0;
) m' r: q, \, y- z  vertices_get_extent(vl, np, &rc); 0 O0 r& a6 z: s& O$ }5 L; w2 n
  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) ) t2 W4 n. F# k7 _' f: ~- Q
  return 0;
. M3 j- D% z5 N% n) K  /* Set a horizontal beam l(*v, w) from v to the ultra right */
5 I! Y7 G# z+ s8 G; ?  w.x = rc.max_x + DBL_EPSILON; 4 c1 B3 b4 p# ~. Q
  w.y = v->y;
) @. C; a2 c1 O+ ]  c = 0; /* Intersection points counter */ 2 v! k  e& R. I1 o! o
  for(i=0; i  ' B: H1 o! L& O% s
  { " k* \0 ^% u; p- N7 `7 A2 l* T
  j = (i+1) % np; $ f/ R! N0 F) C5 |2 ?: Q
  if(is_intersect(vl+i, vl+j, v, &w))
; p9 {3 U6 _7 z3 I  { & n( P4 F% A" Q2 [& `
  C++; 6 w& Q0 X, k2 w3 Y) k. B: p8 h" H) T
  }   d4 t  K! O) C6 n8 \) n# O  _6 ^
  else if(vl.y==w.y) " b3 f/ y- V3 F
  { # N- I2 _. c- L' E' r) ^
  k1 = (np+i-1)%np; $ _: r0 r6 O) g3 S, C
  while(k1!=i && vl[k1].y==w.y) ! v$ M# l) T2 f, Y( r8 U
  k1 = (np+k1-1)%np; + e' Y$ c% a& s. h0 ^. o
  k2 = (i+1)%np; 7 S* J2 r$ H. m; s9 g: C' `+ n
  while(k2!=i && vl[k2].y==w.y)
& C( v/ z, V$ o! H! e% t  k2 = (k2+1)%np;
  z* ^5 `2 M# z& E. |4 y* Z  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) 3 a! m3 L- }/ A2 w3 F- D: v- _
  C++; 7 N4 ~2 j, x' @
  if(k2 <= i) 5 l9 q& K$ O  h0 m
  break; 2 t2 M3 x$ u* C6 G  n, T, I
  i = k2;
4 G4 G" m* {4 u: B. }; r0 I0 z  } ; f5 j# }5 L8 a+ Y* ]. |. W  t% `
  }
3 @) B5 b: {: u+ I* ]9 ^; k3 Q  return c%2;
% k: q; D- `' ~$ z# f% m0 X  }
; V) Y: _) v) G4 E- M4 M" a8 p4 _* K' E1 C
* P; `/ a, b  R: U
  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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