返回列表 发帖

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。- E3 b7 n  E) b* P; `
" [5 K6 K! t  e; h, G6 S9 z
  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。3 y9 R* \- v7 d
6 K- W1 f4 E) `+ g1 h; q) i) O( I; _
  首先定义点结构如下:
  ^) c* a5 D6 l  z$ t: n+ E0 L# I  r+ P9 T) U" }) F) O6 Z
以下是引用片段:
+ K4 M" y$ I3 I9 b! y& s+ w  /* Vertex structure */ % {; u  F; B3 r. j
  typedef struct
9 ~3 i. ?$ D0 E4 }, ?  q  f  {
$ Q2 T* m) R/ B# u& {  double x, y;
' }- E1 |" J7 }! r6 d8 a: }  } vertex_t; 9 g0 T6 P& q7 @/ Z

+ r  [2 c+ _0 o  f4 C  l9 J4 N0 C9 }" b! ?! L; [: T8 X
  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
3 n; ~$ f1 h4 m$ C5 g
7 X; z2 f$ i& Y$ v以下是引用片段:. A/ d! d/ P0 N; v% l- o3 D  x
  /* Vertex list structure – polygon */ 8 f0 F6 m# |4 N- f) w
  typedef struct
) J$ d2 }9 [% j$ n, |  {
, b! j& K; T9 \; ~$ ?6 v5 M2 Y  int num_vertices; /* Number of vertices in list */
+ t. L3 t' B) V/ H6 H  vertex_t *vertex; /* Vertex array pointer */ 9 t  }- f) l! M; ~
  } vertexlist_t;   i! r& A* _$ K6 R/ H# L0 s
  Q+ h  J1 h. z

4 G8 n1 g2 Y! Z( v( m  I  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
" i1 q& y7 G3 R! d0 F
) j0 s7 ?  c1 d. u  j$ ~7 [7 ]& |以下是引用片段:
4 n3 p4 c/ n9 U  k7 h; \: Y; L3 k) I  /* bounding rectangle type */
# [/ a2 C/ \5 H) K  typedef struct
& E, o: d5 O! X2 e3 L: I! R  {
8 I" J5 |( u2 a  double min_x, min_y, max_x, max_y; # A7 d: X% G" n0 e- H2 z& h
  } rect_t;
; S4 S% X) c7 j1 k" V  /* gets extent of vertices */
2 j8 K$ i5 f8 ]: C- r8 I/ X; f  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
* [4 e' N# b# L$ M  rect_t* rc /* out extent*/ ) # \( a0 f0 V1 ^' y" j' O7 M: n' Y/ F
  {
0 n1 r, n4 r+ t  U1 L3 t  int i;
; ~( ^" n# T  }4 d- Y9 t  @( u  if (np > 0){
- w( b* q! g4 B+ r' K0 Q9 L  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; / i) z/ ~6 ~8 \
  }else{
5 s5 B7 b4 ]. U# ^+ H! K% @5 Y  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
  m; `0 M3 S9 E8 t9 e) e  }
7 k: q4 D# |8 L+ v& `/ d5 Q4 @  for(i=1; i  , e' y3 T( k$ p5 }6 ]$ F' t
  { 7 ~6 ]% @0 M: z0 b0 w
  if(vl.x < rc->min_x) rc->min_x = vl.x;
6 L/ J2 U  S, U, b) m  if(vl.y < rc->min_y) rc->min_y = vl.y;
! a, L( e7 g6 c5 g* {! K  if(vl.x > rc->max_x) rc->max_x = vl.x; : `$ b* H7 a+ D0 _
  if(vl.y > rc->max_y) rc->max_y = vl.y; " T- s' R$ O4 }+ s6 N% d
  } . c) a9 B+ D  d6 n0 g3 {. f
  } , t/ G- U; i$ m  O9 |3 h$ `
5 S; q% K7 e( N) _
2 N- A- ]8 M4 ]' I: a" c
  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
7 i5 M# D, o: J) L2 y8 Q& D& u; z. z6 R. U+ C' w
  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
! I) E0 _2 w9 \
8 s# W& \' o  O5 e- q. ^! r  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;( `* C$ _8 W5 ~! u5 @% r  R
9 [4 r' S  W4 B: @* J, i. `
  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
8 G' V) i/ p8 m4 ^* Q$ G  H  b. t$ O" j" r, l
以下是引用片段:
7 V* Y/ P# A9 f/ W# D, H! i  /* p, q is on the same of line l */ " J) b& ~+ [; W" r- q
  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ : l5 Q& e9 a7 q' y5 u" z+ p2 l
  const vertex_t* p,
6 ]3 k$ {9 Q, Q- k) R4 ]2 C  const vertex_t* q) - u% ~6 r0 ]8 q1 p/ p$ [0 `
  { / R8 q# ]1 Z, n) H2 m
  double dx = l_end->x - l_start->x;
( X$ E0 u3 e8 U/ B: ^" g2 ~: K  double dy = l_end->y - l_start->y;
* h# M1 }% m. Y& _# V  double dx1= p->x - l_start->x;
( v( w9 V3 U$ M& `  double dy1= p->y - l_start->y;
" v( e" }) d. {, ?; t5 p  double dx2= q->x - l_end->x;
" e* ~; g9 d" O  double dy2= q->y - l_end->y; 9 _  a+ V% I; X
  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
6 e2 N9 W: b/ W4 J  } - D9 d- M1 @% }: E7 V$ S( L
  /* 2 line segments (s1, s2) are intersect? */
5 a1 b0 m! ~1 g4 F, ~  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, ! d2 R$ J. C0 O1 O8 C
  const vertex_t* s2_start, const vertex_t* s2_end)
