返回列表 发帖

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
; z& w! V! o. ~9 C" z8 L. n8 N5 K  ^4 }8 Q  l
  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。4 j6 }8 W7 \- \  t0 G" W8 P

% o& |# X6 [2 B  首先定义点结构如下:/ {6 \; u: ~1 _' q$ H' Y

& u- F+ W8 E: z; v; J以下是引用片段:7 P( d' i4 W! F) K: h- t2 C5 V& a
  /* Vertex structure */ 5 p# @9 l2 ]8 p2 h
  typedef struct
5 z3 K. o/ F5 f: P0 R+ ]  {
& [  L% D  `' n5 a" F  double x, y;
. X% I0 e! U: G/ K% a  } vertex_t; : M9 K4 S& G" V' I- b
, \- [# ?& k; p' m: n4 Q! I
" v( T! G4 ^5 t! Z: `$ W) v
  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
$ z) p" e. G. k) v4 j
6 n3 d0 n, B5 ?( V! w以下是引用片段:
" x) z) o7 E( ^& _/ r1 z  /* Vertex list structure – polygon */ 8 J- z; t& ]9 t" H; z
  typedef struct
' H. v2 P9 N% |4 B9 O" b1 ~  { " m- y5 `0 p# Y
  int num_vertices; /* Number of vertices in list */ 8 K1 K* h9 d4 A
  vertex_t *vertex; /* Vertex array pointer */ ' H/ [5 G9 J8 o/ z
  } vertexlist_t; + [! Q) p; R& V& b
1 n0 n6 s$ F0 h" o6 Y  ~
2 ^8 P0 a9 z3 h2 Z8 B. P8 `
  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
. N+ b. A/ j+ H3 _
0 s4 n+ u5 k: _以下是引用片段:
! [- {* s" e: k4 `  /* bounding rectangle type */
! ~* m7 L/ T0 G$ U0 M" d3 O; s/ C  typedef struct
9 ~- Y0 B5 e$ B; c" C) W5 v7 d  {
$ b3 q5 j! L0 w: u+ \4 @  double min_x, min_y, max_x, max_y; 9 @$ X% c5 x$ `1 @8 E8 ~
  } rect_t; 7 s. M( }' W5 A6 l4 p  g2 p: F
  /* gets extent of vertices */ & s' n. L. M0 `8 o+ k
  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
# P( R" j& o; k1 Y  rect_t* rc /* out extent*/ )
3 @( @7 N$ @2 g1 p4 o' a" u  { % q3 o( j, c. f
  int i; 9 u( G$ I+ ?2 R& Q1 K' O! F* Y" b
  if (np > 0){
7 X8 o! M  `0 D& ~# v! Y  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
2 N! T, \) Q0 K# \6 D, \9 q  }else{ 9 A1 ?2 d8 F6 S6 L- Z2 {1 n
  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
& I. f1 ^/ G8 h$ x5 q( a7 m  } 1 c  ]$ r5 ~! X
  for(i=1; i  
" y& y- G. h' B0 D( Q  {
6 N) n' U' h2 c/ I( H: i2 `  if(vl.x < rc->min_x) rc->min_x = vl.x; ( Y" I+ D+ n1 P9 r! t( ]
  if(vl.y < rc->min_y) rc->min_y = vl.y;
$ O6 h& z, {; X  if(vl.x > rc->max_x) rc->max_x = vl.x;
) p% L" ?0 s, C3 Y  if(vl.y > rc->max_y) rc->max_y = vl.y;
) o7 A0 Q* I& r  }
- c" Q8 ], s  E* p- V  }
& k) o, h( _: y' g$ X
9 l, u; {9 N2 j" p
% n+ L/ H' Q& P* P  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。1 [* c, `7 Q) P3 z, ^
" _$ u2 V9 m+ [! S
  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
8 Q0 I8 V5 }- o, i$ a' u# S/ Y! d# B7 t
  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;* d, B3 ^9 P9 M+ m" P6 @' ^
" z6 ?0 {$ T/ O6 a- W4 G1 g% X
  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
3 B+ }0 I: G- v
& H, r+ y! f, {) L5 V0 e1 X! J; P以下是引用片段:
! Z: j9 K  J* C  q0 F1 ~; d9 ]4 d7 [  /* p, q is on the same of line l */ ! e7 \* b: h! x$ o
  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
2 c! L; g) u' |1 x: ^$ V  const vertex_t* p,
9 j" P2 a" j; u# V9 n* L- t  const vertex_t* q)
- c1 ^8 c& |" v; Q1 k  { , b) d. a7 L7 @: F3 A8 m
  double dx = l_end->x - l_start->x; 0 j6 [& g( q4 V) k9 `0 S0 E
  double dy = l_end->y - l_start->y; & K  ~$ q" V7 I% V' E
  double dx1= p->x - l_start->x;
% a- D& l' n4 m5 ?6 }  double dy1= p->y - l_start->y; 0 |  Q% z" o3 n  |5 p
  double dx2= q->x - l_end->x; " |% q, _1 x9 E
  double dy2= q->y - l_end->y;
2 u" x: h4 R% x$ F# {. T) `  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); 7 z# y7 C- O0 J( f' w
  } ! P- P' ?+ l8 h; T9 }0 v
  /* 2 line segments (s1, s2) are intersect? */
