返回列表 发帖

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
) e: }+ }( d8 ~/ c) H4 u* s8 R* i: k8 y; O  J* |
  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。: o, e2 c% s2 U" o& ?9 J: m

: U7 p7 S, D* @- ?  首先定义点结构如下:8 u6 t% l( a# g8 L! [- B. {

/ i: c9 v0 F5 S% A8 ~以下是引用片段:6 G- Q0 i! i5 S+ B! E3 T1 p
  /* Vertex structure */
) d) ]2 ~: z# }- H  typedef struct 0 J  p. r: y" j) N, q; }
  { & S3 m8 ]& W: c- I) A) g. S
  double x, y; ! S4 x" c5 P2 O% z: v5 H- i; C/ ~
  } vertex_t; / m4 ?, X1 e9 b4 H3 K- F

! T8 N1 U8 H3 y, @( c' y3 [0 q; ?3 I
# }8 @  v! |2 \! S1 j  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:4 K2 M; d; {$ Q! {
2 X& @5 q; p% a, Q/ Y. G
以下是引用片段:
! b) t4 b5 m" C, _, F! P  /* Vertex list structure – polygon */
5 g- x4 w, D9 b) q  typedef struct ' a! ~4 j( K, H# s
  { ! D% R4 X# c7 _/ F* w  W4 n
  int num_vertices; /* Number of vertices in list */ 4 e5 }. j( g7 D% X
  vertex_t *vertex; /* Vertex array pointer */ ! {% G7 ^3 i( Y# Q0 T% W
  } vertexlist_t;
) V0 u) X( z; C
" j3 p$ |) e! I" w: ~+ p) E/ [4 [- I. h. a, @1 |, Y7 U
  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:" Q9 e7 c5 l# v

% N2 `$ Y, I3 R0 t3 j$ i! X以下是引用片段:$ R7 n( i2 V3 o$ E0 m
  /* bounding rectangle type */ 7 f8 W; z, S) |, E2 f* u' X- E
  typedef struct ; {6 X8 P* `# f9 M8 [
  { $ d0 B# ?. |7 N* A& D& w
  double min_x, min_y, max_x, max_y; : H7 ~# ]  Y& I: s
  } rect_t; : q. }5 N. n( f5 L' U
  /* gets extent of vertices */ & D# z0 |+ @% {) k9 d- {  E
  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ 9 |& J$ I) h9 Z" c) X
  rect_t* rc /* out extent*/ )
6 t2 Q* Z" t4 f' Y* [  {
) G2 X! n# }$ O, j4 c! T- `& D  int i; " v% _; u2 a* F0 L: o
  if (np > 0){
( i. ]# E: x* I. K  [" j3 I+ B  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
0 h; b" C3 f( P9 g  }else{
7 @0 I; t! ?" U6 l7 R  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ 2 b7 x: [+ a, C8 r/ B6 ]; |1 q% S  @
  } ( M  a) K9 ?1 ^6 y9 @; L  B. o
  for(i=1; i  
+ k0 l$ n2 Y( c2 y+ L  {
* T4 ^) u' j/ Z% {5 {  if(vl.x < rc->min_x) rc->min_x = vl.x;
8 _* F( f1 j0 I+ |, O. x5 t  if(vl.y < rc->min_y) rc->min_y = vl.y; 3 u7 w$ C3 t6 i" K8 y3 x; ~
  if(vl.x > rc->max_x) rc->max_x = vl.x; " Q! b, \/ t9 L% O: l
  if(vl.y > rc->max_y) rc->max_y = vl.y; 2 w; W: e2 l) k# A  @: w
  } ' m: a! L. Z7 R- s
  } " b+ ~) e/ B. f2 j5 Q2 S