* F) I1 Y1 a# B! y$ l' }9 c2 V  { 5 I, G# N' b% Q
  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && . b0 G& a9 W/ y
  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
, Y4 x" d- u* `* l, a  }
" j/ s7 ?0 \. ]$ D' x8 X) z& H" j3 N+ e2 D7 ^

9 C" w2 d3 V8 w, Q- c  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:/ J- `) X5 i" ?+ |4 Y

' P7 l* l( q4 ?7 [$ }7 Q  X. p- t& t以下是引用片段:( z! o0 X8 K/ {$ \
  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ ' X. I5 v% p& \3 _/ R! j& R
  const vertex_t* v) 5 T3 n' b2 x' {( t0 P# v
  {   A( j- h+ L+ }" b
  int i, j, k1, k2, c;
' B+ V3 T3 _9 L  rect_t rc;
! e9 q6 ^2 `; E, D1 g- O# v, x  vertex_t w; . {3 O6 A" d, F0 _8 Q$ i* h
  if (np < 3) / t7 ~; i$ D$ B4 ]  X5 n0 V
  return 0;
6 u3 {9 a! A$ v7 p2 q  vertices_get_extent(vl, np, &rc); , y- x* n5 g+ A" T" j; M2 J9 i# X
  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) 3 O- Q; I6 E; c
  return 0;
( L2 p1 Q7 \: c/ J8 o7 ?7 I9 }  /* Set a horizontal beam l(*v, w) from v to the ultra right */
3 q" D& e( v# ^' D/ D' R1 k  w.x = rc.max_x + DBL_EPSILON;
+ C) L+ F; w* v3 _0 j  w.y = v->y; " M; i2 Q/ t* y
  c = 0; /* Intersection points counter */
( d& H9 j& l" l6 C  for(i=0; i  8 k; K7 n% s: t% O3 V, i9 }
  { # \" b. D; o, V7 b% l4 g
  j = (i+1) % np;
1 z9 y8 M; @. @) O0 H( y: M  if(is_intersect(vl+i, vl+j, v, &w)) . N. [2 G  K: m# `1 M) |7 o- f
  {
! c' T0 F) ~2 I) Y  C++;
+ ]# V. \( R# }* ]) n( ^  A  }
  h& U! L( w0 ^  S& j) y: k6 j/ Y3 e  else if(vl.y==w.y)
1 Z" L$ t8 ~0 m3 Z& b2 A  {
% `( t: x% g; ^9 B  k1 = (np+i-1)%np;
  z) K: }9 {4 K8 Z1 g  while(k1!=i && vl[k1].y==w.y)
1 n& g; A$ a$ b: O. a( B& n$ N  k1 = (np+k1-1)%np;
, n; \2 W* b- F  k2 = (i+1)%np; 9 R9 R* Z) U1 u3 M' W. g
  while(k2!=i && vl[k2].y==w.y)
5 c0 O$ [! @# @" e& d( W- d$ U  k2 = (k2+1)%np;
) u4 W! F, ?4 u; Z3 x  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) 1 k3 M0 C6 w, Z' M* T+ U" A3 o
  C++;
" _( M  @2 b) v" A  if(k2 <= i)
: ~& m: ]9 s0 E2 r% {  |  break;
2 k0 f. ?7 @$ g5 b3 C  i = k2; ! a. @2 p* l5 s# y
  }
6 {' V, h9 L. @' U, w1 G+ S0 Z  }
& X- p2 s( \: G5 C4 M9 @. v/ W  return c%2; / Q5 z1 m; P' o$ s1 Q6 b( R+ f
  }
! K; G: E7 g# @# S: F
6 Y9 A$ W, B3 ^$ d' y$ u' p" p9 m: e' _$ q1 Q  N  b
  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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