获得本站免费赞助空间请点这里
返回列表 发帖

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
/ m5 e5 r# ]/ ~  X
, f% y( Y3 [2 D  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
) |  o2 _) ?/ X( h, R9 P' j# W* K3 W4 w9 D4 o" }; n9 o) E
  首先定义点结构如下:
& G, `- m7 B3 ?8 s& Y2 C% j0 v* }' S0 G$ s+ Z1 a2 F& v3 U  h
以下是引用片段:
) P9 @, @( U- u6 E9 d0 b# h  /* Vertex structure */
8 u8 B. Y/ B0 ?  typedef struct
8 T5 l% J0 M& g/ C9 \  {
( f  U4 B' P! ]" E& `3 W3 P+ k  double x, y; ; q) a* r" B: Q/ L  t& y7 B
  } vertex_t;
1 t# `% }% s* c2 q% Z+ {1 w/ u8 C4 F$ h" u1 U% A

  ]; |) l" t4 N5 z5 K  l- z2 `  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
& v, }& [  @- x0 J5 ?7 g4 Z: b0 ?! z9 _! U
以下是引用片段:
6 c# [+ W+ v, O5 R$ D  j  /* Vertex list structure – polygon */ 5 s2 @" W# G( \9 K" Q! P1 E" S! L2 T
  typedef struct # _/ Q. h* Y- x; p" u- {
  {
: d! ~  X4 B( \) m2 X3 R. m) e9 c# O  int num_vertices; /* Number of vertices in list */ ' `- S9 i+ I: L1 K( T8 G2 e$ W
  vertex_t *vertex; /* Vertex array pointer */
( a7 G+ `- |# i1 G& t" c  } vertexlist_t;
/ l! C9 g; u/ U" a" M5 L6 H! h
' \) p: z; v# d' i) X
7 |$ i7 \) L; t$ r0 h) l  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
# L5 [- }; l: y& x4 R* q/ j* v' P, ~' ]3 }3 t6 f5 e# ]
以下是引用片段:
3 H. p9 g9 u7 z# O5 \  j9 K5 W  /* bounding rectangle type */
% e( A! a1 |3 t' i- r  typedef struct . a" l) {! C. K8 y  i  y* T" v
  {
1 T) Z: W9 {' R% |3 h- G8 a* [  double min_x, min_y, max_x, max_y;
' n9 f* B. o+ N  } rect_t;
4 F2 C6 \4 G1 t4 G+ A$ |) U  /* gets extent of vertices */
* ^7 S5 h  h+ h  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ 7 {% t4 o8 i. c2 o2 E% B
  rect_t* rc /* out extent*/ ) 8 p6 X& P" k' G/ g) ?5 I
  {
% K7 t1 x9 x  F0 z. T  int i;
9 s: Q* X7 R; B# _+ P  if (np > 0){
; x; i9 @8 u, K0 e. e  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
3 S# M5 c' j1 D) S  }else{
" _7 y  x/ `9 P, a  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ + x* u0 `5 x9 Y7 x2 L1 l
  }
9 K- b3 }' N; o$ R% Y5 j  S  for(i=1; i  : O9 I, Y+ c, Y7 A2 A0 B1 q9 M5 D
  {
) [1 p6 f0 o( l8 X  if(vl.x < rc->min_x) rc->min_x = vl.x;
3 x" _4 S% A9 K% s  if(vl.y < rc->min_y) rc->min_y = vl.y;
( [3 ]- z  b9 }4 I& C0 F  if(vl.x > rc->max_x) rc->max_x = vl.x; 6 T/ ?# w5 C* a! B) B  y5 `& W  W+ x* u
  if(vl.y > rc->max_y) rc->max_y = vl.y; # t# A5 u0 J8 K, d9 Q% q: H
  }
' t) F6 q) }- N1 t+ C  }
$ i# v8 m4 E1 }: r8 `/ R
9 v- J- L9 q2 S' t/ w, R4 i( H1 X& g( S, t9 F  }) o
  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
% z8 s6 p4 N* K9 F  d, t) b! R% s! R# i3 {% W6 E
  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
- @+ @6 y) i+ ?) k
# S4 M' Y* I0 E& D; i4 }  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
# E; ]( O; x" F' K% T7 a$ T, o5 U# L4 |3 W, [: o7 \
  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
  g: v$ x8 _, D; A