0 n( G5 L6 t1 N  R* r' M
! ~" t7 I& x, H7 V9 L0 J9 w  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。" Q4 j$ ~5 o: |- x
2 C% M8 K- {" Y
  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:8 Y8 K/ j+ F. U3 G7 C, l5 N7 e4 ?

1 E* z. n7 }& T' H' ~" m3 ^. {- Q  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;+ a! |; I$ f2 B$ G' y  k
7 t# o" k5 E. E; f/ C$ E9 z
  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;  {2 H( `# f, c. x6 h
; _  v& t* |& i( C" v$ p2 `& \. k
以下是引用片段:# R, q% Y) Q) |" O$ [7 v& v% `5 f
  /* p, q is on the same of line l */ 2 V3 G2 N; U) U% t8 f# |! n
  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
  Q4 w* y& D5 ?0 K. l  const vertex_t* p, # K) X  G/ i0 y& @+ t) g' }3 b2 w
  const vertex_t* q)
  G. q% O; T& C& L3 p& X  { $ F% `. E- M# F& B5 G: {# z
  double dx = l_end->x - l_start->x; % z% y! g+ T7 Y" f% Q7 e
  double dy = l_end->y - l_start->y;
$ l# u6 o* v5 F& B( P& {  double dx1= p->x - l_start->x; # Q* K7 B( D# H
  double dy1= p->y - l_start->y;   R' C2 x3 P7 F# y8 g
  double dx2= q->x - l_end->x;
0 `7 D: _6 A  q7 Z: B; s* |  double dy2= q->y - l_end->y; ; R- W/ H9 J) x
  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
" s8 _& l/ U: I- {9 @  } 0 O4 h- B/ O# Q: U3 I, q2 O9 Y
  /* 2 line segments (s1, s2) are intersect? */
6 G3 P: t: [. B9 B5 Q* Y  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, ' i8 a. k' d) |  d: G; T5 [! J
  const vertex_t* s2_start, const vertex_t* s2_end)
2 Z' y3 v! a6 B  { , K( T' z5 @) A& j+ |
  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
0 l9 x9 i# m( k- `  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; " [' h2 T' K; j: @+ f# `4 |# a! F
  }
; y; A) i7 L. u( E* F
& r# n" m5 [4 J2 W3 _
1 c, [! \" j" D" H! F0 [( u  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
( a9 T' e1 W: w' S8 O* D% K0 b$ Q2 Q
  s. P. L  f- y以下是引用片段:4 L$ ^2 t/ n) k6 K, T
  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
" F7 c0 E9 V$ `3 h  const vertex_t* v)
4 i- @9 @& G8 V: D) K- z7 }' f  {
( a& C+ o8 \" I: H; }  int i, j, k1, k2, c; / W3 D+ N' D4 _" K- Q
  rect_t rc; ' z, S3 l  g# S( N! Q  d
  vertex_t w;   E# ?7 _! P2 Q- l% b8 m: ~3 D
  if (np < 3)
7 t7 d+ d3 N. ]6 _/ p' t  return 0; ' _* I+ Q8 \  ^1 m/ ?
  vertices_get_extent(vl, np, &rc); ( l1 w5 k; Z; @) ~9 m& ~
  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
# L6 U' D' X, I9 H6 F  return 0; 9 z4 Y( p8 Q2 `8 d% D4 i0 I3 `
  /* Set a horizontal beam l(*v, w) from v to the ultra right */
0 e. K8 ~: r' \( G9 O6 n  w.x = rc.max_x + DBL_EPSILON;
- M5 `6 q- [! o4 [$ L- G, p  w.y = v->y; ( Z9 @! J( k7 ?- D- h
  c = 0; /* Intersection points counter */
% b0 I, C7 ]" C2 C) f! B  for(i=0; i  5 B& d- b  Y# o' T. K. [
  {
; A/ B6 j8 L: k$ O1 Y  j = (i+1) % np;
* `# n4 V" _% o- V2 c8 I2 I$ ]- @  if(is_intersect(vl+i, vl+j, v, &w)) , N. x' K1 H! [8 J: {# v
  {
, R1 w9 e4 R- o; T3 }  C++;
- D2 t/ F7 j: u) T: a; P, z( I  }
, P4 F9 F% e. N  M/ v' i" N$ L  else if(vl.y==w.y) $ i$ z* n* p$ z& h$ U1 n+ R
  { , z3 U, K1 V9 w+ h
  k1 = (np+i-1)%np;
- K3 q, Z2 P8 F7 O# E% r  while(k1!=i && vl[k1].y==w.y) ) D( |7 i8 D; B  @9 P
  k1 = (np+k1-1)%np; . g2 D1 }( x% T! Q8 O/ ^
  k2 = (i+1)%np;
: B5 q- i. g5 S1 a8 f1 ~  while(k2!=i && vl[k2].y==w.y) : x' }- x1 m, ^; a) t" P# N5 J! L
  k2 = (k2+1)%np; ' v, @: {" \( V
  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
1 S( N6 F/ @, Y  }  C++;
8 i0 e9 e! f/ W2 _) U7 X  if(k2 <= i)
- n9 Q) U. L( F' ]  break; 7 ^. z6 T* c& u! U
  i = k2; 6 `- y- ^0 f& {  Z1 e1 B
  }
( b3 q: T$ a% `- U# i  } 2 c! Y8 Q  m" X; P9 f4 t- v# H
  return c%2;
9 y7 Z' T; Z1 m; g3 c: g  }
9 u1 A3 R! K; U. b- f  W) I& o
3 ]9 k. u2 ~1 d8 q. T
' l; N$ A( S/ D  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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