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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
2 P' L/ P. K' R' `8 {
4 T8 i* h2 i0 n- n 这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。8 t! \4 q$ \6 s+ `+ z/ j, O
6 s. I* Z! ]4 u8 s2 n# [7 A
首先定义点结构如下:
& j; c5 X. V/ I2 g6 i' _* p0 x& _$ ?- T, P
以下是引用片段:6 k. X& l; Z8 P' K5 [' x. n$ Y
/* Vertex structure */ ) _. I! i, i/ j- \" j# z( r+ ?9 J
typedef struct
2 d0 o+ ?) L- v. J( m4 F2 r, Q7 j6 ` {
) b7 n( I% L0 W4 \- t& L double x, y;
$ k0 s8 q% g/ Y2 ^ } vertex_t; ( D, K' }/ E& z: w
8 h! `/ h: v" J/ T$ O# @5 h& W ^3 C( `/ K2 z
本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:6 |* S3 a7 X3 N/ f. ` f0 {, g4 K( L
. d- w) p$ N6 S! s/ m以下是引用片段:/ A3 A, w! s6 l% d& k$ w$ W: ^' o0 x
/* Vertex list structure – polygon */
6 f7 |2 U0 i; U' e typedef struct 3 u9 o; t+ N" J: W2 d/ F1 a" g
{
4 R% I* Y G' L" |& x' S! c# O9 | int num_vertices; /* Number of vertices in list */
' }9 s+ T! ]$ t. X7 g- d vertex_t *vertex; /* Vertex array pointer */ # j2 G- y# A* Y
} vertexlist_t;
" D! W/ I, Y, o4 f. W) b9 B" @5 Z5 @5 O! P
/ a1 `) ]/ q6 z) Q* u! k( [. w 为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:6 ^, P8 P2 I6 {1 X# ] N2 H' r
. P" N' t9 k' K3 P1 R% H* u/ u以下是引用片段:* h0 V) Q7 \9 x! d! M8 u
/* bounding rectangle type */ / Z |% i: ?" |3 r! h s V/ M8 v
typedef struct
8 Y: Y; z5 }* Y2 m7 r! Z( E; n {
' e3 U9 z% U4 Z* p/ ^' z double min_x, min_y, max_x, max_y;
$ @# W* ~6 Q7 P8 Y' J- z% W. Y6 c } rect_t; 8 `- y3 a4 |2 U
/* gets extent of vertices */ & d& n0 x/ z7 v% Z" B* k i$ c+ N$ o
void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ ! I/ [4 S6 R, r: M; i1 J9 i
rect_t* rc /* out extent*/ ) + L- h: o+ |6 G
{ ) _: m0 m2 w6 `3 w$ O( A
int i;
+ S |5 l7 C7 e( s if (np > 0){ 7 c" J% v0 K* w+ Q
rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; - J3 i% \. U1 q# Z
}else{
# V" G3 z5 z" Q7 l" D1 x4 z rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ 5 N! B8 g' a" `1 } x0 c
}
# A- [9 W8 r5 ~, R for(i=1; i
9 W; T% x% A9 i { / I6 M- n0 ~2 C/ H' d- ^ k
if(vl.x < rc->min_x) rc->min_x = vl.x; $ b% s9 i3 L. `6 G6 Z( ~) j) S) \9 v
if(vl.y < rc->min_y) rc->min_y = vl.y; * p' W1 X. W8 u; w' p
if(vl.x > rc->max_x) rc->max_x = vl.x; & }" y a" d" f# a
if(vl.y > rc->max_y) rc->max_y = vl.y;
; K: y/ p w% z& G( v) K } * e& W7 z5 v# ?7 f9 v+ _
}
" C7 w& G7 Z( t# d& z- Y$ E
7 N% B. G5 X& A7 Y5 p, f
0 h- w% [8 U/ `/ k+ D 当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
# E' h- h6 y: a2 \2 Q- w9 `8 z, u" u! y1 e( t3 |; _$ O8 l/ g
具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
8 \( {0 u+ \0 [+ A0 @
8 i$ l! ?' x" A* A) c( }* b* F6 u (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
9 O5 p' |1 B! r6 a2 d3 A7 W
1 F5 ]& _" [$ q' K3 G (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;, y7 Z* h7 x; y) n( y9 e5 d
, c2 _! h1 X% Z! [3 v* X0 o# `以下是引用片段:
( I5 _+ i4 i6 g /* p, q is on the same of line l */
7 q% g O* y$ B+ ^8 H static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
; T2 _" B3 N) \# T8 R' h) s! J const vertex_t* p,
: v5 ]4 D/ w1 M- t1 a const vertex_t* q)
7 s: `( S' M, i q: V3 m* V { $ R# A& j+ K* e% r, H9 h
double dx = l_end->x - l_start->x; $ S0 h& H# `( p
double dy = l_end->y - l_start->y;
4 r) q2 V/ t0 q' Q: }: \* q- T double dx1= p->x - l_start->x;
- s. U6 q1 X I: m' S6 W double dy1= p->y - l_start->y; ! T0 `$ F, D5 D
double dx2= q->x - l_end->x;
4 T& W l+ U& T. d$ h3 p double dy2= q->y - l_end->y; " }* v, p0 f" L0 y
return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); ) ^/ n/ l Z: K/ i
}
- V- E7 ~# e2 |/ O) C0 b1 j. W /* 2 line segments (s1, s2) are intersect? */
( `6 K, s+ h: b/ c6 A" i4 E/ m static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, ! T/ a0 `8 F! e* j6 W) |: W) _( y
const vertex_t* s2_start, const vertex_t* s2_end) % b1 r) M( t/ c/ s/ p$ S
{
3 E! s0 t V7 b+ v1 ^# \ return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
- S' p/ u. H! W" V is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
9 y# \) m. u3 X, O& _! n } 8 A2 q# y: O) ~- H2 ^( ?
1 Y S& K' ~' ^3 X" u2 n# l
. @# ~7 I7 D7 g5 C! [ 下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:, H+ R& n- l3 y: z
( q) D; P' W! F以下是引用片段:
7 M, L: b" Z! c# o! G' S int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
8 c" a5 C) }: O1 V; ] const vertex_t* v) 8 u7 Z( I6 K: O4 `" |2 }0 K* q. Q% D
{ 3 ~# d4 ~: k3 P* m
int i, j, k1, k2, c;
) ]& z5 g8 P t) z rect_t rc; 7 r, U8 w1 }2 S$ d" p& A
vertex_t w; 1 K2 b* y7 }+ R. `3 u# P
if (np < 3) ( m" I! S+ j6 y/ l4 i9 J- c
return 0; * ^4 _# |2 q; U6 z* Q. Q$ M
vertices_get_extent(vl, np, &rc);
% t8 T9 Q/ G( d6 b& _+ I/ f if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) ) ?0 N( y& ^, h" {0 P; I/ Q- N
return 0;
; x4 Y! x( {+ J; Y: w T: |; ? /* Set a horizontal beam l(*v, w) from v to the ultra right */ 1 s! x$ P. W. O: G! @
w.x = rc.max_x + DBL_EPSILON; # C6 v9 S; e; h1 }9 _
w.y = v->y; , o) l) A, Q5 E+ D% Z' z
c = 0; /* Intersection points counter */
/ @+ |# n7 X- E5 O% t/ T for(i=0; i 1 D5 A8 D: ]0 z% x2 z. U& k2 |! y
{
1 s' |1 X( J0 [ ^, j5 T" I/ ~ j = (i+1) % np;
$ G& o0 h6 N9 ^! P5 q if(is_intersect(vl+i, vl+j, v, &w)) , B) f- H. c7 s6 w0 J/ t
{ 8 s% x9 ]% p4 H+ x
C++; 8 d* k n( J% _. z/ r
}
4 i# B7 r% ?& r else if(vl.y==w.y)
5 `; _+ X& \1 k' ] t; P3 X {
- j- _6 p& @& H& b- W# m* ^" \ k1 = (np+i-1)%np; 9 C, n0 E, _9 j+ ? ]" v8 d" \
while(k1!=i && vl[k1].y==w.y) , O- p% v+ o$ x6 S; X7 z0 M0 `9 ^! A
k1 = (np+k1-1)%np; z! T2 ]& a4 U
k2 = (i+1)%np; 2 j# Y2 J: O/ F+ b# c9 {
while(k2!=i && vl[k2].y==w.y)
9 D) o, ]& ]% K% T* m' h S% @ k2 = (k2+1)%np;
: \; v5 c7 d( n- A+ x: e3 `. k4 w if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) ) G; D/ {7 h0 s H! o$ ]7 V) W
C++; $ J% \1 Y$ ]7 R$ g0 h; l% z1 L- w
if(k2 <= i) # y5 z* T5 w, Z- x
break; ' e$ u/ G, k* B& L5 T
i = k2; 4 w( D6 D- w- `4 s& C9 c; b" N+ F
} 4 J# r8 K+ z& Z4 Q8 t% W# Q
} 4 E8 h, B+ w0 R4 k' M$ i4 W$ I
return c%2;
& e! X8 `9 W" L' @* N. Q7 j0 i } 7 E3 M. ?( T- I' Y5 e4 o3 W
8 {2 D6 {+ P& s; ~0 p9 s( H" T/ Y: l
本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|