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

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

本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
5 O! ?2 C5 K% b5 {) ?# G' ?5 a' \( y
6 C' d- R: U! Q) N: x2 z' a- s* n  这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
) i! N! Q1 W$ g) v3 [( b3 S% T4 `1 [: k  h+ [# X7 s4 a
  首先定义点结构如下:" D) H: O4 v7 j4 o8 t# c
  i& y5 H: C/ V3 d* f) p7 i; x! L
以下是引用片段:' |( O8 ~% m. z- y1 d/ l- R6 M
  /* Vertex structure */
; \  k6 x1 N: c% x- k9 h  typedef struct
: ~# O8 A* \7 K5 J. m* V* d9 `7 s; g  {
3 g3 d* U$ T% l( ?9 L% U6 Y2 H9 d  double x, y; & P! \& @  U1 |7 @/ {0 v; m
  } vertex_t; 9 l6 T: u) t% d# V. s( J! }
* k# I7 w& a& R  G! F/ k

/ T5 S9 J0 K5 |- r: `5 Y$ ~" T& G  本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
5 q$ v; L1 o9 q9 E7 Q- p, f, U4 Q4 d
以下是引用片段:: W- L8 Q( Q# Y8 z2 @1 F
  /* Vertex list structure – polygon */ ' w* P3 m% A" q, D+ w
  typedef struct . L; J3 n2 K! _* m
  { 6 _2 p: e9 q' v9 V  T, E- i. B& q
  int num_vertices; /* Number of vertices in list */
+ I3 ?  l, `! f( V1 l1 i! x  vertex_t *vertex; /* Vertex array pointer */
; z0 I  W1 s+ q- ?4 b  } vertexlist_t; 2 y3 G7 [7 p$ z* F) T5 k6 r% r

/ m! f; M, t! f7 \. a* F
: |: n4 ^5 ~0 B& H, \/ F& ~! u  为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
0 J. V0 }" K3 P: N
9 W2 l; A7 U* i: W* a8 ?6 e以下是引用片段:; |" d. u! M! Y( j0 d, _
  /* bounding rectangle type */ # C0 A6 s; I7 r7 {! Q
  typedef struct
$ y: w8 E7 e  u, t0 a" r, B  { & i; I6 w/ `, H! z
  double min_x, min_y, max_x, max_y; . u+ L& G. i/ x$ d
  } rect_t;
% s, ]  n0 ?7 \5 \7 U5 m  /* gets extent of vertices */
! U( Y/ A& ^5 u) S5 `. z, n  void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
& Z4 i) Y: p0 y& h0 b. ~  rect_t* rc /* out extent*/ )
6 w/ i/ K4 Y: W( ]' _9 p  {
' n" t# k) W+ j0 @# A( O  int i; , s/ S7 h2 F7 e. Q
  if (np > 0){
& J: G. {$ X( S- N2 p  rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; # Y% o: i& {# ~4 t
  }else{ * B0 N6 c% [8 d9 Q2 j
  rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ & _# q9 _# p7 K5 Z% L0 M! H  V; n% o
  }
/ b, e% D6 Q$ u6 P  for(i=1; i  7 R( s3 Q; q8 w" H7 {* }4 l
  { : }# I7 L7 C7 m: S) w$ l( [
  if(vl.x < rc->min_x) rc->min_x = vl.x;
$ `' E3 Y3 n% g  if(vl.y < rc->min_y) rc->min_y = vl.y; - C" U" A7 Q4 ~% `$ a" r2 P
  if(vl.x > rc->max_x) rc->max_x = vl.x;
* }: o5 k$ v2 U# U* g3 V; c! j  if(vl.y > rc->max_y) rc->max_y = vl.y; 6 V* W2 S9 i, d% C4 i
  } + i6 h! K2 P  r8 K" t3 r, d
  } ' }* E7 x( c7 d' T1 h9 e9 k0 o  S

