返回列表 发帖

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
, l4 V1 q) N% s4 A' @
' u9 A* A$ g$ {0 I6 Z  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。& j$ `' ^" T5 N2 ]/ E" p) F

; _! x- E: {! K; s  首先定义点结构如下:  ?$ }& C) e8 y; h  H

$ M, q5 L# k9 E  a4 _2 {# H6 O: i以下是引用片段:
2 M0 d: }& J- u6 V4 D  /* Vertex structure */
9 {/ {' J# G0 [/ {' O  typedef struct : [: p2 }( ~( Z' P7 J5 X
  {
3 ^1 L& V, {1 U4 e9 T  double x, y; 2 C: ^/ F8 P7 p  G6 |: x
  } vertex_t; * M; A7 Q/ \& K: K% g
% p* m& f, [8 p' }. p

) `. J% e1 }: b( f  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
0 r# V+ l4 S# M( e7 B% V
$ P' w5 w% {3 P7 f* v5 c6 e. x" ]以下是引用片段:
5 O+ n; e/ J; ^' U- r  /* Vertex list structure – polygon */ - P* ]$ M. U' T- r
  typedef struct
- d. Z0 K7 R: q& Z+ K7 v' k  {
6 a/ g1 E1 `+ e# A+ k' u" f# c. z  int num_vertices; /* Number of vertices in list */
+ x7 G+ A2 R/ W+ v+ v1 Y5 W  vertex_t *vertex; /* Vertex array pointer */
. k$ X/ b5 x7 |9 i, ~" m4 h  } vertexlist_t;
) W) K* N8 M' Z: {3 v3 e
6 H4 I3 {  h+ `: j9 ?( O
) i, k, A/ ~5 G3 E8 v  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:* ~! ?; {, x" |) j+ p" }
! N# P9 j3 a% V
以下是引用片段:6 [7 ^; H/ C+ G) M8 ^9 S4 `
  /* bounding rectangle type */
6 z9 h4 B8 I, s3 B: ^# X  typedef struct
# k0 Z+ v$ J, H" e" P  { ; J: T8 u  G9 B5 D8 x# t
  double min_x, min_y, max_x, max_y;
! I( _0 |- W# P3 A2 a( Q  } rect_t;
3 t: A0 X% k. L. M, p& x% X  /* gets extent of vertices */ 4 t/ r; w0 H/ }' p9 v7 Y+ X
  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ ( `7 ?" c, i* ]" B! l1 G, X& M" [
  rect_t* rc /* out extent*/ )
5 l; [! A) a6 [5 l: h  { 1 c3 L. I, x) J* Z$ u3 [% b
  int i; ! {9 U" Q* K! Y  c0 `! E2 X
  if (np > 0){
/ |3 I/ K5 V+ ~! o1 R2 w7 S2 b* _- h% C  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
6 g; C; ]5 Z8 A/ J' k6 s  }else{
: t. W+ n+ t$ t- l/ E1 z5 w9 m  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
" N  R5 @* q/ {. _  } ( ~. ]8 Y6 n1 a2 I. \
  for(i=1; i  
2 ~% l% w5 w8 h, n/ \- `/ G  {
, q$ j0 L. _8 R" y! D) L' W# @  if(vl.x < rc->min_x) rc->min_x = vl.x; / N2 H1 Y7 W( v+ ^* X4 i
  if(vl.y < rc->min_y) rc->min_y = vl.y; ; Q4 k' V8 o+ \0 H! j' N. T
  if(vl.x > rc->max_x) rc->max_x = vl.x;
2 g2 Z- J) Y. S; w  if(vl.y > rc->max_y) rc->max_y = vl.y;
1 t/ x# P& k4 s& M  }
4 `' ?# N3 j- t- F7 ]" b/ `  } * F! Y. s- C- [) t: ^9 i

) W# w+ P2 S( l( j1 ?- l7 u  b1 J% [/ {$ h# X! l8 {3 p$ U
  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
$ p8 l, m9 F7 l
" ]( _5 X2 w9 r+ R0 A* t' X  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:5 H8 f  G) J/ h

9 _" F+ H- c6 l+ x0 U  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
. V8 p7 G3 V3 C0 o4 m; u7 J: f2 R( j
  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
% F- X6 V/ Q2 z% q7 j9 v0 t2 l
; D! ^- Z$ ~$ \0 T. u2 Q9 o- A以下是引用片段:
/ k9 c* u$ m- h1 t, s  /* p, q is on the same of line l */
- M4 X/ ^+ [: H6 X/ g% o, i0 N$ G0 H  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
+ w2 f) H- ]4 ^" q  const vertex_t* p,
7 B0 K2 }, H: H! d" X; P  const vertex_t* q)
' c, f; @$ }: t0 f4 _* R  { 4 e1 K3 H/ s5 A. T/ G. F+ d
  double dx = l_end->x - l_start->x;
  {" b$ ?7 C; k' B9 O+ K  double dy = l_end->y - l_start->y; 5 J& d. d' b: ^7 p* [2 s0 c, ?3 d. E
  double dx1= p->x - l_start->x;
/ B5 P6 V# e* {. N  double dy1= p->y - l_start->y;
" C3 i% Y$ {/ Y# E, D  double dx2= q->x - l_end->x; 8 }- O& {  L! B4 l. U9 s8 h9 G
  double dy2= q->y - l_end->y;
7 d8 I' f1 T$ ~7 d# k2 g7 O: N4 H  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
+ Z4 ?, l( |  j1 Z  }
8 s$ H$ J1 K7 E9 u2 H  /* 2 line segments (s1, s2) are intersect? */ * V  r, m6 i. M8 y: q$ G3 n( p
  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
4 U$ G1 _( n. C3 C3 M. @) k$ s  const vertex_t* s2_start, const vertex_t* s2_end)
! F) N4 U+ p$ A' P7 i  { 7 ^3 E, r1 n4 \( ^  @/ F, @
  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&   o, {1 B* G$ W! O. u; K7 S/ `# ]
  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
' J8 S# t9 O- g1 r  }
; ^1 S1 W$ F6 |: G0 `/ r
+ b, v& n. N) m9 I# b; A
+ E' F# s2 Q  Y8 i5 ?; K/ T  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
2 l% i  R! ]* u* S5 J4 i
7 k( p% E& ]" r4 B2 |/ X以下是引用片段:9 c9 Z' `: @5 F# G2 r
  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ ; B2 Z1 h. |( \; U: Y' W: e
  const vertex_t* v)
- J, y! ]- d. T1 R/ O- L1 C  { + d3 w6 V  O2 |8 a8 m/ X
  int i, j, k1, k2, c; 1 p2 N/ q: g& v, C
  rect_t rc;
9 ?; j9 `! H9 Q, X  vertex_t w;
% h' Z: w. z) B5 v  if (np < 3) ; ]0 k2 x# i' B: B- B
  return 0;
& S+ ]# e6 f( d+ \' r  v  vertices_get_extent(vl, np, &rc);
3 v5 T; d: `* J3 L6 f: g5 ~  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) . T8 ]7 g% l9 [, C
  return 0;
5 A( m# M( C* e: h& H; b8 U  /* Set a horizontal beam l(*v, w) from v to the ultra right */
8 Q6 b1 h0 ^+ Y4 j7 {% `  w.x = rc.max_x + DBL_EPSILON;
& ]4 \" C/ M! [- m  w.y = v->y;   C* q6 h) \6 b6 L* f( L" r& o2 X
  c = 0; /* Intersection points counter */ ; @" [  a+ d9 p( ^1 P$ n  o
  for(i=0; i  5 {. h' L3 }% w2 Q2 ~& Z' O( @0 r" j( \
  {
; J; w: h0 N& K$ Z) H7 T5 ^  j = (i+1) % np; ( l8 j+ ^7 P  V- F( x
  if(is_intersect(vl+i, vl+j, v, &w))
% S& @0 ?8 q/ k2 E* P/ \8 K& j  { + A& A" B2 S# ]
  C++; , z! M' V% ~& F/ x
  } 1 m% j7 M' l3 d+ [
  else if(vl.y==w.y) , p- {/ v# l+ V1 b# h
  { / f  J* N+ ?0 X
  k1 = (np+i-1)%np; , f( `. \+ {" t, ]" f
  while(k1!=i && vl[k1].y==w.y) # y8 M; J9 M0 U, V" r
  k1 = (np+k1-1)%np;
# a# `( }  I( W. f# D/ j# }) i1 W2 |  k2 = (i+1)%np; 4 s6 f( n9 j8 d" K, c6 H% z
  while(k2!=i && vl[k2].y==w.y) 4 H: f9 e; V% t1 |3 f. |
  k2 = (k2+1)%np;
! G: \0 H9 t4 j9 b! Z- \1 N  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) 2 |9 k: Q  ~: T- O
  C++; " w3 L! U  V; ]; m4 \& }
  if(k2 <= i)
9 |) q- j! J. n7 C5 E  S% h  break; 6 G, W2 A/ B7 Q/ R  A" \+ x- i
  i = k2;
4 @1 G1 K8 y; q7 j3 L: u6 }: t  } " Y/ T, W6 t% m, n9 ^- T" B9 u
  }
8 |; t# ]1 w& O7 F" y* e  return c%2;   t8 N3 ]9 Q) N" R  H
  }
4 }( T3 m  O. k6 R1 M4 J
5 f1 Z% O5 C, E' E2 H3 Z5 A" s) v. L, v
  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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