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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
( n$ Y& _/ m5 d* w
: g7 n w" V2 K o. q% L% D 这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
: D8 C1 D! j. Y( ~; u# Y5 X R( _* Y( s8 M
首先定义点结构如下:7 D, B! Q9 r2 A0 P
9 ^' n3 s q" X; W5 Y5 H' J2 A1 o以下是引用片段:. M; B: H3 w, t& K6 o3 j
/* Vertex structure */ ( x9 a# ?+ Z8 y
typedef struct F& M' O" D1 o, i) d
{
7 P$ |( U2 M: u8 v8 M# d5 s double x, y; 8 }& e. ~* y; z$ M$ t
} vertex_t;
- X6 w% M1 \" m* _; m$ ~8 o K, J8 a
* ~6 M( ^+ B/ K9 Y9 ^/ v8 q0 `
本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:0 D7 m3 m. |0 A X" e9 n. t
' L# _' ]$ F4 d. w0 Z8 h
以下是引用片段:5 B- `, W; R- ]0 u* H6 J5 {$ k
/* Vertex list structure – polygon */ 3 v, s8 X" S; S/ c3 |' ~8 G: b
typedef struct # O. b- S m; \1 n8 h
{
/ C, A _6 R1 h, u* e' D int num_vertices; /* Number of vertices in list */ , s3 r2 e# N+ X0 p7 y, e
vertex_t *vertex; /* Vertex array pointer */ $ @4 H& b2 A: e" d, C- X& }, Y( W6 K
} vertexlist_t; 4 [ ]6 X6 R; b2 R' O E, C$ Z
9 B A0 @2 c' B; p; K: S8 Q' C8 {5 I* g# h: |& N$ Y7 P5 m, E, y
为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
1 o0 a; l& l$ j) D9 m1 B; n6 v
8 K& f4 L, w" h7 c# U, ^以下是引用片段:
' o, J7 C" _! s/ N8 \2 ~; K /* bounding rectangle type */
$ i" b# j- ^+ V; U& X- J/ N5 X1 d typedef struct
" L1 E% I% l# }9 `1 e { # m5 a+ r$ o8 ?6 U6 Z8 b
double min_x, min_y, max_x, max_y;
6 v! T& m5 @; L( p1 u( D, J- ? } rect_t;
' l6 m+ m1 y9 l /* gets extent of vertices */
. n$ E# G' C8 S$ s$ S& _ void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ $ V P- e) W. Y: N
rect_t* rc /* out extent*/ )
$ X. z& A! X5 ?7 K. ^. L; T {
- Z5 W% b% F! I int i; * `# u) u2 |# @9 ?9 ^; f/ [+ L
if (np > 0){ 5 h# S4 A" @# S \$ Y1 ^( ~& l1 ?1 g
rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; # h" y) n- G% V, b+ \, G
}else{
- O& F" I* X0 ] a, H/ G8 C: ] rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
% k2 D ]9 `6 L }
) z2 l$ l* d' E0 g. n: x for(i=1; i 5 r) d5 q9 Y. z( o* Y
{ n6 x, }" V# J. T
if(vl.x < rc->min_x) rc->min_x = vl.x; 7 Z2 y' I3 j4 c
if(vl.y < rc->min_y) rc->min_y = vl.y; C. B( k3 S9 F4 G! A+ G' Y
if(vl.x > rc->max_x) rc->max_x = vl.x; - Z$ ^* ^* b7 T$ _" q
if(vl.y > rc->max_y) rc->max_y = vl.y; 8 I7 v# o- _: X6 r* T% B
}
6 p/ r! b1 u7 e, [# L3 Z4 Y } * D- I4 C1 f1 i( Y; q [
6 B0 c, ~" s$ m+ G4 Z) ?
8 U/ R8 `- j/ d/ L 当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。3 A5 d' ~# B3 g& s7 E7 t
, w, z/ t1 o: V7 q' u 具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:5 {) E7 p$ ^4 d* `. H [
8 R) v; d7 D; g6 t/ P8 |
(1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;% c0 Q4 C% y! E c4 Z4 w
% M2 c. W* j! s$ J, w7 c. K, o6 B (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;/ p5 `2 N: N% b9 I1 o6 \# V
" m& U3 c$ u; a+ f% A0 I- O1 h# ^
以下是引用片段:
2 m9 t/ o m- b X. p8 u3 x, U /* p, q is on the same of line l */
+ Z7 ^9 g/ T+ `" v; _+ H static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ 0 z$ A1 H# e$ M, A4 Y( ^" g
const vertex_t* p,
. o$ `5 N( X! K* J$ Q1 u: ]5 d const vertex_t* q) i, R' j2 b+ S. j8 R4 L
{ + E8 t" v% Q6 V2 m' H/ V0 E; _6 b4 T
double dx = l_end->x - l_start->x; F1 c" I- z p8 V5 j$ H
double dy = l_end->y - l_start->y; 2 d( z# R1 O2 P) b6 O
double dx1= p->x - l_start->x;
4 ]7 \8 v4 ?+ r) W+ X/ D double dy1= p->y - l_start->y;
& C3 K6 s' |0 G9 A. A double dx2= q->x - l_end->x;
: f; }. L' w% Y7 x8 h5 R' d4 ` double dy2= q->y - l_end->y;
, G9 O3 `) U1 M& z# J( X return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); , r- t) W" N, M& ^8 w6 w( f, K
} + S9 k# `2 b+ e
/* 2 line segments (s1, s2) are intersect? */ & y# Y, C' _' I0 m5 N1 K' C# s k
static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, 7 H; U' a5 P2 Q- W- I5 h" H/ v
const vertex_t* s2_start, const vertex_t* s2_end) * A- W* I' H: @) O" ~# ?
{
5 A, w5 O# v* A0 o" u5 X return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && 9 t4 K" W6 u* r W0 w' |
is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; 3 R! e. o$ J5 z4 R, q; A9 N
} ' l* Z% C' x c9 l4 k+ R, i
/ k' `. K: v! i+ M
o4 A3 Q) K$ v
下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:( V1 O6 L( r3 x4 V* x
* A; ]8 ^9 u* v% @以下是引用片段:+ E4 U* d U o1 x4 G8 K7 V
int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ % ]$ |7 k: b2 O2 `& ?
const vertex_t* v)
, O$ o6 Q2 T" `2 u3 Y' D {
, A" t6 v5 x$ n4 j) \3 n3 s int i, j, k1, k2, c;
' ]3 z1 Z9 j" d& h6 u" _) L/ c rect_t rc;
: d+ r r8 H0 R& U vertex_t w; ]1 ?# z) I! ?9 |
if (np < 3)
; \7 W, ]% N. a+ D return 0;
) m' r: q, \, y- z vertices_get_extent(vl, np, &rc); 0 O0 r& a6 z: s& O$ }5 L; w2 n
if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) ) t2 W4 n. F# k7 _' f: ~- Q
return 0;
. M3 j- D% z5 N% n) K /* Set a horizontal beam l(*v, w) from v to the ultra right */
5 I! Y7 G# z+ s8 G; ? w.x = rc.max_x + DBL_EPSILON; 4 c1 B3 b4 p# ~. Q
w.y = v->y;
) @. C; a2 c1 O+ ] c = 0; /* Intersection points counter */ 2 v! k e& R. I1 o! o
for(i=0; i ' B: H1 o! L& O% s
{ " k* \0 ^% u; p- N7 `7 A2 l* T
j = (i+1) % np; $ f/ R! N0 F) C5 |2 ?: Q
if(is_intersect(vl+i, vl+j, v, &w))
; p9 {3 U6 _7 z3 I { & n( P4 F% A" Q2 [& `
C++; 6 w& Q0 X, k2 w3 Y) k. B: p8 h" H) T
} d4 t K! O) C6 n8 \) n# O _6 ^
else if(vl.y==w.y) " b3 f/ y- V3 F
{ # N- I2 _. c- L' E' r) ^
k1 = (np+i-1)%np; $ _: r0 r6 O) g3 S, C
while(k1!=i && vl[k1].y==w.y) ! v$ M# l) T2 f, Y( r8 U
k1 = (np+k1-1)%np; + e' Y$ c% a& s. h0 ^. o
k2 = (i+1)%np; 7 S* J2 r$ H. m; s9 g: C' `+ n
while(k2!=i && vl[k2].y==w.y)
& C( v/ z, V$ o! H! e% t k2 = (k2+1)%np;
z* ^5 `2 M# z& E. |4 y* Z if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) 3 a! m3 L- }/ A2 w3 F- D: v- _
C++; 7 N4 ~2 j, x' @
if(k2 <= i) 5 l9 q& K$ O h0 m
break; 2 t2 M3 x$ u* C6 G n, T, I
i = k2;
4 G4 G" m* {4 u: B. }; r0 I0 z } ; f5 j# }5 L8 a+ Y* ]. |. W t% `
}
3 @) B5 b: {: u+ I* ]9 ^; k3 Q return c%2;
% k: q; D- `' ~$ z# f% m0 X }
; V) Y: _) v) G4 E- M4 M" a8 p4 _* K' E1 C
* P; `/ a, b R: U
本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|