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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。; @: z4 e: s; b) l' K
) i4 O7 C- g- N5 E, y: ?+ H2 O
这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。4 a2 h T2 p Y7 T) x
3 p" a7 R K5 S* ?8 a# W: B 首先定义点结构如下:/ a% }- K! E2 E- P2 V; S
% `; s( f3 {/ D r. n4 _以下是引用片段:- q+ }8 Z8 w7 R5 |9 A$ R
/* Vertex structure */ B- d4 h/ H2 y6 A! S' Y
typedef struct 4 V k( E; o1 U ]$ v
{ % L! s; w0 z+ R U% Y9 N* F
double x, y; 0 s- ?% T h @: y0 f% J3 {
} vertex_t;
2 @; U$ N( S2 }& p( b# z
# @) b( u5 ]) s5 c. ~8 G
) ~/ m3 A3 D3 m5 k' | 本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
( m1 T) K0 @; |/ J, D- s) v, P: V! B
以下是引用片段:& W! \. O1 \6 h
/* Vertex list structure – polygon */
- B: @& t+ J' N- F9 q3 G/ t typedef struct
1 z* K: d4 Y! j+ |. Z {
, _( ]6 f7 l4 Q. p# X1 } int num_vertices; /* Number of vertices in list */ ( ^& ~% L1 Q! ?8 Z) b# F" m9 Q
vertex_t *vertex; /* Vertex array pointer */
! ]. T7 [; A9 o( _* t) U4 A$ ]/ F* b/ A M } vertexlist_t;
! p/ d3 J |& W5 v0 l* t
( ~0 w. W$ p" v' i8 h
7 ] E6 Y! T) i" z' a 为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:8 u( ?. |5 u. ]$ m7 q8 p
q' n- j) ^8 y1 ^
以下是引用片段:% M H0 t3 [# [" r8 }$ A
/* bounding rectangle type */
' q' p+ r: n; ^' f* W! z typedef struct
/ r: J ] |0 P( g/ F; L1 E { % W7 g1 p* X/ W- V" f
double min_x, min_y, max_x, max_y;
# a4 `. ]" t& m, ^ } rect_t;
* F1 ?4 m7 z9 D /* gets extent of vertices */
: ?+ ]; w, I, f$ u0 | void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ / H# C$ h0 P5 K9 ]* a# b
rect_t* rc /* out extent*/ ) " O' E: n+ A1 _! ? ]7 ^* H
{ 2 ^1 i8 J9 i! b' F
int i; 0 j6 d$ v8 r" A
if (np > 0){ " d8 j0 O. {* Z) K5 h4 j- I, m9 P
rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; + |+ d n. \' J& M: I# N1 Y
}else{ # x1 T. L+ P+ U) \& D, O
rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ . H+ k6 _$ L) f! F# s) a
}
5 {) Q2 E8 _" r$ Y& \ for(i=1; i
! }$ U3 l+ v0 s9 e { Y0 [! Q3 D- Z }! @& ^3 c
if(vl.x < rc->min_x) rc->min_x = vl.x;
: h8 U! R5 u) G% K4 B, B+ Q' ~ if(vl.y < rc->min_y) rc->min_y = vl.y; & _! d! Y4 Z, S" F. z0 F9 a
if(vl.x > rc->max_x) rc->max_x = vl.x; : d( P% y, E) n# Y" f' g# T
if(vl.y > rc->max_y) rc->max_y = vl.y; 5 S2 K1 n6 H# c+ D6 D' ^0 k9 d% j
}
3 ~* w( _0 ]/ @8 i }
5 a9 H5 u& p/ n- I6 M# ^, G" {: b; X' M! I) m
/ F& P5 D- G' r" B3 A6 Y3 K
当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。" e4 o& @' y1 w2 l6 T! S
7 h: F+ r; k7 v 具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:' \+ {" j0 M( { V+ [8 i; v
7 G4 c) }$ X+ K- p# |
(1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
6 k8 A- f+ q( E; R& }* E) K
& e- m# m( t% X+ a2 R: ^% { (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
: k* t1 b, G: m6 K6 D5 q" _1 v, \3 H8 `: Z& q M# @: F
以下是引用片段:
, e1 I' b; R# T, G4 T- p' w& l& e( N /* p, q is on the same of line l */
. W3 a/ Y$ y( c static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
+ U) E" U2 r, r const vertex_t* p,
J( L: \$ z, w z' H R( i/ X const vertex_t* q)
, k$ `% U) Q9 t% u- F {
0 q! S' a Q8 R. M double dx = l_end->x - l_start->x; % P" x; n0 s% v1 \* l8 B7 h
double dy = l_end->y - l_start->y;
. V& n: X$ i/ I double dx1= p->x - l_start->x;
) m+ e& S8 h+ u# I double dy1= p->y - l_start->y;
+ o6 l7 t& a/ U% ]8 ]" ^ double dx2= q->x - l_end->x; U0 W9 h9 L- B$ U& c2 R3 F
double dy2= q->y - l_end->y; + ]2 ^( y' b/ B* P; ?1 V g
return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); ( u. O3 f. X6 [6 m! o
} " q l) c z) b
/* 2 line segments (s1, s2) are intersect? */
m m. I5 F- B( R' u b static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
- Q7 e Q5 i {; j" {) {1 A# `9 r const vertex_t* s2_start, const vertex_t* s2_end) " r/ H: `& O# n. {
{
5 G" x& U- W: ] ] return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && % K3 P& Y1 }4 I; T3 @" t0 x
is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; " _, v: | \# ]2 ]4 t0 h$ R) _
}
4 Z; ?7 {8 Q7 D6 d h! D4 I! M! a
- b& Z0 _: D" m- n" Z' O }. e. k
下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:* L R9 u+ d7 v' k+ _7 U. u
+ D3 O0 A* a- S: \& D- i" r5 W以下是引用片段:
7 D0 s9 L4 i; U+ d; y4 q6 x int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ $ Y T1 Q1 F) Z. [' Y! h) x4 {% h
const vertex_t* v)
$ @, U: c4 q! y* M3 t { # S7 |' K$ X* L2 q" r8 C
int i, j, k1, k2, c; 5 X1 v- ^1 E) u& g2 l& S" ?
rect_t rc; ; S3 U1 v- {2 K
vertex_t w;
& n# h1 s* ^8 S$ w if (np < 3)
( \7 W W8 I' y& j' S5 _& h) Y- u return 0; . ?+ `6 b W8 m R. W' z
vertices_get_extent(vl, np, &rc);
1 @3 e: k7 d$ l/ ]9 R% p v if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) * w: c3 f: c' s- ]. ~7 }9 _
return 0; 9 N* Q4 q! Z, I7 P z" q- W$ J
/* Set a horizontal beam l(*v, w) from v to the ultra right */ * d$ c3 w7 }" r0 Y3 K9 z) i
w.x = rc.max_x + DBL_EPSILON;
& K/ y/ O* a" q: l4 X w.y = v->y;
8 m! Q: V2 O6 R9 y0 A, x c = 0; /* Intersection points counter */
- {# o# Z$ e2 o/ ? for(i=0; i
7 Q, ^" V1 P( _0 u' G( U8 q& g' I9 [ { " L. j @1 u0 p8 R
j = (i+1) % np;
, B) n- W6 Z! n$ A: R if(is_intersect(vl+i, vl+j, v, &w))
/ }" t4 P8 u h! r* F { 7 G: H9 p0 }+ `1 m0 G- e
C++; 5 J3 W# f! |) w7 o
}
3 c: ~3 ^+ K0 T0 b3 F else if(vl.y==w.y) 1 l7 j) ]6 z6 P; M
{
4 d# H) q7 n! j4 F; ` k1 = (np+i-1)%np; 7 ?$ L( W! u2 z% K, |' u, x
while(k1!=i && vl[k1].y==w.y)
& b1 Z0 |2 p. ] k1 = (np+k1-1)%np;
@, I3 _$ w9 G k2 = (i+1)%np; 6 m; N: H6 k: O) ?
while(k2!=i && vl[k2].y==w.y)
% N) h5 m& U, d" X' c* O. D k2 = (k2+1)%np;
# [# p8 N' u9 x U0 Y if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
$ E" V; O* t8 r0 C4 A4 \( Q) D6 I C++;
3 l: h5 e5 V5 r if(k2 <= i) " x$ _1 y) ?! Z3 [& `, |5 g
break; . @. I1 }8 U% W- V' S( s
i = k2;
! k2 p- F: H1 M9 N5 Y" P! ^ }
2 ^( L) a3 }- P5 M } . X6 f$ _5 o% w# D- o* U
return c%2; ( e8 n4 Y7 c* o) p4 p: Q! Z8 ~
}
8 V% W% E+ c8 D: t5 v! Y+ y
* }- [( \6 y! W/ E/ V, w F$ r4 y9 f$ o7 `
本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|