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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
( t' U+ |* q6 B1 H$ H# s2 y* A6 ?( Y1 k3 W/ j1 B
这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
. K7 s) I# W3 T, s3 z; _0 ], J0 a, k. m/ ^& f& ^
首先定义点结构如下:
' Z0 D6 X H( K, ~
0 q' ^* t3 d7 H. d以下是引用片段:
9 C3 q8 ]$ y; |! x /* Vertex structure */
; y) g, K1 X$ Q, g5 f typedef struct 8 u7 r ]' @) O5 R( @" j* F
{
) \3 r( E7 @% p double x, y; ' i' i) X8 @0 l% w. [: s
} vertex_t;
4 s9 H2 f' j; M; [8 B! s$ x5 ^
$ Y, |) N# f& v0 u; C2 ^ E5 H0 [ J# Q" `* F8 g& Y( Y: W' Q. d/ @
本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
3 _/ E/ f* A* E+ H/ a* V) L1 \
以下是引用片段:& n, v5 f0 d$ d+ N5 h7 N
/* Vertex list structure – polygon */ 0 T7 T# q3 ~+ x! T! W
typedef struct / x/ G7 Y, |, a1 G7 P0 T9 h; \
{
; c- v. q. q& I5 Q$ T8 \7 U7 w int num_vertices; /* Number of vertices in list */ s* d+ c8 r2 L
vertex_t *vertex; /* Vertex array pointer */ , p. ^" l w/ J- i
} vertexlist_t; & y3 M" U: f H/ |+ \" O2 s
/ {3 [7 |3 U+ f
& D: k# j7 ]5 B* n5 x0 {
为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:$ M4 ]- h3 w, X/ `& }9 Y8 v7 x
$ T& h# s& K1 T以下是引用片段:( ~ Z a C U1 G& {- y- x# J
/* bounding rectangle type */
, U; b" {/ ?" j. {1 b typedef struct
5 W' ^+ |# X' K% F { , K" g. }8 g( T# Y* ~0 z: b% M& |
double min_x, min_y, max_x, max_y;
- |1 k5 F' C* K } rect_t; + @+ ~: n) t' A% M4 M+ q
/* gets extent of vertices */
$ b7 l5 y* f( b9 X3 s! R void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ 9 n! u s0 ?# C# u; @3 Q2 Z
rect_t* rc /* out extent*/ ) ; h7 Z l, U# z: k1 C
{
* {+ J! F. n R( b" X: G9 V int i;
" `; A0 Q( c8 h \6 ^2 X if (np > 0){
' i4 z) A& A8 m0 u rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
+ t# `. z8 e: J }else{
% u6 c+ t1 s! z, s* u rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ 7 i" z2 v" U2 Q6 }5 G/ B
}
' b' u. L: U9 f+ y for(i=1; i - @+ N+ d9 F7 w5 z
{
+ e* p+ P2 x- u* m2 M; Z if(vl.x < rc->min_x) rc->min_x = vl.x;
$ ^* D/ m" o$ B7 { if(vl.y < rc->min_y) rc->min_y = vl.y; . y/ Q8 i2 P! A
if(vl.x > rc->max_x) rc->max_x = vl.x; 8 l( U) M: E: y5 p5 C( |$ i. Z$ l
if(vl.y > rc->max_y) rc->max_y = vl.y; 7 P+ S5 M3 X5 r6 @0 l
} 0 E/ P* ?. q0 y2 b
} ; ~. ^+ Y, V' q& l9 O3 Z. ~: k2 i
8 K: q7 L T) j5 B v: O+ c. |2 r
3 \- \9 P5 C4 q
当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
# S3 t" o5 Q: n8 D
* q; n1 r0 P, `. K6 d; l$ J% Y R. I 具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
; ~( K+ Q) W q: c
3 ]3 }8 ]+ L* k/ V2 ]- ?0 U (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
$ G* O6 p4 @" L% z: L$ V' U
2 R8 Z: ^; f" ]8 o) O (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;; ^' m( }* g2 {3 K3 k$ X
9 L3 A" q' S# ]) b) Z& ~- ]以下是引用片段:
* ?' v* S$ x) I8 ]$ o0 T /* p, q is on the same of line l */
7 N" S( s/ A: w/ } static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ 2 F. [- T8 `* H
const vertex_t* p,
4 ?! r7 V C+ z6 b/ u# l const vertex_t* q)
+ z! @9 c9 c) P' M1 R' [2 b {
4 n# f3 T" b5 c* _% k/ K! P double dx = l_end->x - l_start->x;
, @- g0 X* W2 j# S" Y- K' Z3 w double dy = l_end->y - l_start->y;
# k( F0 H& Y# X double dx1= p->x - l_start->x; 6 R+ B: x8 l: w& q& _2 [8 y) w
double dy1= p->y - l_start->y; ' `) s `3 Y5 S% Z: b6 o
double dx2= q->x - l_end->x;
1 c' v; q$ {3 R, ]5 p double dy2= q->y - l_end->y;
" }( |1 W" c6 Q return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
; F/ [4 W/ b* f } & v, Y# C2 `% @- Z# i3 D6 m% {
/* 2 line segments (s1, s2) are intersect? */ 6 H/ u& i8 z. i" j6 X
static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
3 w# p, Q4 Y) ~; h6 b const vertex_t* s2_start, const vertex_t* s2_end)
# h P! a) @! N& ? {
; `. L: L$ J8 W4 H* D5 Y3 [ return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && ; T% G9 b' H% A, \
is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
/ X$ F% b4 m- A3 i- x }
/ z( I: H4 ~0 m4 @* I2 R$ j3 ^* O# c, K1 G
' x# J( B( s$ l- l, D+ n4 w" K9 ? 下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
3 ~. j5 p. P+ a8 ~! f7 s9 F2 I* N0 |4 s7 k0 ]3 ]
以下是引用片段:
" C4 I' X& R* T4 x int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ 6 g$ |: q8 S: [, m' L& m' [
const vertex_t* v) . W/ m) E" m: U' C- K3 H% I0 e, b9 Y
{ 4 n1 M1 o/ b% e1 k! n; A9 |9 O
int i, j, k1, k2, c; , R1 m$ {: q$ x) b, x# K; ~* X! x, Y
rect_t rc; ; Y$ P7 |5 u$ m4 h$ K+ h
vertex_t w;
* Q! Z7 Z# k' c: d& ]% Z if (np < 3) ! T5 a+ r. w1 s; X5 p
return 0;
2 ~& p# A) i; t$ c vertices_get_extent(vl, np, &rc);
. f' I% B9 Y, ~5 b/ X& B: s8 Q$ _% ? if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) ; x( w& q/ T& `5 P- u. h
return 0; # @& u. r5 d0 b/ J9 z( U7 u
/* Set a horizontal beam l(*v, w) from v to the ultra right */
, k; i2 H9 u+ h" C$ x9 ?/ x8 N3 z w.x = rc.max_x + DBL_EPSILON; , z8 W2 ^1 \7 W1 V
w.y = v->y; ! O2 s- R3 b0 c/ k7 | Q$ q6 D: r
c = 0; /* Intersection points counter */
* y0 F2 o, L M% I+ Q4 Q2 x" X: i$ s for(i=0; i 9 e# l2 `- e1 `" T7 G( G% M
{
- ?+ M3 U: j# L/ l j = (i+1) % np; . A$ n: b& Y+ ?
if(is_intersect(vl+i, vl+j, v, &w))
5 k9 O( C0 d3 |! J# J8 J {
8 q% e, u& Z" I- R3 ]: J C++; 0 ^/ [6 j6 ~! ^2 {5 }
}
( i( q& k, @- |/ J" A" L% P else if(vl.y==w.y) 5 e: d9 [% k ?6 O
{
+ ~- v& Z# s" l4 { k1 = (np+i-1)%np;
1 b; z* z/ N! k2 E' O# {1 P; D' \9 ? while(k1!=i && vl[k1].y==w.y) 9 l9 v- x( i% \( x9 i" X2 z, y
k1 = (np+k1-1)%np; / Y- H1 B ~: E3 d
k2 = (i+1)%np;
0 m1 ^- e0 }: v L0 [+ |, W6 B while(k2!=i && vl[k2].y==w.y) $ ^7 V1 P! N9 z+ F# l/ K
k2 = (k2+1)%np;
. A+ _! }$ G( n! V if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) $ Z) X( q9 Q2 [- x: o; }
C++;
/ M' t) U; C- g- _- f if(k2 <= i) 2 k9 e9 g; P* J( z
break; ( r, g% |! j$ X( g& x* X
i = k2;
3 w; ^1 B. R8 @# R. n1 E }
M, g' m8 ?6 e" g$ l m } % o( h( l0 c+ Z, w! D
return c%2; # L4 |' S* S8 S& t! f6 {/ p
}
& e; P( r4 Z8 X+ v$ y1 H/ U# i8 k9 m
D% i# R `+ Y9 Q 本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|