|
  
- UID
- 133
- 帖子
- 51
- 精华
- 1
- 积分
- 186
- 金币
- 55
- 威望
- 2
- 贡献
- 0

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。0 m# v$ E4 X S" B, {4 B# G1 j
9 t1 f2 [$ i, c$ [$ F7 S. \1 `8 ^ 这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
# q; ^. I/ W/ Z. W, V. ~5 @/ z: O( X9 v' s1 Z7 M; _7 l" N1 b2 ]
首先定义点结构如下:
5 F3 G7 j4 K. K% a- }
* ~, o2 z/ ]+ C I以下是引用片段:
7 X7 \- F3 C7 h) f, ?* M /* Vertex structure */
& R( ^" S4 I; ? typedef struct
2 f4 _, i1 I1 c8 y6 X2 h { $ s2 a7 B; z9 ?) _% p/ Y; {
double x, y; % Y: N7 b& N' x9 N9 H# e
} vertex_t; & E+ [+ r1 c1 [5 Z/ s; c( c
0 p" F9 s M8 s9 G2 v
" z6 @* N+ u- T- j+ ~. { 本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
0 d# ]% S/ A. V' e2 t: c0 u" D H1 V
以下是引用片段:
2 e( Q* u$ _ o, X/ | /* Vertex list structure – polygon */ * j* r' u7 L7 v) F$ L
typedef struct
) ^9 @) k0 x8 P9 |8 S! k { % p8 W2 h! u5 ]/ r/ d, N8 E, m
int num_vertices; /* Number of vertices in list */ ; R3 u% Z0 C6 m! S G5 D
vertex_t *vertex; /* Vertex array pointer */ 0 \+ i5 z. v4 A( H$ _8 r" x
} vertexlist_t; 4 @+ r ?9 T" `2 l
5 U( n3 G) K6 U1 }7 T7 M* H
9 |9 \! M$ j1 R 为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:$ b$ R, E( V) g9 _* s/ U0 Q0 o8 [
9 r l" K& D! T
以下是引用片段:( @# d1 ?. i- [& K
/* bounding rectangle type */
( X% j; Y Y" T/ X- a typedef struct
) s: b) A8 O b: W {
* l$ X2 s: u4 ?7 s double min_x, min_y, max_x, max_y;
# T/ o+ L% _+ k' I7 h3 {8 K } rect_t; 3 N* b4 B O0 b$ q; f1 g
/* gets extent of vertices */ . R- I9 T6 o6 M, Z* O, P6 E
void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ 5 c1 H. I3 S8 e# \( c# F) d% x
rect_t* rc /* out extent*/ ) 0 a3 w3 n+ {. K7 Y
{ 6 p& u; m* [% Y8 W
int i;
: E+ f; l Q/ k: H/ @$ O if (np > 0){
# y7 \# P3 s- M9 j2 O# \) w ?7 H rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
! R, t- ]& h% a* t ?0 L }else{
6 W' z$ W" C4 c) i0 N rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
6 M4 i* V4 H9 m! I) _ }
t+ L, d( x k- q* [ for(i=1; i
/ O6 }0 n' P/ g# ?, @( B {
( f. ^" m. O1 C0 E) r if(vl.x < rc->min_x) rc->min_x = vl.x; , ?8 R( {% ], C) T8 p
if(vl.y < rc->min_y) rc->min_y = vl.y;
* E$ P$ g J, L3 Q& a if(vl.x > rc->max_x) rc->max_x = vl.x; $ M- N1 I& x! A% R7 n
if(vl.y > rc->max_y) rc->max_y = vl.y; 8 u, B) ?' I: r& m; t' c. t
} ' p0 n" |" Z( h7 q
} - r/ L f$ S$ r: q
* W5 m1 t4 U/ s, H t4 U, _ ?
% {4 ^: D1 i( V* d4 t: c 当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
8 P# ?6 _8 k1 `/ }
4 c; c9 ?8 U( [8 m 具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
! E* Z, s) B! @3 k2 t* K5 I1 _5 y
" }- j; c( R0 r! ?0 j2 ~, K (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
1 n& F2 t' q. e* g" L& R! O# C% c$ Z$ i& @. j/ ]) E" _9 w7 N$ c6 z
(2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;, ?7 K4 C5 D8 M4 {3 p3 }. v8 E
2 h* R$ f2 p: m* E5 h以下是引用片段:
* E0 u! P& ]6 Q# k /* p, q is on the same of line l */ + L* h* P' q. u! G4 g, D0 A( l% `
static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ 5 l6 o0 v. ?1 A7 [
const vertex_t* p,
& L6 o* h! M0 Y8 v const vertex_t* q)
1 k B4 @8 T/ [9 { { 1 U& R% p6 U7 W& K% \' W
double dx = l_end->x - l_start->x; 6 I& A' |- G7 Z6 }8 S" k* m
double dy = l_end->y - l_start->y;
+ ~$ [( Q/ x9 H0 U double dx1= p->x - l_start->x;
+ m! Q4 i4 H7 j5 w6 S2 I! _+ D double dy1= p->y - l_start->y; 6 I. ^6 w4 x2 D v6 y; i$ m
double dx2= q->x - l_end->x; 1 Z8 `( Q9 W+ F% P D
double dy2= q->y - l_end->y; 1 n# p3 \& L a8 J, n
return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); : }% X- p+ C- V
} 6 T# S6 Z8 t' K" e$ i) H5 i
/* 2 line segments (s1, s2) are intersect? */
+ ]+ Z7 m' Q' v0 L static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, 4 j$ E, M* Y9 J5 g. r* O" ^) h( @
const vertex_t* s2_start, const vertex_t* s2_end)
6 ?6 X$ O0 G- A {
5 D4 b" G: ^( T; L$ h1 J8 Y return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
, U1 T$ Z$ b$ j+ L$ D is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; ' B$ ?0 ^/ H! ^" }) u B) B
}
$ o; M: ?# ]+ \) P; g# b& e! g1 m, R+ \
* f% s# _1 e) W 下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:- q6 S: u: n0 m. U3 ]$ v
2 n" p- E+ Z3 P F/ Z4 \# A
以下是引用片段:
; {0 l% I# P- x int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
3 d/ |0 n3 O% q1 I/ G* e3 s const vertex_t* v) ) F" \: N, a1 C
{ % Z! R1 A7 s9 `' {0 @- F* _
int i, j, k1, k2, c;
1 Z4 r( N: `! S( }& C8 S rect_t rc;
3 W' a* c0 |0 ~7 C& u vertex_t w;
7 Z; N. F6 Z, k8 y8 w; C0 E if (np < 3)
$ j! r8 @$ F R& C6 }: x return 0;
1 d% R6 R1 b+ E! u4 h8 M vertices_get_extent(vl, np, &rc); * z9 A1 p# W# n, k" B3 v% p
if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) 8 ^$ ?9 W" z, T& m* R( ?
return 0;
- c$ Z3 ]& h! G- }4 k5 H /* Set a horizontal beam l(*v, w) from v to the ultra right */ + s- u2 v" d; [4 z; v
w.x = rc.max_x + DBL_EPSILON;
0 R9 ?: I6 U# f! N) Z0 ]5 e w.y = v->y;
+ M n- e! v, r+ B; A- Z' Y9 u. t& x c = 0; /* Intersection points counter */ 2 |4 K: J3 A6 U' L
for(i=0; i
) K9 Y% ^4 ?4 W V {
7 E# O2 t6 K$ I4 n j = (i+1) % np;
' M* t p) X" l3 T8 J% i if(is_intersect(vl+i, vl+j, v, &w))
# V" W) P: W7 m+ X6 C9 l0 x5 s { : O. v; \4 Y' K2 Z8 ^
C++; ) e0 ^, j+ \9 E- t7 r% m* o. p& n9 @
}
/ V% U* }8 \2 Y5 Y) H. }0 |! @ else if(vl.y==w.y)
$ I e- c" {5 H: G/ J8 ~ { , a% A$ g0 q' V9 W$ H7 ?/ ^7 O k! a
k1 = (np+i-1)%np; : b3 I4 F i- K+ |3 X+ f2 J- s
while(k1!=i && vl[k1].y==w.y) - P( [6 R- P, V$ ? z: c
k1 = (np+k1-1)%np; 3 `3 S1 u7 s! H% K D
k2 = (i+1)%np; : Z- o. L# U/ g( G
while(k2!=i && vl[k2].y==w.y)
3 w) N! N: m3 c0 y$ ^ k2 = (k2+1)%np; 6 H. o$ H+ x, W
if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
1 l L9 @; _$ H8 ~ C++; h* G, j/ b1 ]* @8 d
if(k2 <= i)
# e1 P) T, Z3 m) P- n* H break; 5 t& j( {& ]; N8 Z; q6 p4 ]0 }
i = k2; ) @& e" O# z) f+ `# d/ R6 E2 G
}
2 _5 _" ?4 k9 q9 Z( S1 C }
6 q" B; T p5 x return c%2;
2 `) }+ Q0 |! }" ]* l } ( R1 {( M/ H: t6 x0 U0 \
, \7 R& @/ X' ?9 V$ ]
: _6 m# H1 s8 w( m" K3 D$ z5 T$ i- o 本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|