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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。4 f0 A' p/ T* G
- v; y. U+ p" d/ h% p 这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
! g" _& s; B; u2 L
0 V& m, r* `. @4 Q0 T' R6 L8 {3 ^ 首先定义点结构如下:! A7 t1 Q2 ?" M9 [( \5 d j
T+ r- |$ z4 ^5 r# m, n
以下是引用片段:9 P( v/ q1 [4 R; `! s
/* Vertex structure */ / e0 {4 A# }& ^
typedef struct 6 @7 t4 c- [1 A2 Y6 R! F
{ ; a( ]% |/ ~+ ~ ?1 `1 J
double x, y; , p- e( @. W$ j9 X1 D* }( f; {+ h
} vertex_t; ) X, A9 j# s$ W/ g9 Y1 w% x
3 K2 [1 t8 r6 W$ B
3 P; L( |& R$ R: X7 n7 [ 本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:- ^2 L( P4 D5 F/ Y
% o- C; r- J% g# @& a6 Y* ~3 |- r
以下是引用片段:
- R, L z) F% x8 J* `& }* l5 B /* Vertex list structure – polygon */
9 ?4 a7 {* W+ V+ I; n typedef struct 7 B$ Q5 r/ j, A, b
{
( n# Q6 P- z7 L# M8 P! ]0 k- k int num_vertices; /* Number of vertices in list */ ' d9 M. ~; v" C7 {$ L. o9 s0 L
vertex_t *vertex; /* Vertex array pointer */ , f/ L8 v: Y$ I3 F% Z/ f) h
} vertexlist_t;
/ O; m6 x i2 W/ E4 z# q1 t4 r5 A8 b2 K4 n9 u: P& j+ |5 M. @) C+ {
5 }" k) x; p! T. v5 Q
为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:! l2 Y7 @( f; z [; B9 i' i
3 S6 K2 Q* }* y) ~8 J& I
以下是引用片段:
) r1 v( L8 T: { /* bounding rectangle type */
$ M+ \" O& K+ F* y O% A# z8 J typedef struct ; u+ n) I4 I# ^3 Y! ?! l
{
' W; z L" V; s9 T) B1 X% s: R double min_x, min_y, max_x, max_y; ' Y# M# ]6 t1 b, `' V
} rect_t;
7 s: j# F, D8 }+ ], m5 G d4 h /* gets extent of vertices */
3 {3 X7 X% W4 }+ a2 k6 V0 c3 \ void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
) e8 Q0 i" }# t5 B/ Z rect_t* rc /* out extent*/ ) & v8 Z5 u; [" G9 W
{ : d2 L/ R) d3 R$ }! c9 R! l7 y$ P
int i; # _9 A0 N* Z- D( T) _) y- r" Y
if (np > 0){
! L0 H7 {9 W9 S5 f0 ~, K: l rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
' P) U! M7 j3 g$ O }else{
: @+ R5 n; L+ _) q& b0 [4 k rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ 3 ?, V4 u. Q( b; |6 c" u1 Q& p2 A
}
7 z8 O" ]5 s# G* D( v* S for(i=1; i
- ^+ y4 g9 j/ f1 ], K& ^ { + C( ], t8 `, F, R
if(vl.x < rc->min_x) rc->min_x = vl.x;
: \2 y" R9 H6 V/ { if(vl.y < rc->min_y) rc->min_y = vl.y;
. @: T& _( [2 t7 T& D! S; X if(vl.x > rc->max_x) rc->max_x = vl.x; 8 }) w* P3 m1 G$ C& T
if(vl.y > rc->max_y) rc->max_y = vl.y;
5 i0 G' A7 a( E7 R& W# k E+ e } / F: y. B3 ]# l
} 5 }' F n4 {& @, t# R
% \: ]1 x- a; g, Y2 y8 N
# Y# P0 r; ~1 r# ?4 o* k 当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
9 \4 Q; T. k5 i8 F& v8 n- ]) _. p" b+ @: Y4 m
具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:# h4 S& {- t+ i4 P. h
& a( `5 ?/ V" t) ^9 H% g
(1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;; |8 b" s8 T- m# i5 {- W
) z: l, ]4 o1 Y% E (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;4 t: J7 C/ ~) c- ^6 c* [- A
2 @2 q2 _& h- @- H0 ^9 K1 `
以下是引用片段:9 r" F) E9 w& \; x6 P: A
/* p, q is on the same of line l */
" r7 z: i8 U) J1 b static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ 3 K4 ^2 j u( R. N) ?& `
const vertex_t* p, 7 T8 A" g: i8 }$ D
const vertex_t* q) , A% X- ^" J. n; F
{
/ A; i1 S2 i c. f+ C& \+ f- u, C5 T: S double dx = l_end->x - l_start->x;
' ?. r. B+ M7 Z5 \1 F double dy = l_end->y - l_start->y;
5 W& ~1 ?$ c7 U double dx1= p->x - l_start->x;
$ g) e c5 _7 X# Z% V; W) F3 V double dy1= p->y - l_start->y;
# B2 O3 e% U6 F; n" H& b$ `* E double dx2= q->x - l_end->x; # u. L! m+ [! p
double dy2= q->y - l_end->y; / ^4 s/ V0 @* i7 m3 H3 {8 x! O
return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); 2 j# ? W- Z5 f+ I8 }/ r
} 3 P% c; P* v! K/ {
/* 2 line segments (s1, s2) are intersect? */ * y K6 V1 X7 u0 C) u1 @0 Y1 C
static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, - F# m; [7 u p' H3 b! q
const vertex_t* s2_start, const vertex_t* s2_end) ; m) X/ n; O& B. s O
{ ; r+ x: x5 a+ J6 @" v9 T4 ~# ~! i# v
return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && , w8 x$ E% J3 t3 z: @ i
is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
0 i; ~8 G0 z7 E- U }
" r& W9 Y* F$ O- f" h7 k8 v3 G# M8 k; W( ^$ q8 e. ~; l0 C6 n8 e. _
( ^) M, @( S, a) x 下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
' ` p8 Z. p% J3 w8 ^" h) J
/ F7 a9 T. f& t7 j( l1 v/ q以下是引用片段:8 [# c: Q8 q) F; U! c1 ]4 j
int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
! f# k& U$ G' E. W const vertex_t* v) 9 `) y2 i( Y0 F6 J2 j
{ + B8 r2 {+ k" _- x8 M g
int i, j, k1, k2, c;
4 ~0 V$ R+ N1 g/ G rect_t rc;
& ?1 S5 N& a" d; p$ H) Q' G vertex_t w;
. ~# S; J( ~$ D) A if (np < 3) ) k% n! m% \6 T+ r0 ^* Z
return 0; 8 c1 h( F3 e4 t
vertices_get_extent(vl, np, &rc);
( X! [( F) v: } if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) # w4 K8 S3 f1 n. E
return 0;
/ P/ i* f" M, e /* Set a horizontal beam l(*v, w) from v to the ultra right */
% K m4 W0 E3 p. {0 ? w.x = rc.max_x + DBL_EPSILON; - _) s7 u8 R5 x9 Q7 f# u {7 K6 h8 Y
w.y = v->y; / A9 W! I8 d# |9 H5 c
c = 0; /* Intersection points counter */
0 e, w9 P- ]" E) _ for(i=0; i
3 T1 q! {, o" ?' }3 `3 A {
$ n( A, V3 p2 _0 m j = (i+1) % np;
4 }2 h8 ?) Z" ?+ F' M" `5 h& V if(is_intersect(vl+i, vl+j, v, &w))
3 _7 c( r; m2 @+ H4 k9 Y {
! v3 v; D$ ]" e; L# ]2 j5 ~/ [ C++;
) G. l# X# ^* a, B6 ^3 S } , {( c0 P7 j" g' h8 a
else if(vl.y==w.y)
% s |5 N1 M$ n6 Q5 u { % v- W8 l1 M$ O7 ?
k1 = (np+i-1)%np; - \' h0 y" P9 v; i9 M3 }4 u" G0 N
while(k1!=i && vl[k1].y==w.y)
8 f9 t: G* p5 W- w( _6 V- k k1 = (np+k1-1)%np; 1 L, p; z i) W5 r) P9 @* p4 i- R
k2 = (i+1)%np;
* D/ ~, p( C8 t4 I1 E! v while(k2!=i && vl[k2].y==w.y) 2 b& Q1 ]7 Y/ Q
k2 = (k2+1)%np;
. q5 i! ~+ L8 G' U4 O if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
! i( \& q- D) K C++;
' l/ _" O: D; _: v" b7 j5 ` if(k2 <= i)
; D# x1 W Q3 W5 c0 ?% i7 c break;
6 t, ~9 ^; j p: L8 `& G7 b) R6 [ i = k2; # i0 ~8 |3 ]3 I2 e8 `1 q3 z
}
8 r% {) q4 }* m9 Q* e% R" w }
7 }0 a' J% F/ G- }$ _! Z5 P3 _ return c%2;
7 I' m* c2 S8 C5 J3 l( L } H0 [5 ^$ D, v( L( }
6 a0 a" n8 y! V0 Y2 _
& @% }5 ^ E: U$ o 本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|