返回列表 发帖

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。  D  |  V9 y: u+ Z" x9 [

/ L* x: B* D1 P  ~1 ?  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
+ n( ]9 p* _7 h% Z4 t% `, l% u  ^2 z4 Z
  首先定义点结构如下:, z1 `9 L: s" Q2 Q. r* k
5 C# b5 A9 a$ k' @' o' e0 r
以下是引用片段:
/ [( g; b! N$ b  /* Vertex structure */
+ D. Y3 F/ S+ C0 f8 K/ Y; o4 j4 `2 f  typedef struct 5 m: N& V7 g( L5 {( F! D
  { # [: O! E' M- X/ P' r; ]1 j
  double x, y;
' e% `2 C' S5 k8 O' x# r  } vertex_t; / n% B( n' L" t4 _% p& v3 X: n
8 s3 A5 W1 N& k7 R

% t5 j- X* Y8 J8 e8 G& a  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
' m+ m9 n+ g/ m8 G. ]
+ K& o. E' [. P! e+ n, q' T以下是引用片段:5 _0 h/ z* V/ Q
  /* Vertex list structure – polygon */ - x# L6 X& c) V4 i
  typedef struct
0 J: F" u; N/ B: E# y8 W, L  { ' ?  A# A. O" ^# u( p
  int num_vertices; /* Number of vertices in list */
. K7 g7 R/ E- T, b: x- b' m( _- e7 ]  vertex_t *vertex; /* Vertex array pointer */ 6 q' M8 x2 a( K& R" |& H
  } vertexlist_t;
% A! h; @9 s& m. J# R
7 `) G  ?1 |% x% H" n( n; [) @9 C+ I( g( I9 g/ T
  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:( ?4 E$ \7 z. u
) i/ T: d; B5 A/ M8 d  ^5 N0 h
以下是引用片段:
3 @1 h! N6 N  \0 ~2 c  /* bounding rectangle type */ : B/ ]" M* o: A* g* v$ o
  typedef struct " _. j, N1 [, M7 _; o7 n% M( X
  { % b8 ~+ w( A  b' v. a7 t& u
  double min_x, min_y, max_x, max_y; * ?4 F, S; r  i6 v6 b
  } rect_t; - C. p3 G. O0 C5 _+ t8 I$ Y6 ~
  /* gets extent of vertices */
! e# b4 g& _( L- F. y' i8 \8 Q! z  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
- `1 G0 e1 ^  v5 z; e$ R! y- P  rect_t* rc /* out extent*/ ) 9 m/ d" d9 L+ ?( _8 u9 |
  { 3 h3 q5 g% F0 C. N/ ^. W
  int i; - b0 R0 \% k5 m& ?% c% W  z
  if (np > 0){
. n% ]3 Y, I. b; ^5 ^  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
: p( y7 e$ D- N0 U8 X  }else{ . D, N0 A" W% R% A) T) L+ L
  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ 9 M  M$ h9 C3 Y# {8 w
  }
# N- I  N5 z: H, X- \  for(i=1; i  
* h' K4 O. L3 ?/ o: e  {
1 T7 N, ]2 y2 y/ A4 B$ [8 q  if(vl.x < rc->min_x) rc->min_x = vl.x;
5 M4 @; _. Q7 [8 h1 {  if(vl.y < rc->min_y) rc->min_y = vl.y; , b0 N9 N; P) B! M9 U1 v! [$ {
  if(vl.x > rc->max_x) rc->max_x = vl.x; ) w% e. m+ A% t# G" U5 U# k9 T
  if(vl.y > rc->max_y) rc->max_y = vl.y;
- K. a8 j$ C, [$ k/ F  }
# D7 X$ L. T1 S7 v6 u, g1 R  } " Y1 z1 n) R3 S' D, v- ]0 O

7 L- g3 V+ f: k* A0 ?" G
; h/ ]" U8 c" I: {  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。0 i1 x5 V+ ~- z) ^( R: k

/ V+ D; Z4 o+ F$ ^0 y6 L+ m5 K" Y  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
! J+ ~, y& r! C; T
! X( U2 J! T1 ?( a; e! ^  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;9 T0 a# ~# j- ]' W# d

  I8 K' D7 ]+ _  ]' T  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;& Y# X% d9 L. i' ^( i0 H

7 z7 f5 [! ?& t/ B2 q2 R; J以下是引用片段:
; q4 V" u0 H6 \  ^0 K  /* p, q is on the same of line l */
* l; K. H" C; Z( Q# G) h  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
2 B* J) U8 H: b9 z# C' N/ P( w! b- O  const vertex_t* p,
" a4 T. l0 F* i0 C. Y1 _! ~* K  const vertex_t* q)
/ Z& ^" `' Q; U' G' A9 n5 j' |  {
& I2 X& ~+ {# A2 ^1 V! H  double dx = l_end->x - l_start->x;
: I; A5 Y! T& a/ @  double dy = l_end->y - l_start->y; : j% R9 d% J) X0 o% I9 u$ c
  double dx1= p->x - l_start->x;
7 c; e% s( c9 _8 ~7 L  double dy1= p->y - l_start->y;
4 L' L9 E- L- w! e& S/ @, ?/ j# @  double dx2= q->x - l_end->x; & p. F" X: a7 W4 ]+ |
  double dy2= q->y - l_end->y;
" n/ E, k% E" I, h3 `$ j  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); 7 C& x' n& a- w% h% w' s7 M1 @
  }
3 Y5 [& F4 k' r  C  /* 2 line segments (s1, s2) are intersect? */ & Q5 q) }3 B  N5 e" A5 f! p
  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
: r4 B* S8 c5 M# Z5 L* L( r  const vertex_t* s2_start, const vertex_t* s2_end) ) p" D" t+ |1 ?% [+ U
  { / T$ l3 [( ?3 P0 e  {) d
  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && 0 h# S# L' d+ T- T. j+ }, [
  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
7 \: S2 g. v- g2 n  s( R  } 6 p1 s1 E5 \# O6 H" M2 E4 b! u' `" Q
( r, W" Q& c8 m) `2 h
" D' D/ j; ]. J7 g
  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:; a6 k" Z4 Q( d. k! v. B# d

+ S; i& z: p6 Q2 N5 Y* C# G% B以下是引用片段:
' F! |$ N+ e* E' |" I& a$ Z  s. z  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
8 J. j# _5 x  Z2 Y  const vertex_t* v)
  D9 q' f, ~) m$ c/ k  {
3 W9 e2 [7 \: y4 o  int i, j, k1, k2, c;
+ N% K1 n+ e; O: h% N$ N7 S' ^  rect_t rc; 2 i" b/ r- M2 v: b7 c
  vertex_t w; 0 G2 n/ h2 s; m4 d) r  L8 y& z: k
  if (np < 3)
/ w( G; F( S9 K8 e$ W/ w6 O  return 0; ' q( J9 K8 a  t
  vertices_get_extent(vl, np, &rc);
4 {4 Y: V( ]" G  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
' a# e" [+ g6 ^' ]# K  return 0; " e4 t3 d. s/ T+ f
  /* Set a horizontal beam l(*v, w) from v to the ultra right */ - R, z, e% @/ d
  w.x = rc.max_x + DBL_EPSILON;
0 r- I. ~3 C9 p7 X6 R( r  w.y = v->y;
$ s4 l5 M. e  g- v4 A  c = 0; /* Intersection points counter */ : V2 y2 H/ Z7 T5 d* y' f" `# W
  for(i=0; i  % C, ^" B; \6 u
  {
0 a# y1 Y" ]  t! @7 T* M+ R5 S  j = (i+1) % np;
5 l/ x, ~0 P  k' h! Q6 O1 C& z+ p) C  if(is_intersect(vl+i, vl+j, v, &w)) 2 ]. s7 m5 q  l5 Q  u" `: b; L: c
  {
5 {3 O7 O2 z; v8 ]; U  C++; * P- }# k/ X& N* i
  } 1 W) I7 m* o! G
  else if(vl.y==w.y) ! [2 K  O. ]9 l% h8 J7 F1 h
  { . }8 m; \  h2 e" e
  k1 = (np+i-1)%np;
1 A' W5 i; P' p/ E. r% u9 v  while(k1!=i && vl[k1].y==w.y) 9 K( a9 Y/ _& d! A3 p
  k1 = (np+k1-1)%np;
# e7 {5 {& J9 j1 p  k2 = (i+1)%np; ! `9 ?% h' h, W0 \' e+ h! z
  while(k2!=i && vl[k2].y==w.y)
5 ?. p( t' G3 P) ^; ]3 f) t+ G) J  k2 = (k2+1)%np;
4 @! K2 d- f! [3 @/ H  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
8 c1 P6 ^& c. }$ g  C++; & h% v' N+ X, c  C' k& a
  if(k2 <= i)
- U% Y* p. L; ~, i6 e: q  break; + `, G3 |( V" c
  i = k2;
* v2 [: C5 v0 }$ l1 q  } ; s! I# |- D! m& t
  } % J' @! v% P+ F0 f, p$ _! q
  return c%2;
2 a* m+ B7 I, O% C" V$ f  }
* b/ d  m6 }4 [" T
+ r! E1 x1 @0 W1 h5 O9 Z0 M
& r: j7 c* d, O7 W/ A; T  I) n  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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