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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。: m% W& J' j$ e+ ]
$ N3 t8 d. C& U8 h3 C. |$ S$ Q E9 j) G 这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
5 D: W) C- F/ ?& m a0 B& r) a
2 `, Q1 w4 z( W 首先定义点结构如下:
' n& C) |5 K* O% s; O0 t
6 d+ k2 t& T; ]7 O+ N以下是引用片段:. A0 A8 P4 B/ y6 U
/* Vertex structure */
& a) F. k9 t, D- ~, Z1 r typedef struct " ]( `6 }) \' K+ P/ A
{ 1 L) W, F" n- m
double x, y; {5 M" @2 f, C& i( o
} vertex_t;
# D! f3 o4 J$ `/ H5 _. _4 ?7 G5 N' U, n- c
' E4 w& x8 X: k5 i+ K8 V/ _' K 本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
: K5 j3 _, n5 M- `5 }2 D$ l, t& Q G% J
以下是引用片段:
0 N3 O O6 H5 u1 [( ] /* Vertex list structure – polygon */
- J0 d+ L4 D. M7 D$ ? typedef struct 4 P9 U2 X* ?% N: E
{ , _0 D( E: Q) W$ f- j& E
int num_vertices; /* Number of vertices in list */ ; Q. {! Y3 \+ v9 Z
vertex_t *vertex; /* Vertex array pointer */
4 P$ U) s3 q- e7 y5 L } vertexlist_t;
! T, b$ i% Y9 |& h
6 N3 G9 d/ R C3 T$ h. _& l% y. T0 L# x/ O6 P* }+ E( k
为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:5 ^/ W3 v8 k% T) q
$ g+ I I5 r9 C W9 b6 o: i7 E8 p以下是引用片段:% S9 f w7 n: k2 H! v
/* bounding rectangle type */ ( |' J' m: T4 ?6 l* G4 W
typedef struct
2 l! _, n% l/ H/ X {
; N. H/ B) \6 b double min_x, min_y, max_x, max_y; : g, T4 J: L, ~0 w
} rect_t; , P0 q9 `6 {$ h1 Q4 y
/* gets extent of vertices */
3 c- W6 s! c# p+ L& m* C void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
2 |2 Q5 |$ a9 Q. d, i/ Y8 Y rect_t* rc /* out extent*/ )
. c) c! \ b$ B9 G5 s { * ^5 H$ N. _2 o0 N3 X: @' |
int i;
. h( i$ u% j4 v% H8 {8 m if (np > 0){ & ?8 i* s+ {+ e5 O u
rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
0 f A# R. h6 I3 I0 G4 {+ {9 O' ~5 A }else{ : H V5 @ p/ ^9 W! n; W
rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ , \1 k- H( Y/ k0 L9 i% F
}
5 j* g) N& |% A4 K! S" Q2 v, {: J for(i=1; i
^) r; [, _. c6 R6 Q { & ^! y6 u# E: g) o+ K8 q% ? u4 g
if(vl.x < rc->min_x) rc->min_x = vl.x;
0 e1 X! f2 U3 A5 T) W1 H: ]/ J if(vl.y < rc->min_y) rc->min_y = vl.y; $ A- ?* u. U' T' V
if(vl.x > rc->max_x) rc->max_x = vl.x; " g5 `4 }) x% @9 E5 j& _9 j
if(vl.y > rc->max_y) rc->max_y = vl.y;
9 A* [4 Z. i8 c8 P }
) x2 C- O" ?4 y& Y- E }
3 u; S u9 B( S
: Z; s7 j( l" o5 i# n+ R2 M9 b) a6 j. J4 Q6 y0 Y
当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。8 J8 U" f. [- x* P( [
/ m$ g2 ~7 X4 B
具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:& F+ w, w- ~1 \" `, r4 V b: |
! z5 N2 {/ z; E+ z! O2 f) U4 p
(1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
- }$ f* x9 M! Y* l& l2 X
$ t& I s) L1 d; f" \ | (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;$ u7 ~! {, K# x$ ?1 F! J# s
) ~% i6 D0 @$ ] P8 Z2 A2 N$ ~以下是引用片段:
2 E1 O8 @3 j2 T* {1 i' X: Z8 S# _ /* p, q is on the same of line l */ * Q( R* R M& y5 D0 f9 ~' H# t5 ?' ~
static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ : e7 c7 Y7 N1 z) x7 g3 t
const vertex_t* p, ; g$ z- c4 G. g! Q1 a) Y, s
const vertex_t* q)
% R/ h: A& ]5 @ {
! V: ^% k/ k6 J Z2 c double dx = l_end->x - l_start->x; 4 c |6 V- e7 E! u7 \9 k7 | l
double dy = l_end->y - l_start->y; ; u* w4 S; c& i+ E0 i' H/ i
double dx1= p->x - l_start->x; 2 Z7 \" o( e6 @9 Y4 C7 c
double dy1= p->y - l_start->y; n2 G4 X+ x+ b0 ?
double dx2= q->x - l_end->x; $ Z [7 d5 c7 Z3 i* A" d
double dy2= q->y - l_end->y;
/ G* c! i8 G0 l" c5 t3 m9 z return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
l( b/ v: [! v, v* L: q( F }
9 G- W7 a0 K6 B$ Y' d /* 2 line segments (s1, s2) are intersect? */
! e2 V! E- h1 _$ n# A static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
* r- ]( u% d5 s7 `9 `, L, j* B V const vertex_t* s2_start, const vertex_t* s2_end)
$ D6 l3 @; Y% F {
6 z' ?: }/ r" Q5 ~& B return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
: _ q% p2 ~- N& v" n" a+ [/ W is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; / C- T: L' k% g5 E4 K! d
}
4 V- ~0 M! [+ | _$ C! o" k5 B
* p7 z- K& b7 p+ F% B! D' m8 o3 c( v" _% f( v+ v
下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
6 I0 O! q: u8 k0 T6 y9 l+ R! a }5 [" T) k
以下是引用片段:
& v! d, I+ a$ u1 C# v& g1 c; X int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
Q0 |# F1 z1 {1 B7 `0 N9 O1 x& Z! | const vertex_t* v)
+ ]1 b# S1 U3 ]8 t0 C {
$ u' V% j% Y3 P9 R6 z4 t3 R1 s int i, j, k1, k2, c;
1 T: B; a( q2 g% z5 c rect_t rc; 9 h1 B1 e& h' v% P `+ Y& N+ |
vertex_t w;
8 S2 o! C+ c8 ^) T. S if (np < 3)
4 g2 U ~2 v3 d2 x4 I return 0; 3 o; B% N. A% L" S1 [
vertices_get_extent(vl, np, &rc);
2 }7 J8 w6 p1 g+ g/ ]2 { if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) ! @' F3 @$ q2 e3 a. U
return 0;
: [- U u# K! O0 W /* Set a horizontal beam l(*v, w) from v to the ultra right */ # m: b8 I) z1 _ g6 K
w.x = rc.max_x + DBL_EPSILON; 3 K; x' ?+ \ M) n) @
w.y = v->y; 0 h& Z2 z7 C+ b9 A' A3 Q
c = 0; /* Intersection points counter */ 5 F: @, c$ n7 j2 G; H# T/ n+ m
for(i=0; i
" D; r ~( B& {( \) k { 4 s; B" o+ ~5 c9 d( z
j = (i+1) % np; 4 d( \! Q6 A7 } {6 m( K0 n
if(is_intersect(vl+i, vl+j, v, &w)) 9 z9 h5 r! \- `' n
{
3 V" {# e, l) _; G2 u" N C++;
" `; E j/ Q& p5 W } 0 {9 Y2 p/ X j) e9 g5 J1 T! u Y
else if(vl.y==w.y) " z5 F# u8 u* r! v( Y3 m/ Z
{
X: h! W$ [* J- f' Q, Z) [; _ B k1 = (np+i-1)%np;
& j$ v) v4 m0 K1 ]7 P while(k1!=i && vl[k1].y==w.y)
" @7 R: X' Z" Z k1 = (np+k1-1)%np; 0 y- j8 j* j$ W3 t: s2 ]: R7 }
k2 = (i+1)%np;
5 i$ I) h7 G+ T, H7 c: ~& } while(k2!=i && vl[k2].y==w.y)
! o# Y, F- o0 G% i+ O1 J# p k2 = (k2+1)%np;
2 \; |* R$ s5 \+ P/ W7 I if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) 6 [; `. {# o( R: u' [
C++; % b7 g! e. w0 _# I6 j! d. u% u4 j
if(k2 <= i) 4 e4 \0 _! E7 T" K
break;
# p. O4 z' Y w O i = k2; . l. L& B$ a# f0 i: n& b
}
! h. ~, O# K( [6 w' D! ^8 i7 i }
! A' n" K2 S! H, ~* f return c%2;
4 q% U: e% P, c, P }
N2 l, v" X$ e p
5 C& q: `# N5 H0 j& _' P
+ @+ C& P% q7 g9 f$ [$ w6 u 本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|