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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
/ |5 X- B1 N, @) x. l& I8 L; Y. k4 {
5 M9 \4 A" w" k3 P+ f 这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
6 [( {) b, k1 p& H: J+ W' o
) _$ T$ u6 j, X3 t' Y2 j7 |' c7 h6 r 首先定义点结构如下:
; d% s. N& g. _/ F4 X& w1 W, V( y* K p3 [2 j: j& R
以下是引用片段:
; W; C; V; m% D) ~7 _ /* Vertex structure */ / Z, X ^& H( S5 f
typedef struct % W8 p5 h F7 {% y( w9 ?, [
{
9 y7 n! b% h1 u3 t* I% w6 C8 D double x, y;
# y$ Q, N" A# I# o } vertex_t;
; z0 b. k8 t/ L" I% w0 \9 r
2 @. a! F. S7 R; O, X% i& P, x' c2 k- V4 w) G0 r1 M
本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:: b7 r _! z; z* _- f. r: O$ Y
" J* j/ M7 M: U7 f7 D. |以下是引用片段:
- [) u1 |/ d' S. p( Q" c/ L /* Vertex list structure – polygon */
2 b" O" y, X3 y& u( B3 p7 J typedef struct - p$ ]: A$ S, i
{ 8 E, H7 u' I9 Q6 b
int num_vertices; /* Number of vertices in list */ & n9 _' u5 C$ g, v8 ]
vertex_t *vertex; /* Vertex array pointer */
1 w% G u" O% s; w2 V } vertexlist_t; ( W+ T. D9 W1 K$ T8 o: V% o. ]- S
! k3 m. L. `( S, X! ]. \0 r
. {' ?- a' b" m4 _# s. y( C7 v, F 为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:# M" H1 A0 j% z! M. l' W
+ }8 b9 T2 s5 Q7 G9 f3 o6 ^以下是引用片段:
) R ~6 n, |% d. V7 |- ~ /* bounding rectangle type */
- K, i8 k0 H. E$ S! S% r1 ] typedef struct * y" @0 D c+ v) j
{
0 R: T' \' c0 H3 r+ u+ v0 M double min_x, min_y, max_x, max_y;
9 d) |( q" p0 J! z } rect_t;
9 N* V( Z4 a( m3 s5 B /* gets extent of vertices */ . [" L% ` q3 \6 g0 ~2 |
void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ 3 U( a& D! p7 e6 l: h
rect_t* rc /* out extent*/ ) ) f3 ^- S8 r5 v
{ * W: C9 z. x, C. y
int i;
2 @* `* n4 a, Q7 i. s if (np > 0){ $ A8 d3 }' [+ l/ k$ ~- S
rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; 4 v W( ~4 e2 U9 g/ d& C5 ~
}else{ & X' L0 e2 t- X# p1 o; u. D9 K
rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
! p/ X- e7 N U0 i# T, X }
/ d6 n+ K9 ~: N4 Y( s) G8 h# n for(i=1; i " X* _+ f0 |( ?& ?
{
$ K/ z! G( ?5 \& \ if(vl.x < rc->min_x) rc->min_x = vl.x; ; ~; k- T+ _, A- A5 S: Y- m( [# U
if(vl.y < rc->min_y) rc->min_y = vl.y; 6 }& l/ r% n1 w4 n
if(vl.x > rc->max_x) rc->max_x = vl.x;
* _' B# T- b0 r: e) I if(vl.y > rc->max_y) rc->max_y = vl.y; ; r4 q' R. Z U3 `
} 2 \: k$ f5 Y4 E$ J( A) ^
}
2 _5 `6 Q1 s7 o# e5 F% N- z
6 @# ? s* v' D' N" q; _. |# ?% M- O+ \4 [1 h" ~
当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
$ B% z. x; c- E! C/ [3 v
6 |9 q( }" L% Q: X 具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
, }" m( u( d" [% c' Y
2 Z/ u& X% B: `: z. U* _$ S (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;& x; B# d% t9 d0 t
6 F% v9 P0 I! m, @3 J; S, c$ @8 g5 ]
(2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
* J: X0 v6 N2 \/ L0 h& B3 z. z6 V
! l8 e9 M3 P4 O8 I以下是引用片段:! v0 @0 f g, y) E! ^
/* p, q is on the same of line l */
: P X, b" v- f7 S3 E( y) ~ static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ / h) |& p2 [7 u( t5 Y5 G: R
const vertex_t* p, ) R: O ?$ A: r7 Z- V
const vertex_t* q)
* j6 _3 u T5 Q$ x, E' B {
! _2 Y/ |+ s' l. C( h# t double dx = l_end->x - l_start->x; : [1 g7 w; R' V+ f4 S1 x, _
double dy = l_end->y - l_start->y;
( V" z' H& t6 \) n* y" g, x( }2 M8 @ double dx1= p->x - l_start->x; 2 u4 T8 r& j+ \; C9 q# m
double dy1= p->y - l_start->y; " L( ~$ }6 \% e/ } t7 S( z- U+ d
double dx2= q->x - l_end->x; 8 i' F9 e) a8 i* ]3 ?$ ^
double dy2= q->y - l_end->y;
) ?) ?8 z; F" E return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
; }9 x, g) Q4 J3 d6 b } . q% ^3 Z( C8 G' J0 i
/* 2 line segments (s1, s2) are intersect? */ 1 g! K3 l+ O4 ~& V& M, A) l
static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
- @% l& }# I5 [; b const vertex_t* s2_start, const vertex_t* s2_end) ) L8 X" J1 C1 I3 I5 R
{ , Z7 x# y9 Q- U6 ]4 m8 H; o8 F, |
return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
/ p |$ n, c" Z' } is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; # g+ c( r% O9 _- M1 A4 s; T& V/ |7 T- m
}
4 g# j3 b. ~5 h0 Q8 P: `/ K( ^, N* U6 R0 b4 b& R: ~
" G0 v! K. p" C" k9 |& M) e3 z 下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:8 u8 f/ J# g& B3 S9 L. C
% s9 e) g, u' f! f+ _+ t8 O/ h* v
以下是引用片段:$ @6 r U4 w. z2 ~( D9 J5 `+ L
int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ ' H, ?' E* a; F+ j& R: J4 ^* F- V
const vertex_t* v)
3 b3 ?9 U" t) z/ O+ B- H B0 l { 3 \8 ~2 W" M& i' Q( r! a
int i, j, k1, k2, c; * M1 P$ K3 K2 y
rect_t rc;
$ A( f' F8 p% F+ J vertex_t w;
8 O- g& o8 W# f if (np < 3) $ X/ i4 X* M6 D
return 0; . g& R7 F9 p' ?; |- ]0 g, M6 P
vertices_get_extent(vl, np, &rc);
- [( h) v3 L$ ~' e if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
1 {( R7 R( w2 K! k- a, M- A return 0; 8 g2 u( _0 M5 g* m! D% ]
/* Set a horizontal beam l(*v, w) from v to the ultra right */
3 W6 W+ Q7 t. y( H w.x = rc.max_x + DBL_EPSILON; ! ?- q7 h4 m, H6 d* I1 o8 O
w.y = v->y; 7 n0 c X) p; [2 C7 b- \6 x, r% U
c = 0; /* Intersection points counter */ s1 p3 j* D, F' B' H1 ?
for(i=0; i
' E1 p6 W1 Y0 d3 @ {
9 S; _6 V/ ~% ^. f j = (i+1) % np; & S5 H5 b8 p6 `0 `" _+ h2 d5 ^( m
if(is_intersect(vl+i, vl+j, v, &w)) 6 t' J* j$ u p* ^+ j z7 b
{ , u! Q- M+ Q6 u) A H: ^6 `- B4 ^
C++;
& E) ^; Y4 o+ g4 d } / j& h+ V+ H& l+ E
else if(vl.y==w.y)
& R/ N0 w7 Y+ `2 o8 Z) S" | {
5 }- B/ b& J$ ^8 y3 M: W k1 = (np+i-1)%np; 1 H' s( `4 {9 P% b- m) F7 F
while(k1!=i && vl[k1].y==w.y) & x& V: E7 j- F ^2 f0 L7 \7 ]- i
k1 = (np+k1-1)%np;
M' C1 A8 M& y3 x& u k2 = (i+1)%np; + s; x/ E& Q$ c; U! _: I
while(k2!=i && vl[k2].y==w.y)
7 }! Y& E- K* J& D k2 = (k2+1)%np;
1 l$ Z0 ~; t3 D/ X- h. j( ^ W if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) - X/ g) L# L: f* ~+ t1 m
C++;
" Q2 ~) G9 n2 o1 w; J1 X if(k2 <= i)
0 N/ }( y3 f, A break; 4 H; T% D- O9 V/ l4 G5 [' d
i = k2; 0 S0 G; |. {/ }8 O
}
5 u! ~( w' w8 k/ \) @% J7 n }
* ~% G+ F& q+ a4 s- r, h, l/ h return c%2;
" X$ `9 R/ S/ M } ( i' x$ |+ O3 s/ g
! ~8 \ T" e' ?9 z
; g5 L& A( T7 F% T9 P, |
本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|