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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。0 X8 ^/ ]$ J" b% j2 i! ]% b4 m
4 f' r) R5 l2 Y3 ~: a 这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
' O, v X' D, i$ v
7 P/ t8 V0 x3 P" S# e* ^5 N 首先定义点结构如下:/ I+ z6 c4 G# B* Q% e( ~6 ]0 v4 z
- E0 N$ B$ F7 e/ u Q9 Z以下是引用片段:
+ f* u9 A! {# ?: r) g* d) |. d /* Vertex structure */ 2 O+ M; ?! j* \3 Z) I( I
typedef struct
$ M) c, A$ |1 u" O/ n, r {
: Y r5 z4 h; {$ I double x, y;
7 t T! h# G. o% o2 w2 Z9 @ } vertex_t; ' U% \( m5 G$ V, ^
7 p$ O- v' W: k, J8 A5 }8 F: D$ B
本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
( A* n2 G- }& b7 Q! Y: g# g9 D: [) F% i7 s t4 C/ m X+ `
以下是引用片段:; }% |8 x4 q5 O c) n% {
/* Vertex list structure – polygon */ ( J* H3 M2 q/ }" z3 x# A
typedef struct ( _$ `7 K8 L* q2 k: l
{
0 t) u. f, T! T2 ~! d4 U* m int num_vertices; /* Number of vertices in list */
2 |% R0 t7 x* _: l, z; R7 e vertex_t *vertex; /* Vertex array pointer */
! @. t9 C9 r- G5 } L/ E } vertexlist_t; 6 U! W9 s }) D4 i! H0 w [6 d! o0 a
* d& @% |/ X( x
9 D9 r g( n' z
为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
2 _5 o8 C7 ^5 j3 K. R' n8 W+ V3 o8 \' G' T
以下是引用片段:
+ ~: c( t9 B) M6 n; U5 b /* bounding rectangle type */
, W% A4 @4 Q4 Q, m8 b typedef struct
7 Z, H6 r. R6 X( E) }" ] { 3 A. Q: R: k3 K' u# E$ I
double min_x, min_y, max_x, max_y; 8 D. \3 o1 J4 y: a" Z
} rect_t;
/ i' O$ N- n0 j. z& ^& i# i /* gets extent of vertices */ 9 B. L: G- u4 ]0 w# g0 v D
void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
' \9 ~5 S3 @& U; f* E+ H& s# y rect_t* rc /* out extent*/ ) ; B8 e; c& V! _9 v+ R
{ $ b4 K& S o' E
int i;
) X. J. j# R; @7 | if (np > 0){
' C6 b% D& t H# t' Z rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; : ~9 v. V( I4 ]: |6 ?- {$ c* a
}else{ ) P+ y4 m9 T" \+ f
rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
0 l w6 t9 S6 D I! I& x A } ( X2 ?6 b7 N+ K8 q1 k0 d
for(i=1; i & l4 A5 |' V, q/ Q9 S: @
{ % t) E# R% B f0 R
if(vl.x < rc->min_x) rc->min_x = vl.x;
6 v8 n: y- |" K* i# ? if(vl.y < rc->min_y) rc->min_y = vl.y;
( c; w" o- k9 T( ]0 n8 h6 Z( E if(vl.x > rc->max_x) rc->max_x = vl.x;
( z9 R6 S: g; @+ V6 f I7 q if(vl.y > rc->max_y) rc->max_y = vl.y;
$ J" x! y+ l' p }
l0 M8 C& N$ y4 a }
6 y5 I8 J2 e0 X3 R6 T; H
- K1 u, ?9 b% o1 [& b& f6 q( g; S) e( u/ D3 b8 W H
当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。" S- J5 k: G4 |/ L4 m2 V3 V' \
1 o T2 n* k0 R) `- b) e {$ P6 W 具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:: h, e% N0 @1 z: V: M. t
/ ~3 N9 V! M$ C# u' n" i. T9 W6 Y: x (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;$ T4 R- T5 g% `. n9 m+ a) i
" U P; @( V3 N: b* O! C+ s! e
(2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
1 p% \3 @. A; P3 T! d H* X& p9 a$ q9 |$ k6 F# T$ }* L+ I
以下是引用片段:$ h, a0 e* r, `% H& x& E; R
/* p, q is on the same of line l */
% @ x+ U* R- J" S static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ $ z/ }+ x% Y8 ]9 Z
const vertex_t* p,
) t1 L" B( H/ [- t+ A8 d const vertex_t* q)
( v3 o1 T+ ^# @. k {
4 s# h- k0 Z1 M5 g* D M double dx = l_end->x - l_start->x;
$ c L! F& E1 j double dy = l_end->y - l_start->y;
9 } J: M# [0 i! @) [" }6 h double dx1= p->x - l_start->x;
& g# } H. U. r- Q5 K6 y double dy1= p->y - l_start->y; 4 g7 [3 `( P3 b" i1 q! \ u) ]
double dx2= q->x - l_end->x; 8 G8 a! }. X* L8 P' c0 b
double dy2= q->y - l_end->y;
# Q6 y* W, p/ v4 A. R" L2 e return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); / b0 t' `) X7 z* |/ @ _
}
" [& K: I2 K& Y+ C) }8 i5 _ /* 2 line segments (s1, s2) are intersect? */
. k- ~. T$ k" r: D: s: g/ S static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, 2 T) M$ l6 d% R2 {8 v" U
const vertex_t* s2_start, const vertex_t* s2_end)
3 ?. }, Q% t! I8 E( j T0 \ { ) H- U4 L4 S$ U& \5 N" [8 W4 a
return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
4 H: ^" [6 {: W6 i5 _# O. r1 _ is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
. I$ v, P, Z1 G0 O4 G7 } }
: t! ]8 e: |. o* p1 b3 e2 c- T
, a: T- V3 h4 q7 _: i3 h* J
% x6 B! L% c# `8 h1 L/ \# r 下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
$ ?! P) j( [( ?# A5 z! ^- g; q% v/ |. v, l# H, ]/ j
以下是引用片段:
6 V: l6 f# `/ M. F6 O/ T% `5 M int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ 9 z2 u8 v5 k% H4 g' b# h2 U& `7 e
const vertex_t* v)
. h. U+ B( c# K% J {
( d9 J6 u" `1 g4 X/ \ {( `$ } int i, j, k1, k2, c; , K0 h. `2 [; H' _; a1 Q
rect_t rc; $ G% s+ N4 q+ e3 {) ~* c
vertex_t w; 0 Y1 \+ L/ {3 W$ J" Z) Q
if (np < 3) 3 e# v1 F( |& k' K( b
return 0; 9 O9 q; v3 t1 n" J8 ?* Q$ e! p" r
vertices_get_extent(vl, np, &rc); 2 ^5 I$ P, r* W
if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) 5 k. Z6 B7 A) b/ n1 f m
return 0;
: L) }, H* m$ V) P( `; D# K. d$ [ /* Set a horizontal beam l(*v, w) from v to the ultra right */ , |* w$ w& u' G# H
w.x = rc.max_x + DBL_EPSILON;
4 ^" N; H9 ^% k0 x! I1 w( q# |7 l9 f w.y = v->y;
p* m/ s! a* T9 L5 U' G c = 0; /* Intersection points counter */ % {0 i4 G, A, l8 H S8 L9 ^8 Z, R
for(i=0; i
+ A+ [; g1 [8 ^2 d1 h8 }# j {
) q+ s* |: i, v- K+ I j = (i+1) % np; 5 N5 z" j' p; \! T, z" O
if(is_intersect(vl+i, vl+j, v, &w)) & g/ E \; G5 N( M
{
( M( v: X* D0 @0 L" h3 I, o C++;
4 |- c9 f7 l, x# R, f } ! ^9 z& J" k7 b! q
else if(vl.y==w.y)
- y4 a" q' M; x' j {
0 f. O$ C# m) \6 s1 V Q- H O k1 = (np+i-1)%np; ) w1 {! e: ~6 ~1 f% z) y
while(k1!=i && vl[k1].y==w.y) ' H6 B3 \, Q) |; c1 I. w1 {3 L. q3 H
k1 = (np+k1-1)%np;
% A: W Y# \# _2 [0 P( b k2 = (i+1)%np;
8 ^0 H; g, Y* c3 {, |- R while(k2!=i && vl[k2].y==w.y) ! P4 N9 [7 c$ e! X; s% `
k2 = (k2+1)%np; / L! K! [ ~+ I8 e) v1 R
if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
- F. z! A6 j; l6 e7 G C++; ; n8 l0 q7 s7 Q) i# O& t
if(k2 <= i) : ?! m& T& { P* O0 q( @- C
break; 8 q/ R1 Q/ t1 }! `7 |
i = k2;
; s9 k, o" W/ g } ) a- T8 Y6 g$ k: E
}
9 N, ~! E/ j4 a8 U# r return c%2;
- a+ O/ Z" q: S% G# e& M# ~- x } & O0 F$ }% r3 J1 n) s
0 g' n; _9 o2 N8 \7 X: O: f
+ g& f/ A' u+ V( B 本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|