获得本站免费赞助空间请点这里
返回列表 发帖

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。& B3 c# n7 a5 o4 w
) d! u( c' d; l- m2 [/ ]( j) ?
  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。' Y' }) `' D! D$ P% ]( Q

$ Q! C8 d* o$ a, v' `' l  首先定义点结构如下:1 i# X: _* @! n; [) ?. S
: P' ]* l" W' A5 V6 \7 I  v# z
以下是引用片段:
! I" A# X% f* E) i5 r; N/ O* s# s+ V  /* Vertex structure */ 1 [; {8 r/ u8 j$ b3 Z
  typedef struct
) L0 s+ U5 m4 s) T4 C# W  {
$ z- a- h* m* K  double x, y;
3 N# t5 c5 a& ~  } vertex_t;
( q/ [* Q8 C, I6 D7 e. U( _
- k1 U! k% y% [+ a7 |
  A9 t. @& T) F- z5 P# f3 t  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
3 ]- b$ r7 @2 y( t* j
( P% u/ T# _& t& q# @, {以下是引用片段:2 D% H4 J, }& a9 ?. x, p
  /* Vertex list structure – polygon */ * i8 {- H" I( k  C; R! S+ p
  typedef struct
* t, p7 J/ V+ K* L( ?2 Z  {
0 u8 Y0 a+ P$ _0 g- c+ I. _" F! S  int num_vertices; /* Number of vertices in list */
  \+ T) Y" z) t: W  vertex_t *vertex; /* Vertex array pointer */ ) w2 L0 Y2 G" S# o
  } vertexlist_t; / R) x3 M3 r3 E% ]- @
0 R( Z+ M+ Q" X, Z% U6 ~
' a& R4 k7 [, e
  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
' j3 ~- J3 b0 n( P# f4 n. Q1 q. C& P) m, t4 l- @
以下是引用片段:+ ?: {7 p% N' T; a. p0 P' u
  /* bounding rectangle type */
  j/ ]+ t- O4 a1 E6 d. G' N/ @  typedef struct   p8 r! ]) O; y  M% z! K" p% U
  {
6 R) T0 j, p. y1 @& J# l0 t' q  double min_x, min_y, max_x, max_y; 2 K: j" a4 I8 M1 E: x+ D' k7 R' S9 X
  } rect_t;
* x: h+ R! }; i8 h  /* gets extent of vertices */ 3 f/ c1 X7 i$ }8 ~* m; r, H
  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ # l- P8 w5 t9 y4 q/ d! R2 q
  rect_t* rc /* out extent*/ )