1 q( N1 _6 D4 f! S4 V
9 |( B- G6 t! a2 R1 C0 E  当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。! F8 d) x6 Y& P  ?
8 l# z, e; D2 j* q9 c
  具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:, G3 Y& L! @8 o
. Q6 m, a; m8 l/ d
  (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;( \7 u, u/ P7 {

9 P# E% {7 y2 q# `( L8 b$ k/ T  (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;* N7 u+ `3 m  J* i7 z/ ^$ T6 }

0 R! o! ^: m- H以下是引用片段:. K7 e. `$ \1 d# C7 p+ \1 y, p
  /* p, q is on the same of line l */ - [  ~4 Y4 C: L- G
  static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
0 J+ i/ J  k6 V7 U* C3 u  const vertex_t* p, ' D& [6 A+ C$ o* N. O; l1 E
  const vertex_t* q)
7 f: I% R0 y" ~; f" d+ }$ {1 V  { " K' T" y& ?, e1 L3 T
  double dx = l_end->x - l_start->x;
# D% s+ j; i- z6 n, j4 C- v  double dy = l_end->y - l_start->y;
$ l% i, J+ h% N% K* U, u  double dx1= p->x - l_start->x; % q' f6 x8 ]5 U# o5 R* S1 ]
  double dy1= p->y - l_start->y;
2 c$ g* Y. ]/ A" G( [  double dx2= q->x - l_end->x;
0 `$ ~, t  L) U- Z! u2 c' ]5 }  double dy2= q->y - l_end->y;
# z1 U6 _* j" t' F  return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); % a' X' n3 s$ P1 H
  }
; k' D/ q- q8 Y% v8 W  /* 2 line segments (s1, s2) are intersect? */ 7 e. T" \) H- H
  static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
9 V  F: ~* I  u( S7 T* B5 X  const vertex_t* s2_start, const vertex_t* s2_end)
9 R* I& Y+ X) l7 L! U  { 0 D  I6 Y3 K# \. P% G! S
  return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && 1 O& g6 A2 d; E
  is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; - E6 d9 r' H9 i: I! a
  } % x/ l2 M" q  O% ~
6 l: ^9 t! E: b5 n- R

0 Z* h7 f  K- i, L  V  下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
4 W: r7 C2 ^% p! R- `! ?; a/ y, w& C& {
以下是引用片段:
# a" g9 F6 X9 ?) D; i  int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
" ~: l  G/ Y. h$ x/ k: C! K  const vertex_t* v) 5 f; N+ a- {% H' ~, U9 j$ g
  {
% C  V- G+ i/ V1 i  int i, j, k1, k2, c; 5 w$ Y/ s: Y7 X4 M# B) n
  rect_t rc; 4 Z- {( ?% [, @$ O- i. n
  vertex_t w;
7 D6 E8 b# ?( [5 Y4 `  if (np < 3) 9 ?, y3 G, \2 |3 r9 P" g
  return 0; 2 o: K" d2 U+ V! K
  vertices_get_extent(vl, np, &rc);
1 n5 `9 }! g4 A  if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
# f- q9 ~' w  l9 }& r  return 0;
0 x2 C0 }6 t# b$ K  /* Set a horizontal beam l(*v, w) from v to the ultra right */ 9 P1 h- \# G  ^3 d. R$ z4 H4 ^
  w.x = rc.max_x + DBL_EPSILON;
! W8 x5 J$ `$ |" l; b$ @* }+ ~  w.y = v->y;
# a0 ~4 E& p' m% ~+ E' J" c2 n  c = 0; /* Intersection points counter */ ' t) h: x& B* Z2 d1 `
  for(i=0; i  7 F% X: ]' o7 y. h
  { 9 @5 A9 \& l8 _& H% G' ~
  j = (i+1) % np;
4 d: e% e  t; Z2 i# f9 l" j/ {  if(is_intersect(vl+i, vl+j, v, &w)) ' {) V' d/ g" ]( t+ I
  { / J. V4 P) Q: ~+ |) M
  C++;   Z% E* j7 w6 A: I( u/ {- k0 _! k
  }
; L/ c( a  W  L) v; ^" g  else if(vl.y==w.y) 0 d  e- b& `0 B3 z+ ^3 b2 {
  {
! c0 T/ _' p# P/ B+ t# U4 p  k1 = (np+i-1)%np;
3 U0 Q( ^! _; R/ q- X. Q! l5 e  while(k1!=i && vl[k1].y==w.y)   V+ H( z( _3 G" [. S
  k1 = (np+k1-1)%np; . X. L) a; O0 c/ T4 C  ~1 A6 g
  k2 = (i+1)%np; % N5 Q4 W* A% S* `1 T& C' Q( ^
  while(k2!=i && vl[k2].y==w.y)   X( z+ i! D3 y$ N: Q/ ]9 R, Q7 y
  k2 = (k2+1)%np;
) H8 B# g5 e9 b+ i  Y  ~7 B+ L1 Q  if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) ( @0 K$ Q# j, ]) @7 Q4 _
  C++; 7 t+ w, Z: p- |% o0 D
  if(k2 <= i) : O+ w6 O- ?2 k
  break; * c( m' v3 r2 }8 `) I6 i- Q* R
  i = k2; : O; u9 S$ N* C+ C" M1 X- ~) l* k8 \
  } 6 z/ m, u7 Y- `0 o
  }
  X* U2 u  o" a2 g. {, V8 a, _  return c%2;
0 k( p' g2 q4 t3 J$ U  } ( B  ]: Y1 b" }3 c) E
# x, N# i, E) {) b% }8 R9 g
3 y$ T) W, b( P' O- n
  本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。

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