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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
- C' Z% m8 F+ K/ q' j+ Z/ r% ]# A1 V) m3 e! n& b: [) c
这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。% }0 @% T6 U% P1 W5 s) ?+ V
" c- h: N- G* y( y% G+ r 首先定义点结构如下:
+ O" i. J% H+ e+ q" _
9 l9 c! R8 {9 {: e7 M& ?以下是引用片段:( {. G/ Z4 ~5 K0 S- x0 U( D
/* Vertex structure */
# L1 w2 x6 x8 j# o( L- w, h1 s) O typedef struct 9 X$ f- ~/ l. K" w$ k
{ ) b$ ^ t7 R4 `9 a; l6 J. a
double x, y; 1 {' m2 r2 @1 X4 m
} vertex_t; 4 j3 w6 d% W" T5 e* R5 O/ n
7 K0 M x+ b0 R. q: K! g
5 i/ A/ b }3 U/ p; ^) _
本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:9 C" i) N( w5 h. G* [$ }
. d* y2 P* W6 f# a# f+ _以下是引用片段:
$ L% G0 i% O! G8 G) l /* Vertex list structure – polygon */
: u+ j. c8 `3 y! Q/ J typedef struct $ @9 h6 U e4 f7 V/ q8 v( k V
{
! D8 E' D4 M/ F) I3 I" |! s4 Z* o) j int num_vertices; /* Number of vertices in list */
9 ?' `$ J; L( q8 `, x( \+ G* R vertex_t *vertex; /* Vertex array pointer */
' i; E( n9 Q+ ~ } vertexlist_t;
0 X- _5 B+ `/ I. A0 O- X, K/ R% X# z; D! ]5 U
8 ]/ c* {) `1 R! [' q5 J, s
为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
; p( s' [7 g$ o! ]+ h
) m$ p) R& h% a6 y以下是引用片段:
( \# C, J: @: m) B" i /* bounding rectangle type */
% q+ y1 |2 Y; @- z" p: O, o( _$ U typedef struct
& \8 K+ @( E% R+ C I3 w* Y8 B {
2 A/ I+ S; n* a double min_x, min_y, max_x, max_y;
2 b& L" ]4 f: |( g' i7 C } rect_t;
' Q* G8 [2 A" w5 m1 |% k% B% k( m /* gets extent of vertices */ " @. {& f K% }2 R
void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ ) K3 a- E8 @! O, J3 u( x
rect_t* rc /* out extent*/ )
7 q" P- `3 S: x' |6 B/ ] {
: ^( m! }% f' o- q, R0 h# ^4 k4 v int i;
' d$ F k2 T, }- }; n if (np > 0){
& ~3 f) Q3 T Z% |! a rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; 8 S$ p0 T1 D; P5 ~4 L# j
}else{ ) M0 C1 C, V1 n' e) Y$ [
rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
3 w* W P. E; `: A1 N2 ? } + D( D& i! ?! J, j) f/ i, l- E- L# L' L
for(i=1; i # \# |5 t- e6 ~+ p! V9 K9 l
{ $ X8 g0 r# S W, c
if(vl.x < rc->min_x) rc->min_x = vl.x; ) ]2 E. y3 Y! P! H9 w$ `$ N
if(vl.y < rc->min_y) rc->min_y = vl.y; " k' |: T6 r* h0 _* |: F
if(vl.x > rc->max_x) rc->max_x = vl.x; 4 e, n) A ~! {% D; A0 O" ~
if(vl.y > rc->max_y) rc->max_y = vl.y; 7 u% ]4 v% X# P% @
} . r0 K* _+ c1 N' x/ b3 `# s+ i. V9 R; {
} 9 W/ M! ]* @& X, t
" I! q$ L$ {, X+ W
- y/ Y8 Z" H7 E# {
当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。$ a7 A8 b2 t5 F6 S
* m, z. R: C: Q4 H 具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:* w7 s+ X3 K0 y0 M% h: _; q& V
$ {: B* G! g5 y (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
+ V* x4 i4 W! A' y4 ?2 N; k& j1 E {9 r. H3 q
(2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
2 ~; H0 h! s. N. ~4 D! o$ y% n; D% z- j5 x+ X4 [( F; b+ \6 d5 ?
以下是引用片段:
- [5 |. [7 V' D1 y /* p, q is on the same of line l */ ) G W1 m# K+ ^' C" U# F. i8 R/ a$ L
static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ ) R9 t1 Q, W+ r2 F
const vertex_t* p, & O9 N& F [: T
const vertex_t* q) ! p1 W e9 u9 ?# Z8 H# c/ |2 W
{
8 A. b" [8 R. M/ \8 H. D double dx = l_end->x - l_start->x;
9 i- v- o, H- z) n. I' B" a5 K2 n double dy = l_end->y - l_start->y;
y2 x; M' d# j- \( J! [ q/ o double dx1= p->x - l_start->x;
' H) l& s8 v8 u, u double dy1= p->y - l_start->y; : Y# k. u; D" p, A4 x
double dx2= q->x - l_end->x; / e% ^; p3 z8 r7 ]) E
double dy2= q->y - l_end->y;
8 m. F! u$ ^+ C1 V4 y return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); 3 v1 V8 k! P j6 {, F' L% _
} + t+ o* W+ U2 A/ _: r3 C- ]
/* 2 line segments (s1, s2) are intersect? */
! o6 t5 ? Y7 w/ S static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
7 n5 R* a/ n( A/ D7 v' Q% ] const vertex_t* s2_start, const vertex_t* s2_end)
" V+ G# I: m8 ^( d" Y {
m0 Q% B6 a+ b3 {/ D$ o9 k return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
; h! S+ n" d' F9 R7 U1 n; H0 c0 F is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; . k' U* D3 D8 M* B% R1 `
}
, T7 z. e3 Q* _* B V6 T# _4 ~9 Z, L9 [" W% j0 {
f( p: Q8 V+ C `' e* Y 下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:& M# Y# V4 E9 N* v0 q& T' l( A
' p: W$ \: Q# z% `以下是引用片段:
8 O- z- e* A0 V/ S int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
7 d& p. j" j( a! x3 i const vertex_t* v)
8 J3 }5 v n, D& Z- ] { 6 X! y1 C5 [8 \1 @% T. Z
int i, j, k1, k2, c; - g8 }4 R. J& v$ V& q: u
rect_t rc; 3 N1 G. a. U+ `7 F: U
vertex_t w;
9 x |+ A! v8 x; Q3 m: a& i if (np < 3)
6 n8 k# Q6 ~+ i' @ return 0;
# V [3 \; X1 \9 { vertices_get_extent(vl, np, &rc); # `( D8 o( Q4 L2 Y0 h4 z9 M
if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
# O# K* }/ D3 L' l5 }: Y return 0;
+ W Z5 ?* F8 d+ a" k /* Set a horizontal beam l(*v, w) from v to the ultra right */
$ y) ~7 F9 `6 |8 Q1 o n w.x = rc.max_x + DBL_EPSILON;
2 D/ \* c9 R) o. ~' J7 H% ? w.y = v->y; ( k! Z) A; J& o
c = 0; /* Intersection points counter */ / a- M5 E9 b/ c! Y1 c) J# V
for(i=0; i
; Z9 o" `, v6 L1 ` { % p! W; O5 D4 j
j = (i+1) % np; 8 ^- |2 }, Y5 S! p' D; D, e0 f4 ^4 M+ A
if(is_intersect(vl+i, vl+j, v, &w)) % _2 G/ a" J8 f8 Y; k9 G
{
$ [/ s7 c6 f% W' I: y C++; # G. `$ s4 u7 X
} $ z2 P9 |1 n( L6 `
else if(vl.y==w.y) 2 E( b# M7 f- A/ R
{ Y1 a# w$ n2 F- @, s) b) Y- }
k1 = (np+i-1)%np;
, d- v' R; M' W8 a" b( v2 a while(k1!=i && vl[k1].y==w.y)
) F2 l5 ?% ] P+ i D* I4 U k1 = (np+k1-1)%np; 6 K7 _: _: _) G% x
k2 = (i+1)%np; ' h- u6 A" S# e2 ?$ Y8 [" L
while(k2!=i && vl[k2].y==w.y)
1 T; y% F( C% C/ {8 _" C) t k2 = (k2+1)%np;
& o# i: a6 G5 o; f: I if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) ) b7 A. T) \ O3 O
C++;
& f6 s' X. I3 c! T if(k2 <= i)
2 i8 m" d0 X( i. d- ~9 ~ break; & o+ C s j5 ^- L3 q9 o* n7 I1 a
i = k2;
4 D: R# l4 b, a( M6 R/ X }
2 m6 i4 R! e( d }
& S( Y& L# b/ {" @7 B return c%2;
1 w+ q$ I) o5 {0 z; W } - v- b) K7 d$ c4 C& k% y* G8 P) y
7 d3 ~# ?9 k1 h
8 w- `% B4 `' A% F+ D8 C# [- q 本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|