- R8 q( w, v& z- w2 F; y6 I/ m3 Q0 Z  { ! l1 o$ ^2 D( S6 C% [
  int i;
, n6 j; T6 t4 `7 Q: t  Y  if (np > 0){
* A$ o3 k* e7 g2 h- I' I7 i; O4 z  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
6 z' x, T% R# x7 Q- w; ~/ y  }else{ . z1 J2 v- U% _) ]/ h5 `
  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ - A: a1 |) T# M6 u/ u/ y
  } & Y% T, k: H2 W5 k! t1 s0 g9 [
  for(i=1; i  ) C0 g: G4 \/ W# X  h$ t* o# `
  { ! {$ U' q" H4 S  s  U
  if(vl.x < rc->min_x) rc->min_x = vl.x; , J& _$ ^# _. E
  if(vl.y < rc->min_y) rc->min_y = vl.y; 5 O1 D3 X5 {, w
  if(vl.x > rc->max_x) rc->max_x = vl.x;
- B7 Q3 z& R( B& S* E0 @  if(vl.y > rc->max_y) rc->max_y = vl.y;
( _7 y2 _) X, p3 }" }  }
4 r( M7 O6 |9 E" y* c2 t- Y  } # W! v0 F5 E8 q" u4 A1 m, R4 V" G
9 H1 a3 o$ |: X* `; y

- o/ h+ G! D' e6 r  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。; V; ?: t# J+ @  [- O8 V# \

# _" c) K, R5 i+ i' X  T  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
6 ~6 q/ y6 s% \0 Y8 k
- X2 E6 a5 v- L! E$ a  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;/ s2 H7 H! _  d  V( {, v( A. G

5 j4 x# B% I4 `  j. s5 K0 s5 w  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;( ?& C& f0 T+ Z0 r0 Y! N: T- P

, ?9 a* K$ x2 i+ |* t8 m0 D5 G以下是引用片段:2 a2 f0 J3 f$ B4 p
  /* p, q is on the same of line l */ 3 e1 D5 r  s# r( \
  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ 3 O3 e# D! I' u/ K5 q
  const vertex_t* p,
* q& `* p% O  d' \& I% ?- G  const vertex_t* q) # i3 K; U7 u1 D( ^
  {
- N6 F6 t9 i2 q5 x  double dx = l_end->x - l_start->x;
( j% x! U( f# T0 u2 p) n( v' Y  double dy = l_end->y - l_start->y; 2 ~2 E% s8 T7 [3 A
  double dx1= p->x - l_start->x;
9 R5 _3 t0 Z: H! h1 t7 k, B! I( J  double dy1= p->y - l_start->y;
8 r' S% `& U  \; T- x/ |$ c  double dx2= q->x - l_end->x;
6 ~7 `& x- C7 e* w2 x  double dy2= q->y - l_end->y; 0 Y( ~7 S& Z5 c+ Z
  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); 1 N; ]/ T( o8 {& K: \
  }
' m$ W: g& X* U1 P. W3 l- v7 L* T  /* 2 line segments (s1, s2) are intersect? */ 5 a/ m$ J. N/ ^+ s) {. m# I
  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
- |" r+ I, E/ ~' Y7 |+ G# Z$ \  const vertex_t* s2_start, const vertex_t* s2_end)
! j" E& _5 O$ a9 D' R4 V; y( c& L, O7 b  {
0 V5 o. Z( o: C, X, k  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && 5 E( K! l( d. t7 `4 ?  ^4 [; r% l
  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
8 y/ O/ P  W: _" c  }
7 q# [" W- a  \1 P5 x" Q5 K6 Z8 P; V3 L
5 k4 l0 c4 |% ?3 q3 a: q" [  Y+ O+ T5 `
  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
( {# q6 p# }+ H
+ P% {# E" g: ^以下是引用片段:/ R6 |$ V9 o+ e0 t$ ^- W7 K2 y9 n
  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
  D3 r5 j  F  \4 H2 y) [9 y; o  const vertex_t* v)
4 f" s3 r' [6 \# ~+ w1 @  { 7 z+ r1 s9 e8 ]# T* p+ A! S# D3 B' H
  int i, j, k1, k2, c;
3 @1 l6 u  t, {# y" z9 k9 H  rect_t rc; % i( W6 u- y5 ?7 B2 C
  vertex_t w;
, \1 M  \- V6 M/ d8 C  if (np < 3) 2 b8 ?5 L9 W2 H! O
  return 0; 8 L2 a0 Q; z! I9 V2 P' A
  vertices_get_extent(vl, np, &rc);   R( p5 I* l/ ]; k% O% m+ Q& S
  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
7 J$ P% m1 Y) P% P& M0 g% O  return 0;
- X8 [, g& a3 b) _' s  /* Set a horizontal beam l(*v, w) from v to the ultra right */ 3 h1 A7 G! q4 R, ?% k( A1 K
  w.x = rc.max_x + DBL_EPSILON;
9 b0 N# N# v4 S' {  w.y = v->y;
! \$ |# d- Y. x9 w" T4 k/ T  c = 0; /* Intersection points counter */ 5 f- G& b( J5 f* N* I
  for(i=0; i    ~# T% ~) W* i! D
  {
" d0 [/ N$ X. v  j = (i+1) % np; 4 ^) d% x/ ~7 F, o  C
  if(is_intersect(vl+i, vl+j, v, &w))
, _! J1 {4 X, r. }& I- S  {
2 t" W( C- j0 j7 A7 P$ q2 [  C++; 2 E4 T; o' i. D1 x5 D
  }
  \" v* _( M6 s, l; `# N  else if(vl.y==w.y) & H8 m/ b. \3 n5 ~! v
  {
1 l- ^, W  X6 s* F# O  k1 = (np+i-1)%np; / R& ?" W6 Q$ \# S
  while(k1!=i && vl[k1].y==w.y)
" C* c9 |$ h7 e+ K( \& i  k1 = (np+k1-1)%np;
9 p; c5 w5 T6 p5 i  k2 = (i+1)%np; % o6 X. v8 f' x% @5 s+ K
  while(k2!=i && vl[k2].y==w.y) ) D0 K5 `- o0 q/ t) C
  k2 = (k2+1)%np;
6 G& n( G* P$ [  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
2 o: j) L9 C0 J  C++; + [# _; {+ [: J/ _( Y8 x/ ]
  if(k2 <= i)
2 ^% E4 m9 z1 C: h; l  break; 9 A8 e: g" T. `+ Y: f
  i = k2; , P* H9 z0 c$ U1 e+ o9 V& D7 ?
  }
' i( ^4 S; W% r/ ~% D7 b  }
! @- r7 k2 {' V1 Q, Z  return c%2;
+ W3 q7 I* }1 }+ Q5 j: G1 T$ Q3 z+ X/ U  }
# a0 L+ z3 Z& Y$ D" {5 r% Q2 m7 u+ P* {4 L1 E  G& O. z5 N) Y
( N; d9 |$ o. ?9 e  h& n1 ^' F# D
  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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