; e/ D4 Q$ x% q* `0 U4 E1 b* R  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
2 k' G: ~- [0 b! I  const vertex_t* s2_start, const vertex_t* s2_end) ' ]/ I* c/ k! k& }( E
  { $ Y& j- j% V/ v# C0 q/ B
  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && 2 z, z9 y! G+ t$ u+ `: O" `& ~! V
  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
4 B% F) H, N: ?4 v5 ~8 k  } ) |( z5 {4 ?9 p
  i1 N2 t1 `- m9 \: O; r
/ R  u0 x: D! O4 u' r! V; U9 }
  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:9 \" I# D4 k: M/ e+ j

) u8 }) y# @8 z8 e( s以下是引用片段:3 |4 ~2 n' X# m  N, r4 v& }
  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ , d1 D4 m! `! n( s+ I
  const vertex_t* v) ' h- R* j' e9 Z6 _1 B- f
  { ; j4 A; j. k. ]5 Y7 [/ N' r- Q
  int i, j, k1, k2, c; 7 A5 f, \1 r% l4 B- H( A  T5 K1 Z/ o
  rect_t rc;
5 D- `  N6 A7 k/ |. x  L0 k4 f  vertex_t w; + d! v5 O/ a* i8 ?5 {
  if (np < 3)
- i0 ]. l) a; j; w5 |1 x( L* q  return 0;
3 I( G6 ?6 a  [7 \4 a" _; H0 B  vertices_get_extent(vl, np, &rc);
  S3 \& n5 h' j- T4 e* A" B  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
6 ^! E" j, P2 l. B5 U2 a/ r  return 0;
6 [1 D) b; f' H) t  /* Set a horizontal beam l(*v, w) from v to the ultra right */
* k& }# B+ [( d7 y  w.x = rc.max_x + DBL_EPSILON; & u, C- r. S4 N. h/ w3 ^, C5 }
  w.y = v->y; & y$ i6 k! S* {  @+ Y: F
  c = 0; /* Intersection points counter */ 9 q$ e" J$ C. F& s# C
  for(i=0; i  9 R3 K! E+ m/ X0 @$ g. f! N" Y& r1 s
  {
& ?! o! I4 h! ~) V; o5 d  j = (i+1) % np;
# ]! x% K- L$ v4 S$ Q0 b2 @1 T  if(is_intersect(vl+i, vl+j, v, &w))
+ I. D& f5 O) |( I) S; B+ r  {
1 F1 n( Q4 J2 C  P: U- f8 W  C++;
8 J, {4 \  S) ]3 u4 i$ d  }
. I) T9 K+ i- ^8 ~% ]  else if(vl.y==w.y) : O) [) o( m6 z3 A7 f
  { % P8 n  c9 V7 s: A# O0 q! U
  k1 = (np+i-1)%np; * E/ H1 x; P8 @. y
  while(k1!=i && vl[k1].y==w.y)
0 D7 H* U2 q* o+ m- C  k1 = (np+k1-1)%np; / a8 L$ A$ P$ B8 h/ ?. U& A
  k2 = (i+1)%np;
, g* m# F+ t% u+ [% X  while(k2!=i && vl[k2].y==w.y)
" p% O: L/ p4 O  k2 = (k2+1)%np;
) u7 T3 I3 b4 w% W- ?3 V- W/ K  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
6 a, [/ \9 S1 ~7 Y  C++;
4 T8 {9 \$ r6 H( p; Z) F: L) Z  if(k2 <= i)
7 `2 c' s6 Z7 d' x7 M+ i3 U: k7 f& q  break; 1 [% ]4 g; N; z! A8 w: G3 ]; A7 ~
  i = k2; 5 k9 n, k5 [. B) Y+ ?) N
  }
1 |+ m  }# v, y* E: z# K  } 9 l/ t( I% m* c* \7 I9 _
  return c%2;
+ b- @. H6 \1 W# N. b% ~  } & {6 g% V4 j" P! k9 }
/ Y+ {5 q1 s& P% m: T8 U

' q: r( R$ \& k  ~9 B$ @  W- C- B" n2 G  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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