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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。. |( @8 W; p" R
$ ?: D: ]% x: R; h- u! o
这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。% o; m& N+ G+ V3 v/ @0 b) h
7 `8 ?2 _3 R# J: @1 U 首先定义点结构如下:
+ H0 o$ Q" ?! j
: S+ o' u) q& C0 T' T& D0 N. s以下是引用片段:- k: C4 [( C, ] s. d: N+ _
/* Vertex structure */ 4 b3 [5 D( o- r: I( p3 t
typedef struct
1 y5 }7 d. r' @8 N7 h7 K {
9 g$ }1 `9 U' T$ V double x, y; 9 p' ~! y: Q: ^! _* R' s: ]
} vertex_t;
6 a. h% q x3 X* A2 E" c- {7 p9 [& s: C" n3 d4 Q
0 @! O! D; N% k. ?) q' S" J' o1 E 本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:; Y% d: ]2 ~( l, H8 J+ j9 t
) V2 Z* q; d( M3 y1 u
以下是引用片段:
+ z$ L: g% i6 H7 ? /* Vertex list structure – polygon */
% f9 U% \! j. g$ W typedef struct 1 k# X) [$ x# p) M; X
{
6 r6 \3 f8 [) c* W7 l9 `' B int num_vertices; /* Number of vertices in list */
! L% ]) g; Y' o0 A vertex_t *vertex; /* Vertex array pointer */
, O( g, i: N" U$ Q& X7 }; q } vertexlist_t;
# Y0 T' K/ T$ K4 B* c% Y
2 H1 W4 g; [0 x1 Q( s1 s: c1 M1 V, i6 v9 u6 Q
为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:, `$ d- A F6 u g
- I: N5 |. z# H8 q4 S ~* ]
以下是引用片段:
- t7 r& R, ^/ h. I/ b /* bounding rectangle type */ 3 |, d3 P* t$ E& R# }0 ^5 ]
typedef struct 7 h+ C- K" U. w/ A2 W' S
{
1 V, D8 z/ q4 ?( x double min_x, min_y, max_x, max_y;
. a& N/ t! H# F: X) J1 q } rect_t;
3 Q' R6 E: r4 X5 g9 d `6 N /* gets extent of vertices */
" G4 k7 g" R4 L# u8 b' n void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
& q1 X; C- }* p; W rect_t* rc /* out extent*/ )
* d ^" _' i7 \. p {
' G6 G. m) _% p+ y* a$ R- k int i; + c% }1 v: Q. X) g9 r0 ]
if (np > 0){
! V0 ^$ S% X1 l9 n- _2 T; f rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; 4 w) B5 ^; m. V9 r8 E% g. _$ {
}else{
! U) _ b4 `& @( \8 E rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ ' l& l' O8 G, a* S) v* D0 Z0 e. Q" [0 V
} : N$ i7 K+ A& a% o; h3 _0 D8 S6 g
for(i=1; i
! g" ^7 f0 p$ G+ T {
9 k' R0 c) B& V' u* K3 n0 k' u if(vl.x < rc->min_x) rc->min_x = vl.x;
: o) n8 M1 ]" E w) h if(vl.y < rc->min_y) rc->min_y = vl.y;
" i' I6 ]3 Q- d! w- O; n if(vl.x > rc->max_x) rc->max_x = vl.x;
7 n8 s J. @) R5 A% N* v! ~: | if(vl.y > rc->max_y) rc->max_y = vl.y; 1 s$ A/ S1 r; Z0 w/ ~8 p! ]
} 3 Q9 B: L' ^, I7 c9 y8 p4 c; S) x- z
}
& f7 u4 Y0 x v1 W Z* {4 \. J" R
6 Y1 P4 `! S; G6 M) B" E: h+ S5 `. p' \1 k" \, N
当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
/ a* P6 O' X1 O/ t- D, `6 ^$ A+ z: Q! t* A8 T D) c
具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
( c) ]1 M9 [' A4 E$ m' s' e6 m9 z3 v: c' n. z( }1 o: ?
(1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;& t) @$ g3 q8 s! }2 X
" |+ B" t7 N6 D m1 i
(2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;, F% U8 W8 X. U: r; F. d
V! Y1 q6 }0 m: s0 o8 p以下是引用片段:
5 F6 l& g% m# V+ j5 q( N( ]& ] /* p, q is on the same of line l */ 7 J6 \4 `0 q' G6 r3 ~; Q
static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
5 l E" {; c8 v8 Y2 G( D! n1 ]0 r1 o const vertex_t* p,
) ~/ ` y4 O. P8 M% m, L const vertex_t* q) , ?+ U' B O% n
{ e, B0 | J. }" ^$ ~, r9 U5 ]
double dx = l_end->x - l_start->x; - ~* \1 @ H9 m. {$ M
double dy = l_end->y - l_start->y; d# B, J6 E% G* l6 f
double dx1= p->x - l_start->x; 5 W, X( l& y* x) o1 G
double dy1= p->y - l_start->y; ! }3 Z: n- Q3 Z w2 E2 O
double dx2= q->x - l_end->x; 8 h; o* c& L: Q1 ]
double dy2= q->y - l_end->y;
8 `4 D) @' G& P+ ` return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); 5 |" Q/ z* l: b* a" U8 g: d9 ?4 ^
} - z, B* j+ b& N K
/* 2 line segments (s1, s2) are intersect? */
/ n: f+ q: ]. {: |$ D" w* S5 q static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, . e7 I# e- k; s* {" h
const vertex_t* s2_start, const vertex_t* s2_end)
. S' @6 \& [8 ^$ q {
! I$ ^* q9 y& u9 [ return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && ' G: ^$ X5 @7 J
is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; . X: X p, n; W' t# v7 X
}
, Q9 R5 S9 b/ A# Z: P
8 y% I0 a0 h- n( U* l
. V/ B$ O! P7 o/ Y6 B% t 下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
4 ]# F3 m$ v- _4 n
. Y: B+ v% n& Z& ~5 L以下是引用片段:
3 s+ c2 }' c" W, k- |3 n+ ? int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ 5 Q6 {8 j4 [9 a) l- n( h( q
const vertex_t* v) ' p( j0 o% w& Y }# K2 |
{
# Z- |* |4 A3 Y+ o Q int i, j, k1, k2, c; ' Z/ B) R q$ W/ _
rect_t rc;
0 X+ w9 R8 H8 \# l( s7 F+ c vertex_t w; , ]6 e+ V1 j0 D4 f
if (np < 3) , y2 U# X% j( V
return 0;
4 {" m& \0 N, S. F. B0 L2 u vertices_get_extent(vl, np, &rc);
% a, a- o% k' P& k( L. H- e if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) # T' e: I6 Q% B1 r# Y3 g
return 0;
# m( |3 p8 ` b: v7 c1 l4 j( L /* Set a horizontal beam l(*v, w) from v to the ultra right */ . z" w( Z' K; ^2 @) d+ y
w.x = rc.max_x + DBL_EPSILON; 3 | A" d6 e! Z" [' c) I
w.y = v->y; + T4 m- H4 w" O; {
c = 0; /* Intersection points counter */
, l" L% q( p# Z o7 T" T$ g! _" |9 B for(i=0; i ) p2 k5 _( l( b3 Y7 o
{
% G# |9 B* W% ^ j = (i+1) % np;
# b p! Z, y4 v m( R% ? if(is_intersect(vl+i, vl+j, v, &w))
# Y( L% V- j, m8 q {
9 X5 X5 s4 G) `+ `: o C++; 2 l7 d& n/ k" |" T: P
}
6 m6 z1 V+ y2 q% j! x else if(vl.y==w.y)
, T% i4 e* ?% n5 t' S" l( ~ {
5 A, W w2 u8 I2 x2 b0 U* k, M k1 = (np+i-1)%np;
7 ]* R8 I+ m9 Z$ L9 \ while(k1!=i && vl[k1].y==w.y)
; E. |, R9 N1 v9 b, N- o) N9 X k1 = (np+k1-1)%np;
; R. P/ R( T' ` k2 = (i+1)%np;
: G7 C. B; w; \6 f1 k! @, D while(k2!=i && vl[k2].y==w.y)
' g: I7 O7 I9 u4 A& G; R0 J k2 = (k2+1)%np; ; x0 X, j$ ^3 r0 o$ s
if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
5 z# X$ X& s3 Y4 |9 d2 P C++; Z( U( o+ Q1 m) F- v5 f) I
if(k2 <= i) 6 e+ p! A& k* p. y
break;
" O) T9 `, [$ F: Y5 A i = k2;
1 E, a" x. ]9 N7 @. T) d } 8 }4 h- _+ A; Q6 M: T
} 0 v0 T" L4 q6 E3 h. l
return c%2; 2 U: ^/ J2 ]% S+ }
}
, F1 O7 s8 m$ I# n8 w1 R' @ ^7 i" G M H! ?6 T4 m' f/ ^! X/ r
" |1 w9 y9 G; i9 I& p$ o' [! z 本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|