: `# f8 E" _1 f0 t以下是引用片段:9 Q4 `! {: J- e. N9 A1 E
  /* p, q is on the same of line l */ - T2 s+ E; o& x' A2 m( N
  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ " _" k1 z# `" x9 g4 [/ w
  const vertex_t* p, * ^; i' _  L: W; H2 N; U! N' Q
  const vertex_t* q)
) c4 I1 k9 d1 o, J6 L5 r9 K0 x) o  {
6 V% t& x$ d: m5 @$ n6 x  double dx = l_end->x - l_start->x; 8 O: ^$ x( c. ?
  double dy = l_end->y - l_start->y; ! ]9 {7 n. o. C* ^4 a+ D
  double dx1= p->x - l_start->x; 3 H2 y% Q" _8 Q' V
  double dy1= p->y - l_start->y;
# U# F1 N( z, q: u  double dx2= q->x - l_end->x;
7 D; Z% c1 j8 n( d6 i! E2 z  double dy2= q->y - l_end->y; * w" k4 j4 u+ a9 H
  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); ( v% f6 u' ^8 _  O# g
  }
- n  p' ^6 O/ U  /* 2 line segments (s1, s2) are intersect? */ 7 |' k  e$ w4 I$ G0 k0 X3 b
  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
: l/ ?& G7 a! f- z; O# ?  const vertex_t* s2_start, const vertex_t* s2_end)
$ M" I4 F5 t3 {- Q! [) X  { 6 _9 E! m$ @' u' _4 A. V+ U
  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
! i& i! R7 x  }" B  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
+ n7 U4 C* S& J8 P/ P+ Y- ~  }
: p4 g$ x  f+ ~8 N, w! o5 h# b9 X6 V

1 h" V- X; o/ v& s; Y  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
  [- {5 ], l) l/ g1 s
7 H0 O* ^1 C' Q* N5 i* I以下是引用片段:" o* c0 \) F/ o$ _6 H8 L; r
  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
" `0 L# Y- ^& H! }+ y, H  const vertex_t* v) 7 g& `3 ~  p/ o
  { , U& T3 J4 J/ s( K
  int i, j, k1, k2, c;
' u7 b  V' N: {: w0 L  rect_t rc;
( Q8 w. O1 @; h" \  vertex_t w; , i5 d7 a% R: _* k$ \+ c
  if (np < 3) , r( z  T+ K  \3 O# h/ z5 @
  return 0; 3 F; X5 D& V+ V9 Q' e
  vertices_get_extent(vl, np, &rc); + Z. l4 p5 Z2 ?' q2 [
  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) ; x; v2 C# C( i2 C3 Z: @
  return 0; 1 O2 G+ e: O% K0 [7 l' A- c
  /* Set a horizontal beam l(*v, w) from v to the ultra right */
6 _. z+ w& `8 `' B. Q: e; i0 M% o  w.x = rc.max_x + DBL_EPSILON; 9 @5 i! X7 t' Z: D% y1 |" q
  w.y = v->y; 8 g+ V% t. U) }- t8 h
  c = 0; /* Intersection points counter */ 4 e8 y9 o# {1 u7 [2 R4 d
  for(i=0; i  
- D) o2 ]0 w$ {. o6 o  {
2 s/ J# ?) H7 I+ ~  j = (i+1) % np;
5 z& P) z# t7 S& j  if(is_intersect(vl+i, vl+j, v, &w))
( J$ J; I2 i. S* w  { 6 G" _& g' w) H2 ^6 h: S4 \
  C++; + [% a( k. S# |# R& `
  } ! y  q* T' Q+ t( r3 c4 P8 u, S
  else if(vl.y==w.y)
+ m' |; o5 I' q8 y" Y2 w  { 0 p) ?9 _+ a3 B9 Z
  k1 = (np+i-1)%np; 1 {- a" l/ E4 ~- [0 u" ]: t* N
  while(k1!=i && vl[k1].y==w.y) 8 ]4 O4 k" @9 Q2 i0 j
  k1 = (np+k1-1)%np; & B1 Y1 l( W% ^
  k2 = (i+1)%np;
+ B* [" b7 B4 K0 `0 ]( I  while(k2!=i && vl[k2].y==w.y) - ^; Q2 s& Y$ ]& A% w0 }
  k2 = (k2+1)%np;
" k/ r8 t+ V  y; ?# P* k1 `  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
; O( B' K- d0 G) g  Y, e  C++; " ~  ^% d- _) _, r: ]
  if(k2 <= i) , w! ]- U' d& Z: O4 X/ j6 Z
  break; 6 w& f3 l. O- x! P
  i = k2;
+ e& P* G/ `. K' r; [- g  }
. Z+ L/ E9 c$ u2 X0 K1 S; H  }
' m# K1 b3 A9 z2 R  return c%2;
0 Y* [2 ~$ j. o+ B( y  } " y+ n! ~" d! x: s) T8 g

. t9 H9 C/ s& }# U
6 i' V$ b' P& R2 C4 G3 {: O  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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