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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
6 m$ W+ e1 f$ N6 @- e$ d: L0 z
这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
1 ? e* B3 P; y/ b4 X/ t7 n! C6 n& E7 p P c# ?, J! }
首先定义点结构如下:
; i/ l/ Z0 |, t
# L- n0 ?, H2 ]( R& W: T以下是引用片段:
; k; b" ?5 A. {% M% a9 w( V /* Vertex structure */ / a' h5 W/ f ~
typedef struct
( ~: k C/ D& `0 s/ s! i' p9 s {
, C5 _+ i! x% Y' Y3 S1 x3 }% | double x, y; . a c$ ~. `) ?( V8 P' @* R3 E
} vertex_t; 2 ?- b9 i" e9 |5 P v* I
8 t0 I& A6 q. O8 a+ [
& @; ^$ A2 _. d5 i" z7 L" D2 _
本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:2 v+ w6 J; L( U" ^) Y1 ]# |
/ B5 a! ~0 n9 ^( r/ u! L以下是引用片段:$ v4 Q1 e& I- J% E/ u1 q; \
/* Vertex list structure – polygon */ ( z- @+ [9 d: |8 E
typedef struct % \# T0 @9 m4 T
{ ' ]$ x3 @/ w4 d- u; o
int num_vertices; /* Number of vertices in list */ 1 W, v# C3 P" y7 ^; s% r
vertex_t *vertex; /* Vertex array pointer */ : J6 k! T' Y- A
} vertexlist_t;
" ]! w7 v, `- c& J8 l6 T
8 Z) ~* n& u/ M. P5 z8 v( N% B9 O" o: U8 F' @4 h6 I" ?7 b
为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下: d: e8 q. g6 K; ]( Z* j. {$ d
) a$ s! q5 \$ ]
以下是引用片段:: {; |- M8 q8 g& Y! ^: q+ Z
/* bounding rectangle type */
& X- { c2 r) H l' d typedef struct " Y \7 ]6 \5 @# P
{
( p: W, ~9 d) C double min_x, min_y, max_x, max_y;
% n5 {9 P( V& Q) X( n7 D5 {1 K9 @ } rect_t;
4 I1 u* V( \8 ?3 q, a1 y /* gets extent of vertices */
- L5 h" s. a2 I& _$ w* J$ V void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
# m& _3 }* Y& r. L. A1 S rect_t* rc /* out extent*/ )
) t0 K! F* h1 E6 C7 { {
1 f- s7 i. P" k int i; 1 b5 t$ b1 F; _' F( n4 c$ h6 Y
if (np > 0){ * W* V8 v V+ F m# r% X4 K4 a3 q
rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
6 r+ z) U) D# s$ v2 O }else{ / V: _6 {9 F% U/ y% G8 ^+ T$ b
rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ % ]# l) \, h) _3 {' D
}
7 e1 _' W; |, o5 G* z4 Q for(i=1; i 7 E b. E2 t% e2 A
{ 6 m6 O8 P k( I) V' W3 O
if(vl.x < rc->min_x) rc->min_x = vl.x;
, Y0 e, _9 l% B" _6 L if(vl.y < rc->min_y) rc->min_y = vl.y; + Z5 O( e- Q$ C$ C! n% b
if(vl.x > rc->max_x) rc->max_x = vl.x;
3 p r) B3 V$ U" f- z# r a5 ^; a if(vl.y > rc->max_y) rc->max_y = vl.y; / F* G; B2 j- ~; r2 T
}
' W9 |2 J* v0 q7 N4 p }
- K% T6 y2 Q& o0 Y
$ z' z( C3 U( k0 l' {1 E" v# |: c: j
, O+ e5 k: E$ s: z6 V& d, p A 当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。2 y! X9 r5 ^- x7 u/ e2 P
: b# J8 x L9 _- b# t) E 具体原理就不多说。为计算线段间是否存在交点,引入下面的函数: @9 A1 x: Y& a1 h3 a
, d$ W6 G: u4 f# u* z (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
6 w3 s1 \1 U8 j1 C# u
7 s% P7 `: N$ Z9 ] (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
7 M. z' ?, {& s+ Y' \ ?- ~1 Z8 v' Z$ B4 r+ ^
以下是引用片段: @& c/ ]( Z6 v7 o
/* p, q is on the same of line l */ " u3 w# B7 T2 J) Q. n: g# k5 Y9 K
static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
0 O* N" g) \7 {3 W- u3 o const vertex_t* p,
W" ~ W) a: c. x# K# i const vertex_t* q) i1 I; b0 C* m7 m# c. U
{
# A$ S0 i; ]; }2 f double dx = l_end->x - l_start->x;
& h2 j" t, y" x double dy = l_end->y - l_start->y; 8 {, P0 n! H7 d Q
double dx1= p->x - l_start->x; 3 E5 E1 r" v' [4 [1 y K
double dy1= p->y - l_start->y;
8 o+ w: y3 ^. N$ X! ]6 |& a. w double dx2= q->x - l_end->x; 6 _8 U, |# Y: `& T7 I& ~/ G* H
double dy2= q->y - l_end->y; ) ?, w5 ?0 O! g
return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); 6 b( N7 g( h& k% j
}
6 G& r- W+ `! M2 K6 T" N9 q8 e" q3 u /* 2 line segments (s1, s2) are intersect? */
* k* S0 d$ L2 u) S6 ^& u static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, 8 R! ]7 K1 U, g, c/ k
const vertex_t* s2_start, const vertex_t* s2_end) ! t# i" M1 y$ P3 ?( `' }
{ 9 L! } ]- s" h: Y* y+ y
return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
9 ^7 C/ @, i( m3 f is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; 7 e' U0 L% j. i. @
} 0 w9 t1 a! Q6 n3 m/ c/ K
! }1 o) o! X8 f: M# O: y3 w
" h% N0 r' W$ O) h0 a 下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
[; Q) }# J+ e
! G7 I5 s" c( S. t5 R% V0 n以下是引用片段:
0 P9 e! D* U5 Z1 M( Q$ P int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ 8 H, m3 s( @7 C& I3 [/ q1 a
const vertex_t* v)
0 ]% ^- } F4 Y3 v2 o6 O7 ^/ U { 4 }$ L( k/ B5 J9 q) [' |8 O9 f
int i, j, k1, k2, c; - ?- I9 ? f( F; m6 Z- C
rect_t rc;
9 `7 Y* [, m$ ^ A- |- t vertex_t w; + ]+ Y/ \, k! X& J' l* a
if (np < 3) # q# g+ a5 W; p" q4 E4 L N3 p
return 0; 3 v+ ]# h6 G% U& j* {% u+ h
vertices_get_extent(vl, np, &rc); " q8 N: |; Q) c# w$ x" k2 R. S
if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) ) H' O4 T) X( M M/ g: Q! Y
return 0; V# I ?6 o4 ]: e$ s2 W
/* Set a horizontal beam l(*v, w) from v to the ultra right */ $ u/ y2 v+ e( I" \6 K' |
w.x = rc.max_x + DBL_EPSILON;
- `: x- E2 v% N1 G w.y = v->y; 8 o0 r6 f7 n" \ Y& t( Q# Y
c = 0; /* Intersection points counter */ , ?' N! w7 h. [
for(i=0; i
" o" u0 p9 J3 Q, M$ U {
; T+ T- ?2 M2 Y, Y j = (i+1) % np; ; N1 g2 v+ }: e! ^ v; |4 P; f6 x/ [
if(is_intersect(vl+i, vl+j, v, &w))
9 X* P A4 Q5 y& x8 B' c* Y { : F. v) S& v) |4 Q' z6 _- x
C++;
4 ~, ^: a! O1 i" u } L9 F2 M) L# J# C: k y, e- ^
else if(vl.y==w.y)
1 J) P8 P: F; _6 l7 ]" ?; U {
6 Y7 y8 |+ w, Z: }6 o& P% e- e k1 = (np+i-1)%np; , {+ P3 @, a. F) R1 l
while(k1!=i && vl[k1].y==w.y) 0 W; I, w! c( X$ x x9 ^
k1 = (np+k1-1)%np; 7 N; m) {$ g1 P6 X: y
k2 = (i+1)%np;
+ W) O( O9 \7 N while(k2!=i && vl[k2].y==w.y) # `" ]# A, n c: `: G) D( d' J
k2 = (k2+1)%np;
: a1 e, y5 j) B" T H/ A3 r- s' W/ A if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
& K0 Y) i6 u2 B Q C++; / w# t1 h' g3 d% n$ P
if(k2 <= i) 4 J; \1 c) w" D k9 u
break;
4 Z" s b8 T9 c2 N+ b i = k2;
. |2 b1 M* [' [* l4 N- j" C! e } 4 Y; n1 `! B1 C3 s% s" f% S
} 0 j+ A1 x5 _9 w0 i2 d8 E
return c%2;
" V# A+ P5 X# X/ w1 s }
# t7 C1 i2 S3 N) |9 ?+ \3 P
3 z+ L0 |% M8 u3 _ t$ Z
7 C: ?$ k' @; N0 v0 y% k0 S) g 本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|