返回列表 发帖

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

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

- v; y. U+ p" d/ h% p  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
! g" _& s; B; u2 L
0 V& m, r* `. @4 Q0 T' R6 L8 {3 ^  首先定义点结构如下:! A7 t1 Q2 ?" M9 [( \5 d  j
  T+ r- |$ z4 ^5 r# m, n
以下是引用片段:9 P( v/ q1 [4 R; `! s
  /* Vertex structure */ / e0 {4 A# }& ^
  typedef struct 6 @7 t4 c- [1 A2 Y6 R! F
  { ; a( ]% |/ ~+ ~  ?1 `1 J
  double x, y; , p- e( @. W$ j9 X1 D* }( f; {+ h
  } vertex_t; ) X, A9 j# s$ W/ g9 Y1 w% x
3 K2 [1 t8 r6 W$ B

3 P; L( |& R$ R: X7 n7 [  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:- ^2 L( P4 D5 F/ Y
% o- C; r- J% g# @& a6 Y* ~3 |- r
以下是引用片段:
- R, L  z) F% x8 J* `& }* l5 B  /* Vertex list structure – polygon */
9 ?4 a7 {* W+ V+ I; n  typedef struct 7 B$ Q5 r/ j, A, b
  {
( n# Q6 P- z7 L# M8 P! ]0 k- k  int num_vertices; /* Number of vertices in list */ ' d9 M. ~; v" C7 {$ L. o9 s0 L
  vertex_t *vertex; /* Vertex array pointer */ , f/ L8 v: Y$ I3 F% Z/ f) h
  } vertexlist_t;
/ O; m6 x  i2 W/ E4 z# q1 t4 r5 A8 b2 K4 n9 u: P& j+ |5 M. @) C+ {
5 }" k) x; p! T. v5 Q
  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:! l2 Y7 @( f; z  [; B9 i' i
3 S6 K2 Q* }* y) ~8 J& I
以下是引用片段:
) r1 v( L8 T: {  /* bounding rectangle type */
$ M+ \" O& K+ F* y  O% A# z8 J  typedef struct ; u+ n) I4 I# ^3 Y! ?! l
  {
' W; z  L" V; s9 T) B1 X% s: R  double min_x, min_y, max_x, max_y; ' Y# M# ]6 t1 b, `' V
  } rect_t;
7 s: j# F, D8 }+ ], m5 G  d4 h  /* gets extent of vertices */
3 {3 X7 X% W4 }+ a2 k6 V0 c3 \  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
) e8 Q0 i" }# t5 B/ Z  rect_t* rc /* out extent*/ ) & v8 Z5 u; [" G9 W
  { : d2 L/ R) d3 R$ }! c9 R! l7 y$ P
  int i; # _9 A0 N* Z- D( T) _) y- r" Y
  if (np > 0){
! L0 H7 {9 W9 S5 f0 ~, K: l  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
' P) U! M7 j3 g$ O  }else{
: @+ R5 n; L+ _) q& b0 [4 k  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ 3 ?, V4 u. Q( b; |6 c" u1 Q& p2 A
  }
7 z8 O" ]5 s# G* D( v* S  for(i=1; i  
- ^+ y4 g9 j/ f1 ], K& ^  { + C( ], t8 `, F, R
  if(vl.x < rc->min_x) rc->min_x = vl.x;
: \2 y" R9 H6 V/ {  if(vl.y < rc->min_y) rc->min_y = vl.y;
. @: T& _( [2 t7 T& D! S; X  if(vl.x > rc->max_x) rc->max_x = vl.x; 8 }) w* P3 m1 G$ C& T
  if(vl.y > rc->max_y) rc->max_y = vl.y;
5 i0 G' A7 a( E7 R& W# k  E+ e  } / F: y. B3 ]# l
  } 5 }' F  n4 {& @, t# R

% \: ]1 x- a; g, Y2 y8 N
# Y# P0 r; ~1 r# ?4 o* k  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
9 \4 Q; T. k5 i8 F& v8 n- ]) _. p" b+ @: Y4 m
  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:# h4 S& {- t+ i4 P. h
& a( `5 ?/ V" t) ^9 H% g
  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;; |8 b" s8 T- m# i5 {- W

) z: l, ]4 o1 Y% E  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;4 t: J7 C/ ~) c- ^6 c* [- A
2 @2 q2 _& h- @- H0 ^9 K1 `
以下是引用片段:9 r" F) E9 w& \; x6 P: A
  /* p, q is on the same of line l */
" r7 z: i8 U) J1 b  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ 3 K4 ^2 j  u( R. N) ?& `
  const vertex_t* p, 7 T8 A" g: i8 }$ D
  const vertex_t* q) , A% X- ^" J. n; F
  {
/ A; i1 S2 i  c. f+ C& \+ f- u, C5 T: S  double dx = l_end->x - l_start->x;
' ?. r. B+ M7 Z5 \1 F  double dy = l_end->y - l_start->y;
5 W& ~1 ?$ c7 U  double dx1= p->x - l_start->x;
$ g) e  c5 _7 X# Z% V; W) F3 V  double dy1= p->y - l_start->y;
# B2 O3 e% U6 F; n" H& b$ `* E  double dx2= q->x - l_end->x; # u. L! m+ [! p
  double dy2= q->y - l_end->y; / ^4 s/ V0 @* i7 m3 H3 {8 x! O
  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); 2 j# ?  W- Z5 f+ I8 }/ r
  } 3 P% c; P* v! K/ {
  /* 2 line segments (s1, s2) are intersect? */ * y  K6 V1 X7 u0 C) u1 @0 Y1 C
  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, - F# m; [7 u  p' H3 b! q
  const vertex_t* s2_start, const vertex_t* s2_end) ; m) X/ n; O& B. s  O
  { ; r+ x: x5 a+ J6 @" v9 T4 ~# ~! i# v
  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && , w8 x$ E% J3 t3 z: @  i
  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
0 i; ~8 G0 z7 E- U  }
" r& W9 Y* F$ O- f" h7 k8 v3 G# M8 k; W( ^$ q8 e. ~; l0 C6 n8 e. _

