返回列表 发帖

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
- C' Z% m8 F+ K/ q' j+ Z/ r% ]# A1 V) m3 e! n& b: [) c
  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。% }0 @% T6 U% P1 W5 s) ?+ V

" c- h: N- G* y( y% G+ r  首先定义点结构如下:
+ O" i. J% H+ e+ q" _
9 l9 c! R8 {9 {: e7 M& ?以下是引用片段:( {. G/ Z4 ~5 K0 S- x0 U( D
  /* Vertex structure */
# L1 w2 x6 x8 j# o( L- w, h1 s) O  typedef struct 9 X$ f- ~/ l. K" w$ k
  { ) b$ ^  t7 R4 `9 a; l6 J. a
  double x, y; 1 {' m2 r2 @1 X4 m
  } vertex_t; 4 j3 w6 d% W" T5 e* R5 O/ n
7 K0 M  x+ b0 R. q: K! g
5 i/ A/ b  }3 U/ p; ^) _
  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:9 C" i) N( w5 h. G* [$ }

. d* y2 P* W6 f# a# f+ _以下是引用片段:
$ L% G0 i% O! G8 G) l  /* Vertex list structure – polygon */
: u+ j. c8 `3 y! Q/ J  typedef struct $ @9 h6 U  e4 f7 V/ q8 v( k  V
  {
! D8 E' D4 M/ F) I3 I" |! s4 Z* o) j  int num_vertices; /* Number of vertices in list */
9 ?' `$ J; L( q8 `, x( \+ G* R  vertex_t *vertex; /* Vertex array pointer */
' i; E( n9 Q+ ~  } vertexlist_t;
0 X- _5 B+ `/ I. A0 O- X, K/ R% X# z; D! ]5 U
8 ]/ c* {) `1 R! [' q5 J, s
  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
; p( s' [7 g$ o! ]+ h
) m$ p) R& h% a6 y以下是引用片段:
( \# C, J: @: m) B" i  /* bounding rectangle type */
% q+ y1 |2 Y; @- z" p: O, o( _$ U  typedef struct
& \8 K+ @( E% R+ C  I3 w* Y8 B  {
2 A/ I+ S; n* a  double min_x, min_y, max_x, max_y;
2 b& L" ]4 f: |( g' i7 C  } rect_t;
' Q* G8 [2 A" w5 m1 |% k% B% k( m  /* gets extent of vertices */ " @. {& f  K% }2 R
  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ ) K3 a- E8 @! O, J3 u( x
  rect_t* rc /* out extent*/ )
7 q" P- `3 S: x' |6 B/ ]  {
: ^( m! }% f' o- q, R0 h# ^4 k4 v  int i;
' d$ F  k2 T, }- }; n  if (np > 0){
& ~3 f) Q3 T  Z% |! a  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; 8 S$ p0 T1 D; P5 ~4 L# j
  }else{ ) M0 C1 C, V1 n' e) Y$ [
  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
3 w* W  P. E; `: A1 N2 ?  } + D( D& i! ?! J, j) f/ i, l- E- L# L' L
  for(i=1; i  # \# |5 t- e6 ~+ p! V9 K9 l
  { $ X8 g0 r# S  W, c
  if(vl.x < rc->min_x) rc->min_x = vl.x; ) ]2 E. y3 Y! P! H9 w$ `$ N
  if(vl.y < rc->min_y) rc->min_y = vl.y; " k' |: T6 r* h0 _* |: F
  if(vl.x > rc->max_x) rc->max_x = vl.x; 4 e, n) A  ~! {% D; A0 O" ~
  if(vl.y > rc->max_y) rc->max_y = vl.y; 7 u% ]4 v% X# P% @
  } . r0 K* _+ c1 N' x/ b3 `# s+ i. V9 R; {
  } 9 W/ M! ]* @& X, t
" I! q$ L$ {, X+ W
- y/ Y8 Z" H7 E# {
  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。$ a7 A8 b2 t5 F6 S

* m, z. R: C: Q4 H  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:* w7 s+ X3 K0 y0 M% h: _; q& V

$ {: B* G! g5 y  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
+ V* x4 i4 W! A' y4 ?2 N; k& j1 E  {9 r. H3 q
  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
2 ~; H0 h! s. N. ~4 D! o$ y% n; D% z- j5 x+ X4 [( F; b+ \6 d5 ?
以下是引用片段:
- [5 |. [7 V' D1 y  /* p, q is on the same of line l */ ) G  W1 m# K+ ^' C" U# F. i8 R/ a$ L
  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ ) R9 t1 Q, W+ r2 F
  const vertex_t* p, & O9 N& F  [: T
  const vertex_t* q) ! p1 W  e9 u9 ?# Z8 H# c/ |2 W
  {
8 A. b" [8 R. M/ \8 H. D  double dx = l_end->x - l_start->x;
9 i- v- o, H- z) n. I' B" a5 K2 n  double dy = l_end->y - l_start->y;
  y2 x; M' d# j- \( J! [  q/ o  double dx1= p->x - l_start->x;
' H) l& s8 v8 u, u  double dy1= p->y - l_start->y; : Y# k. u; D" p, A4 x
  double dx2= q->x - l_end->x; / e% ^; p3 z8 r7 ]) E
  double dy2= q->y - l_end->y;
8 m. F! u$ ^+ C1 V4 y  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); 3 v1 V8 k! P  j6 {, F' L% _
  } + t+ o* W+ U2 A/ _: r3 C- ]
  /* 2 line segments (s1, s2) are intersect? */
! o6 t5 ?  Y7 w/ S  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
7 n5 R* a/ n( A/ D7 v' Q% ]  const vertex_t* s2_start, const vertex_t* s2_end)
" V+ G# I: m8 ^( d" Y  {
  m0 Q% B6 a+ b3 {/ D$ o9 k  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
; h! S+ n" d' F9 R7 U1 n; H0 c0 F  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; . k' U* D3 D8 M* B% R1 `
  }
, T7 z. e3 Q* _* B  V6 T# _4 ~9 Z, L9 [" W% j0 {

  f( p: Q8 V+ C  `' e* Y  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:& M# Y# V4 E9 N* v0 q& T' l( A

' p: W$ \: Q# z% `以下是引用片段:
8 O- z- e* A0 V/ S  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
7 d& p. j" j( a! x3 i  const vertex_t* v)
8 J3 }5 v  n, D& Z- ]  { 6 X! y1 C5 [8 \1 @% T. Z
  int i, j, k1, k2, c; - g8 }4 R. J& v$ V& q: u
  rect_t rc; 3 N1 G. a. U+ `7 F: U
  vertex_t w;
9 x  |+ A! v8 x; Q3 m: a& i  if (np < 3)
6 n8 k# Q6 ~+ i' @  return 0;
# V  [3 \; X1 \9 {  vertices_get_extent(vl, np, &rc); # `( D8 o( Q4 L2 Y0 h4 z9 M
  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
# O# K* }/ D3 L' l5 }: Y  return 0;
+ W  Z5 ?* F8 d+ a" k  /* Set a horizontal beam l(*v, w) from v to the ultra right */
$ y) ~7 F9 `6 |8 Q1 o  n  w.x = rc.max_x + DBL_EPSILON;
2 D/ \* c9 R) o. ~' J7 H% ?  w.y = v->y; ( k! Z) A; J& o
  c = 0; /* Intersection points counter */ / a- M5 E9 b/ c! Y1 c) J# V
  for(i=0; i  
; Z9 o" `, v6 L1 `  { % p! W; O5 D4 j
  j = (i+1) % np; 8 ^- |2 }, Y5 S! p' D; D, e0 f4 ^4 M+ A
  if(is_intersect(vl+i, vl+j, v, &w)) % _2 G/ a" J8 f8 Y; k9 G
  {
$ [/ s7 c6 f% W' I: y  C++; # G. `$ s4 u7 X
  } $ z2 P9 |1 n( L6 `
  else if(vl.y==w.y) 2 E( b# M7 f- A/ R
  {   Y1 a# w$ n2 F- @, s) b) Y- }
  k1 = (np+i-1)%np;
, d- v' R; M' W8 a" b( v2 a  while(k1!=i && vl[k1].y==w.y)
) F2 l5 ?% ]  P+ i  D* I4 U  k1 = (np+k1-1)%np; 6 K7 _: _: _) G% x
  k2 = (i+1)%np; ' h- u6 A" S# e2 ?$ Y8 [" L
  while(k2!=i && vl[k2].y==w.y)
1 T; y% F( C% C/ {8 _" C) t  k2 = (k2+1)%np;
& o# i: a6 G5 o; f: I  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) ) b7 A. T) \  O3 O
  C++;
& f6 s' X. I3 c! T  if(k2 <= i)
2 i8 m" d0 X( i. d- ~9 ~  break; & o+ C  s  j5 ^- L3 q9 o* n7 I1 a
  i = k2;
4 D: R# l4 b, a( M6 R/ X  }
2 m6 i4 R! e( d  }
& S( Y& L# b/ {" @7 B  return c%2;
1 w+ q$ I) o5 {0 z; W  } - v- b) K7 d$ c4 C& k% y* G8 P) y
7 d3 ~# ?9 k1 h

8 w- `% B4 `' A% F+ D8 C# [- q  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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