返回列表 发帖

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。) a  P+ n# c2 ]7 e/ i

( K* N- m* f' q* w2 X" Q! |  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。! G, g- ~$ Y, A7 E* q

2 K* [/ Q1 p2 d, _  首先定义点结构如下:
  m: z( t6 ~: l/ U) J4 \/ i
( F5 B3 h0 z/ C+ M: {以下是引用片段:
$ e8 n: s4 p5 Y  /* Vertex structure */ 0 P' X% M' q/ O# N: s
  typedef struct
$ W3 a4 i: n# J  { 2 J6 c4 H! {  l# n/ C  F% d9 y
  double x, y; * d: n: B: B( y$ P- g- z
  } vertex_t; 7 ?. K' @/ y" P. ^$ C  J  ?
* H, J( ~5 i9 L& ^- A1 d+ H, T

( C& |& P' h# M: @) x, a2 M  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:  a2 k1 P, q  v4 I. B

9 H  D6 Z: m" ^; @% s- ~0 C以下是引用片段:6 A8 E" T9 P- @( M
  /* Vertex list structure – polygon */ ) ~( _# n4 V% ~1 Q1 l
  typedef struct % k' k6 Y# T$ _; n4 B& s3 V" b
  {
- z' }/ w8 S$ ^. K- m/ z' @( m  int num_vertices; /* Number of vertices in list */
6 v% N- ]2 Z# g  vertex_t *vertex; /* Vertex array pointer */
! G' ]6 t: ~1 ~5 G  } vertexlist_t; / w0 h4 \  J9 ^0 ]4 \" d

1 z7 W* N( \# ^! U' J, w3 ^3 N0 ~) l" F) U( R- v3 U# B  v
  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
! j4 i; d8 ^5 p/ o
6 F) R/ \( l8 @5 G以下是引用片段:
# m2 g0 ~6 I" Z5 C  /* bounding rectangle type */
& f; C& x- Y) N/ c* A  typedef struct : K" W# F, @5 W& a
  { 6 [: e2 p) p7 c4 G) ?; ?4 \: R
  double min_x, min_y, max_x, max_y; : D; y* O4 _6 I9 I
  } rect_t;
8 |0 Q  x) O4 T' G# p  /* gets extent of vertices */
% q& U; u; y5 A3 q3 a# F  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ 9 ~6 {* N0 |( y- q" ^( z
  rect_t* rc /* out extent*/ )
/ j6 K$ F( g1 U, |  {
3 ~2 P2 p( S5 p( C7 Z& k9 X  int i;
% b/ e4 B0 l) B' h# U  if (np > 0){
3 g% B/ K! R: c. w9 j& g' P  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; 9 V& b, _( I; S3 r6 P7 {7 C& t/ [; {
  }else{ 0 m; \- L; w$ |, |' ?
  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
& P6 i( Y$ r! B# @8 N! r' X" d5 E  }
* r7 ~  g  d+ O/ l" P8 k5 T, x/ R: Y  for(i=1; i  3 k* D+ p* R- }- _
  {
+ C' x5 ]7 v1 V! Q5 f5 ]* i  if(vl.x < rc->min_x) rc->min_x = vl.x;
6 b/ ^0 X! T8 t+ Z2 |) T6 I  if(vl.y < rc->min_y) rc->min_y = vl.y; 3 M& J0 t4 v6 @1 u' N/ d# X
  if(vl.x > rc->max_x) rc->max_x = vl.x;
* [( Q% W, G0 W( C, e0 a+ F: {0 S/ O  if(vl.y > rc->max_y) rc->max_y = vl.y; 9 \8 h$ Z) z; h; b
  }
* W9 D/ c2 ^" @7 J8 V- ~' H9 s  }
& B- V4 R& f' e3 }' T0 `# _9 l5 p" [$ k

( z. V5 b, d, o( b% u  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
5 G1 K# c2 h/ h: x) E& w
0 n1 v4 W" |4 U' g& T  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:+ o2 K$ [$ h3 {
* K  |: B, L. h  Y+ B- k
  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;; [. l( `  ^4 m( W# n# A2 d/ L# R7 I

0 T1 L8 e2 N; n0 ~8 I  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
0 g6 ^' b* R9 W, T  W9 A
7 U- F* I1 M( l* s5 Q3 G- [以下是引用片段:
% T, n2 V# |* U' r8 O  /* p, q is on the same of line l */ % k& S$ H; E: I4 p; i' F) t  {
  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
  F' S: L6 g% @! m: }7 a  const vertex_t* p,
! `" p; B% Z( C/ p: Q( p4 u4 B  const vertex_t* q)
' w# h' D* F! {( x" K, M  {
4 z1 G" ?8 k9 N0 r  l  double dx = l_end->x - l_start->x; & g  H' @8 ?9 B' f. d7 U+ x2 }
  double dy = l_end->y - l_start->y; - u0 k) Q) C( g& k, a
  double dx1= p->x - l_start->x; / o7 r7 z/ s1 d/ i
  double dy1= p->y - l_start->y; 1 M- D; a0 b( H% S1 y0 \$ c0 n
  double dx2= q->x - l_end->x; - I, K4 G+ w# G; }2 o; S% ]( _4 B
  double dy2= q->y - l_end->y;
2 `3 G/ ~# f8 H  S; P# F3 n, x" I1 r  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); - l. i$ l+ V5 M$ U% g
  } ( t6 _/ P9 n) d' d: r
  /* 2 line segments (s1, s2) are intersect? */ 2 L) q& Z$ W) U4 a( D
  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, " K8 z0 L: c2 Z0 Q& U2 n
  const vertex_t* s2_start, const vertex_t* s2_end)
7 N5 R/ f) M5 o( x  ~4 D' q3 U6 }% {  {
2 V* `$ b3 e# T) {( _  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && 0 T" G  U6 p+ H  _! t/ k
  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
- y9 D! G. V; p+ T* c  }
' h& A( c1 f- W  P: d/ B7 x: x& a6 q# x, Y% R; \8 R

! k. g! e3 q4 t3 J. u  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
+ a1 o& x* K# M" g; O
6 K4 O% w6 Y# l1 g以下是引用片段:! v) d, P, v+ [3 ]
  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
* I8 x, Q! Y1 w( F/ K  const vertex_t* v) ; c# V1 Z) b9 E; u
  { % @  w9 O& ]5 r# p7 o# ~3 B
  int i, j, k1, k2, c; 2 v: P, Z3 G% I( e' {
  rect_t rc; 2 y: }& U& [  h: M. a" o
  vertex_t w;
5 V  {  y& d4 T9 s" P  if (np < 3)
. G0 X0 K6 q% ?* H  return 0; 7 \! [! U4 u6 G) j2 g! z4 S2 O8 e
  vertices_get_extent(vl, np, &rc);
* t  c" R  U# T7 r/ `2 O  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) 7 X% l. b$ v2 a
  return 0;
$ b6 j% q  a( F6 t+ q  /* Set a horizontal beam l(*v, w) from v to the ultra right */
7 b. {7 X5 T7 v" N1 J- A, [  w.x = rc.max_x + DBL_EPSILON;
7 S8 {( A$ h! F  w.y = v->y;
# |& \' a) q+ u% \3 b! C/ i4 I  c = 0; /* Intersection points counter */
& e5 v7 ]5 g  a, K6 `/ q  for(i=0; i  
8 @3 L% Y# h; T" P  { 0 W2 O8 X- C: R7 U
  j = (i+1) % np;
. L( E1 S( d% f  if(is_intersect(vl+i, vl+j, v, &w)) & ~. w1 U; m+ c8 l! M
  { 8 x, R0 q) a: C( T* @$ E
  C++;
' G+ u5 Y6 B% P* M! G$ K  }
! ?( d! ^6 T* g  else if(vl.y==w.y)
! c* c! \7 [; [! q" _) U  {
1 ?4 V% @, f  K7 s  k1 = (np+i-1)%np;
- y0 W0 d/ P6 ?' x! m% ?& k- I8 i  while(k1!=i && vl[k1].y==w.y) " ~3 W# H, ~' d; ^, X# h- \4 ]
  k1 = (np+k1-1)%np; 5 N! V! a* c1 x2 _4 N2 L' l+ z+ R8 w
  k2 = (i+1)%np;
: }; Q/ g: q- G0 j$ _5 v4 ?6 r$ G  while(k2!=i && vl[k2].y==w.y)
' q& P5 n: C  P) v* ^  k2 = (k2+1)%np;
% U  T. F' F! W) p  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
% S, f  V- V6 ~# v  C++; 4 \$ z. }2 P# T" {
  if(k2 <= i)
* L6 X& p# _! b6 F  R" O9 q  P  break;
  I4 b7 Z- w; ]  i = k2; - x% H0 O7 a4 {5 B; `* e6 X
  }   K* r; t4 H0 z: J9 ^, j( R
  }   h' O8 b; T2 P% K
  return c%2;   |/ R7 N! a6 x5 \* ?4 N  E
  } , T6 n( V- H" |/ b$ c+ O% d
0 C% x1 B: v  n' Z' E: A

: R$ v4 {. c( S2 v" T  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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