返回列表 发帖

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。$ _. h* z+ k7 `' m9 p( P
9 l3 Z2 Q5 W0 v. a3 e8 W' Q8 b- K
  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
; ]$ v4 j' J7 s
) |  [; X9 u9 T4 e- L4 Y  首先定义点结构如下:
9 d  ]  F( M4 |9 C4 n0 x
- W$ @0 ~; v2 M1 W) {3 a8 E以下是引用片段:
" l2 `- J" C2 h4 M0 x- }  /* Vertex structure */
5 A& D: @: D, D! \  typedef struct
5 m1 H' P+ M8 D; A) m3 q) ~# @5 @  {
9 u2 ?; i, Z  h  double x, y;
; `5 Y+ H+ z- I0 i3 d/ I  } vertex_t;
& J6 M+ s% k0 }% t) `5 Z, D4 u3 u. ]/ `  F1 ~- Z6 L4 F9 k. `5 X
$ ^* ?- B7 I" _
  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:, o1 x! R! o0 A0 B
8 K& l+ e% [, y9 ]; ?9 j
以下是引用片段:
* n& }2 B  u0 b: z$ t9 r4 p  /* Vertex list structure – polygon */
0 C& s  I& E( c5 P8 ^9 v  typedef struct 7 H; R  _6 h' _
  {
, P; O$ v& b0 @8 s# K$ j  int num_vertices; /* Number of vertices in list */ . g  h0 B9 J0 B  e7 X. x* y: v3 C
  vertex_t *vertex; /* Vertex array pointer */ ; m* Z) ]6 g, R7 R/ a
  } vertexlist_t;
