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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
5 O! ?2 C5 K% b5 {) ?# G' ?5 a' \( y
6 C' d- R: U! Q) N: x2 z' a- s* n 这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
) i! N! Q1 W$ g) v3 [( b3 S% T4 `1 [: k h+ [# X7 s4 a
首先定义点结构如下:" D) H: O4 v7 j4 o8 t# c
i& y5 H: C/ V3 d* f) p7 i; x! L
以下是引用片段:' |( O8 ~% m. z- y1 d/ l- R6 M
/* Vertex structure */
; \ k6 x1 N: c% x- k9 h typedef struct
: ~# O8 A* \7 K5 J. m* V* d9 `7 s; g {
3 g3 d* U$ T% l( ?9 L% U6 Y2 H9 d double x, y; & P! \& @ U1 |7 @/ {0 v; m
} vertex_t; 9 l6 T: u) t% d# V. s( J! }
* k# I7 w& a& R G! F/ k
/ T5 S9 J0 K5 |- r: `5 Y$ ~" T& G 本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
5 q$ v; L1 o9 q9 E7 Q- p, f, U4 Q4 d
以下是引用片段:: W- L8 Q( Q# Y8 z2 @1 F
/* Vertex list structure – polygon */ ' w* P3 m% A" q, D+ w
typedef struct . L; J3 n2 K! _* m
{ 6 _2 p: e9 q' v9 V T, E- i. B& q
int num_vertices; /* Number of vertices in list */
+ I3 ? l, `! f( V1 l1 i! x vertex_t *vertex; /* Vertex array pointer */
; z0 I W1 s+ q- ?4 b } vertexlist_t; 2 y3 G7 [7 p$ z* F) T5 k6 r% r
/ m! f; M, t! f7 \. a* F
: |: n4 ^5 ~0 B& H, \/ F& ~! u 为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
0 J. V0 }" K3 P: N
9 W2 l; A7 U* i: W* a8 ?6 e以下是引用片段:; |" d. u! M! Y( j0 d, _
/* bounding rectangle type */ # C0 A6 s; I7 r7 {! Q
typedef struct
$ y: w8 E7 e u, t0 a" r, B { & i; I6 w/ `, H! z
double min_x, min_y, max_x, max_y; . u+ L& G. i/ x$ d
} rect_t;
% s, ] n0 ?7 \5 \7 U5 m /* gets extent of vertices */
! U( Y/ A& ^5 u) S5 `. z, n void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
& Z4 i) Y: p0 y& h0 b. ~ rect_t* rc /* out extent*/ )
6 w/ i/ K4 Y: W( ]' _9 p {
' n" t# k) W+ j0 @# A( O int i; , s/ S7 h2 F7 e. Q
if (np > 0){
& J: G. {$ X( S- N2 p rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; # Y% o: i& {# ~4 t
}else{ * B0 N6 c% [8 d9 Q2 j
rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ & _# q9 _# p7 K5 Z% L0 M! H V; n% o
}
/ b, e% D6 Q$ u6 P for(i=1; i 7 R( s3 Q; q8 w" H7 {* }4 l
{ : }# I7 L7 C7 m: S) w$ l( [
if(vl.x < rc->min_x) rc->min_x = vl.x;
$ `' E3 Y3 n% g if(vl.y < rc->min_y) rc->min_y = vl.y; - C" U" A7 Q4 ~% `$ a" r2 P
if(vl.x > rc->max_x) rc->max_x = vl.x;
* }: o5 k$ v2 U# U* g3 V; c! j if(vl.y > rc->max_y) rc->max_y = vl.y; 6 V* W2 S9 i, d% C4 i
} + i6 h! K2 P r8 K" t3 r, d
} ' }* E7 x( c7 d' T1 h9 e9 k0 o S
1 q( N1 _6 D4 f! S4 V
9 |( B- G6 t! a2 R1 C0 E 当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。! F8 d) x6 Y& P ?
8 l# z, e; D2 j* q9 c
具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:, G3 Y& L! @8 o
. Q6 m, a; m8 l/ d
(1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;( \7 u, u/ P7 {
9 P# E% {7 y2 q# `( L8 b$ k/ T (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;* N7 u+ `3 m J* i7 z/ ^$ T6 }
0 R! o! ^: m- H以下是引用片段:. K7 e. `$ \1 d# C7 p+ \1 y, p
/* p, q is on the same of line l */ - [ ~4 Y4 C: L- G
static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
0 J+ i/ J k6 V7 U* C3 u const vertex_t* p, ' D& [6 A+ C$ o* N. O; l1 E
const vertex_t* q)
7 f: I% R0 y" ~; f" d+ }$ {1 V { " K' T" y& ?, e1 L3 T
double dx = l_end->x - l_start->x;
# D% s+ j; i- z6 n, j4 C- v double dy = l_end->y - l_start->y;
$ l% i, J+ h% N% K* U, u double dx1= p->x - l_start->x; % q' f6 x8 ]5 U# o5 R* S1 ]
double dy1= p->y - l_start->y;
2 c$ g* Y. ]/ A" G( [ double dx2= q->x - l_end->x;
0 `$ ~, t L) U- Z! u2 c' ]5 } double dy2= q->y - l_end->y;
# z1 U6 _* j" t' F return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); % a' X' n3 s$ P1 H
}
; k' D/ q- q8 Y% v8 W /* 2 line segments (s1, s2) are intersect? */ 7 e. T" \) H- H
static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
9 V F: ~* I u( S7 T* B5 X const vertex_t* s2_start, const vertex_t* s2_end)
9 R* I& Y+ X) l7 L! U { 0 D I6 Y3 K# \. P% G! S
return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && 1 O& g6 A2 d; E
is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; - E6 d9 r' H9 i: I! a
} % x/ l2 M" q O% ~
6 l: ^9 t! E: b5 n- R
0 Z* h7 f K- i, L V 下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
4 W: r7 C2 ^% p! R- `! ?; a/ y, w& C& {
以下是引用片段:
# a" g9 F6 X9 ?) D; i int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
" ~: l G/ Y. h$ x/ k: C! K const vertex_t* v) 5 f; N+ a- {% H' ~, U9 j$ g
{
% C V- G+ i/ V1 i int i, j, k1, k2, c; 5 w$ Y/ s: Y7 X4 M# B) n
rect_t rc; 4 Z- {( ?% [, @$ O- i. n
vertex_t w;
7 D6 E8 b# ?( [5 Y4 ` if (np < 3) 9 ?, y3 G, \2 |3 r9 P" g
return 0; 2 o: K" d2 U+ V! K
vertices_get_extent(vl, np, &rc);
1 n5 `9 }! g4 A if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
# f- q9 ~' w l9 }& r return 0;
0 x2 C0 }6 t# b$ K /* Set a horizontal beam l(*v, w) from v to the ultra right */ 9 P1 h- \# G ^3 d. R$ z4 H4 ^
w.x = rc.max_x + DBL_EPSILON;
! W8 x5 J$ `$ |" l; b$ @* }+ ~ w.y = v->y;
# a0 ~4 E& p' m% ~+ E' J" c2 n c = 0; /* Intersection points counter */ ' t) h: x& B* Z2 d1 `
for(i=0; i 7 F% X: ]' o7 y. h
{ 9 @5 A9 \& l8 _& H% G' ~
j = (i+1) % np;
4 d: e% e t; Z2 i# f9 l" j/ { if(is_intersect(vl+i, vl+j, v, &w)) ' {) V' d/ g" ]( t+ I
{ / J. V4 P) Q: ~+ |) M
C++; Z% E* j7 w6 A: I( u/ {- k0 _! k
}
; L/ c( a W L) v; ^" g else if(vl.y==w.y) 0 d e- b& `0 B3 z+ ^3 b2 {
{
! c0 T/ _' p# P/ B+ t# U4 p k1 = (np+i-1)%np;
3 U0 Q( ^! _; R/ q- X. Q! l5 e while(k1!=i && vl[k1].y==w.y) V+ H( z( _3 G" [. S
k1 = (np+k1-1)%np; . X. L) a; O0 c/ T4 C ~1 A6 g
k2 = (i+1)%np; % N5 Q4 W* A% S* `1 T& C' Q( ^
while(k2!=i && vl[k2].y==w.y) X( z+ i! D3 y$ N: Q/ ]9 R, Q7 y
k2 = (k2+1)%np;
) H8 B# g5 e9 b+ i Y ~7 B+ L1 Q if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) ( @0 K$ Q# j, ]) @7 Q4 _
C++; 7 t+ w, Z: p- |% o0 D
if(k2 <= i) : O+ w6 O- ?2 k
break; * c( m' v3 r2 }8 `) I6 i- Q* R
i = k2; : O; u9 S$ N* C+ C" M1 X- ~) l* k8 \
} 6 z/ m, u7 Y- `0 o
}
X* U2 u o" a2 g. {, V8 a, _ return c%2;
0 k( p' g2 q4 t3 J$ U } ( B ]: Y1 b" }3 c) E
# x, N# i, E) {) b% }8 R9 g
3 y$ T) W, b( P' O- n
本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|