( ^) M, @( S, a) x  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
' `  p8 Z. p% J3 w8 ^" h) J
/ F7 a9 T. f& t7 j( l1 v/ q以下是引用片段:8 [# c: Q8 q) F; U! c1 ]4 j
  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
! f# k& U$ G' E. W  const vertex_t* v) 9 `) y2 i( Y0 F6 J2 j
  { + B8 r2 {+ k" _- x8 M  g
  int i, j, k1, k2, c;
4 ~0 V$ R+ N1 g/ G  rect_t rc;
& ?1 S5 N& a" d; p$ H) Q' G  vertex_t w;
. ~# S; J( ~$ D) A  if (np < 3) ) k% n! m% \6 T+ r0 ^* Z
  return 0; 8 c1 h( F3 e4 t
  vertices_get_extent(vl, np, &rc);
( X! [( F) v: }  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) # w4 K8 S3 f1 n. E
  return 0;
/ P/ i* f" M, e  /* Set a horizontal beam l(*v, w) from v to the ultra right */
% K  m4 W0 E3 p. {0 ?  w.x = rc.max_x + DBL_EPSILON; - _) s7 u8 R5 x9 Q7 f# u  {7 K6 h8 Y
  w.y = v->y; / A9 W! I8 d# |9 H5 c
  c = 0; /* Intersection points counter */
0 e, w9 P- ]" E) _  for(i=0; i  
3 T1 q! {, o" ?' }3 `3 A  {
$ n( A, V3 p2 _0 m  j = (i+1) % np;
4 }2 h8 ?) Z" ?+ F' M" `5 h& V  if(is_intersect(vl+i, vl+j, v, &w))
3 _7 c( r; m2 @+ H4 k9 Y  {
! v3 v; D$ ]" e; L# ]2 j5 ~/ [  C++;
) G. l# X# ^* a, B6 ^3 S  } , {( c0 P7 j" g' h8 a
  else if(vl.y==w.y)
% s  |5 N1 M$ n6 Q5 u  { % v- W8 l1 M$ O7 ?
  k1 = (np+i-1)%np; - \' h0 y" P9 v; i9 M3 }4 u" G0 N
  while(k1!=i && vl[k1].y==w.y)
8 f9 t: G* p5 W- w( _6 V- k  k1 = (np+k1-1)%np; 1 L, p; z  i) W5 r) P9 @* p4 i- R
  k2 = (i+1)%np;
* D/ ~, p( C8 t4 I1 E! v  while(k2!=i && vl[k2].y==w.y) 2 b& Q1 ]7 Y/ Q
  k2 = (k2+1)%np;
. q5 i! ~+ L8 G' U4 O  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
! i( \& q- D) K  C++;
' l/ _" O: D; _: v" b7 j5 `  if(k2 <= i)
; D# x1 W  Q3 W5 c0 ?% i7 c  break;
6 t, ~9 ^; j  p: L8 `& G7 b) R6 [  i = k2; # i0 ~8 |3 ]3 I2 e8 `1 q3 z
  }
8 r% {) q4 }* m9 Q* e% R" w  }
7 }0 a' J% F/ G- }$ _! Z5 P3 _  return c%2;
7 I' m* c2 S8 C5 J3 l( L  }   H0 [5 ^$ D, v( L( }
6 a0 a" n8 y! V0 Y2 _

& @% }5 ^  E: U$ o  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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