8 L2 ?: G# d$ x+ p: l) Z! r. p/ e$ t/ f, F  i
( f, e( @9 y7 c  f' s7 i  F( E
  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:- z0 F- f% {9 B5 [- g

+ q! {8 h' E; |7 N以下是引用片段:2 l" `, g* ]$ U3 a4 {
  /* bounding rectangle type */
3 b+ c; {6 T/ F( O: t5 M  typedef struct
2 G, [( [$ d$ S+ ?8 m  {
/ m& T% I, E, o0 f, c$ B1 \  double min_x, min_y, max_x, max_y;
* ]4 F# @0 ~- a. d  } rect_t;
6 r6 S1 a! @( U  /* gets extent of vertices */ 2 E. R1 f% r  S  [3 E
  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ # T5 Y! ?& X4 P6 L9 l! W+ w: u
  rect_t* rc /* out extent*/ ) ( p, F3 G/ _0 O3 q* E0 d* X
  {
, M8 c2 l0 {) |! h1 @  int i;
( q4 ~4 t: s7 [: _  [/ H5 L  if (np > 0){ 0 S$ `( X& V$ x5 `6 B% W
  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; 0 F4 }4 [9 |( v. ]% @& t. K
  }else{ ! r# E' o  k, a% J9 I" j
  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
+ p' N# T  d& l) B  }
: J. e$ h: J$ D  for(i=1; i  + a% i# p" D" S4 q, y5 D
  { ' @; g- O# w3 Y8 [# a3 ]+ Y
  if(vl.x < rc->min_x) rc->min_x = vl.x; ) e% g# z1 R" V/ h; @
  if(vl.y < rc->min_y) rc->min_y = vl.y; 2 l+ ~* k% x" }8 Q6 x% m# Z/ R
  if(vl.x > rc->max_x) rc->max_x = vl.x;
4 o. s5 A0 T# V6 F8 G, W! E5 h  if(vl.y > rc->max_y) rc->max_y = vl.y; * Z. C3 y, ~% s! d
  } 5 y, l$ {9 V6 e( X
  }
5 v/ y7 x* Z* `- u5 I+ U/ K0 c* k# v+ L1 ^8 t+ P

1 S- ]( p! f3 ^  [  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。! M$ m  n9 v+ Y* K" |0 K

( g3 U% h( l8 a% f  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:( f7 |6 ?7 `5 y* s8 o

. c3 M3 {; e( s% R  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
* `3 M, Z3 b& R# d; a6 `
" C8 d; r- \( Z  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
1 |2 b  {8 |; Y+ v' d7 s
, F% l( t" S+ E! K6 m6 ]以下是引用片段:
7 i/ W( _9 O$ y% Y6 d: N  /* p, q is on the same of line l */
8 l& b4 v# z; J; N# O  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ 4 ~3 a  e3 L: A6 \/ g$ v( i
  const vertex_t* p,
' a- [* l4 R7 j3 s& D1 m: _  const vertex_t* q) + f% R  |( U. g9 h# D2 A
  { : t6 z& U( c- \" L7 t  n$ z
  double dx = l_end->x - l_start->x; 8 b$ q" w, g7 j
  double dy = l_end->y - l_start->y;
) ?- z! a( E+ |0 l) }  double dx1= p->x - l_start->x;
' G, C$ V& i9 S. _6 R) G4 S  double dy1= p->y - l_start->y; # F+ {7 r7 x1 C3 F7 H( g6 _* H  j1 Q
  double dx2= q->x - l_end->x; 5 c& c: w' D! a0 D9 W
  double dy2= q->y - l_end->y; " C: B8 v7 }. x" g, ~+ ]7 r
  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
$ ~4 Y& J! ]# ~. @5 B; a+ a4 S  }
, r- |# p- h% _& B% V  /* 2 line segments (s1, s2) are intersect? */
, }( A# J# m7 ]1 @1 ~( Z; b/ V  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
& a/ }# y8 j+ p" O" s  const vertex_t* s2_start, const vertex_t* s2_end)
) [4 a. i8 T! c7 h; \  {
1 _( f4 A% G, z* g; K& r  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
. l- t" v- o5 U( }& M! \9 J- }  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; - s1 J: K/ g5 J8 \& u
  } ; F4 v' \1 M" y8 a
. X0 u# A; q9 V9 x/ m3 Q
, o$ ~- S; a! d0 ~, f* u
  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
% j( l1 g. t/ A* A
' ?, I$ i3 z5 Q- |+ @% K" u以下是引用片段:
+ Y3 r( X2 H2 n. M  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
2 R7 Q; X- a0 P. s9 m  const vertex_t* v)
" P6 n3 V- E. S: U$ @8 q/ w' V  { , v: D( x) }, |: L7 y$ T$ ~. k
  int i, j, k1, k2, c; : y2 B9 Q4 ?  T# K7 U; H" b
  rect_t rc;
  y  c; h- {& X; S( w  vertex_t w; . b; N( r! e$ G1 w  G" v
  if (np < 3)
: Q# Y# P$ q/ [, g5 F  return 0; 9 R" n, C5 A8 w1 \3 ?
  vertices_get_extent(vl, np, &rc); 0 v. p2 H% M  S% Z4 E# S
  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) $ H7 i6 u8 j! C+ x5 X# C/ C
  return 0;
; j2 w/ C& K, \; W1 ^  /* Set a horizontal beam l(*v, w) from v to the ultra right */
. M0 c% O8 k2 D0 W; w* Y# g. }4 Y- N  w.x = rc.max_x + DBL_EPSILON; : T. w7 ~5 I' l/ Y! s# X/ L6 g$ Y
  w.y = v->y;
& }3 a% c0 }5 c6 |& F  [) h# I  c = 0; /* Intersection points counter */ 5 |8 V: U8 R0 G0 k4 a: A
  for(i=0; i  
! Y1 R% C- k9 ?9 U5 H6 ^$ W6 R& A  { 7 d3 `" _" G' Q
  j = (i+1) % np; 3 X' `, n: F7 Y, k
  if(is_intersect(vl+i, vl+j, v, &w)) ! c: _. k: H6 l# i
  {   T  u- G9 Z' X2 P6 D3 O
  C++;
6 j, p6 J; x# b: o3 C# o  } 8 e" Z" V1 T1 |7 l, x7 v
  else if(vl.y==w.y) 1 s4 ^! X, K1 w1 e2 v5 M+ p. d
  { 1 k) _: ~# B8 ?2 _: f! D% M
  k1 = (np+i-1)%np;
& K" X  Q4 g& F; F: `$ H  while(k1!=i && vl[k1].y==w.y)
' q: I( _4 f  g& d1 i+ u8 H  k1 = (np+k1-1)%np;
+ @' `% T. Y, N  C  \  k2 = (i+1)%np;
/ \! O( d$ N8 w* U; a  while(k2!=i && vl[k2].y==w.y)
3 `: M" N" f0 M  k2 = (k2+1)%np; & E8 s1 ^3 m8 H8 ~
  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
2 J& k$ ^$ c8 ]* Y4 y/ L  C++;
) e3 y6 _  N: b& @, w" k0 ?  if(k2 <= i)
, E& t% b0 |* Q* ~! ?5 W  break;   Z, R: F7 l# \5 O! Q( ^+ T
  i = k2;
) v, a# p$ s8 b9 j6 n7 d& I  }
2 ^7 v) [0 g* ~% \% @' b& J! J5 {  } ' c* M# X6 }( V5 e* T) @: D
  return c%2; * w+ N3 S4 w7 a2 E
  }
$ T/ e0 e; m% b" }7 a- X0 Q7 d
1 @9 P7 b3 N" F/ }9 q1 v2 Y
: r2 {; \/ ~+ {7 \  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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