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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。5 M8 E E) h% K, K8 K0 }
# i8 E4 y5 B8 R
这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。1 z3 g, k$ L" @5 k$ J+ p3 w
0 h- V( [/ U8 D" ]
首先定义点结构如下:9 A; B" u( a" I( A/ ?8 L+ q- l
! Y& W3 `; k r2 U
以下是引用片段:8 }; W2 z' X5 K1 f6 A7 ?% J
/* Vertex structure */ ' j7 R' K1 f6 N. b1 e. O9 R
typedef struct
5 ?1 N0 ^+ K6 |! N {
* J/ x9 e! Z9 R3 x- ~7 O' T- J double x, y; N9 N- ], f/ m' G8 g4 M) u
} vertex_t; 1 k$ l# l5 ?6 I0 o- v
4 A D. j! s, W% G. X/ U* j
: F+ m" W9 [7 n
本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:+ o# c( w( @3 {- W% U2 @
; f/ F/ O% p, B+ j' I
以下是引用片段:
1 U9 g4 P/ V3 a; h /* Vertex list structure – polygon */
8 w$ C8 k8 z9 U R5 m. |. C( G typedef struct
6 \# k) c% W* }/ M7 [) n { 2 D8 [% b0 i( {9 V- |
int num_vertices; /* Number of vertices in list */ ' S6 i' K {* P$ s7 t
vertex_t *vertex; /* Vertex array pointer */ 3 d$ l+ e& f/ _( V; V
} vertexlist_t;
3 o) Q8 `! V( B; n7 B# E: h8 d. Q& l2 l6 M
) }& @% u7 V* l3 n, d6 s* O8 o
为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
! H" f/ N W! y* `$ Q5 ?% @! S' J% ^ j: x6 u7 Y
以下是引用片段: c; G4 F$ D% n, o/ D
/* bounding rectangle type */ + C7 u1 M) k- `5 e
typedef struct
. J, n8 ?0 i$ }# y5 w, ?! L { - s+ i0 }; \9 b1 H5 y% v' a0 C
double min_x, min_y, max_x, max_y;
9 p! z* g! I. X+ b& e+ f } rect_t; 1 t4 H. ?# w9 Q* ^4 J9 C' ?, f
/* gets extent of vertices */ 9 J2 z- c4 z* }0 v8 n
void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ & z% u& o1 K9 K( b( b! a7 A* E
rect_t* rc /* out extent*/ ) 3 O% b) I ~6 a( |. [, \: }6 Y
{
4 t3 B2 `" [1 E+ v# d' u int i;
1 U2 H) \% v/ @, p if (np > 0){
: p( c# Y. o9 h3 B. F rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
! F" P; a! Y. l% U. D* F1 y! ^ }else{ k/ a+ n; f) T/ a$ {1 B
rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */ 0 B8 T; \+ J. a! d6 H
} % H; _# f% A j# N+ F0 u
for(i=1; i
2 ~, n/ h* L" N3 a2 f {
, p4 b! M( G: V3 p" R0 V* i' f if(vl.x < rc->min_x) rc->min_x = vl.x; 6 f" G$ h& f( y
if(vl.y < rc->min_y) rc->min_y = vl.y; 5 y7 _# [# J0 y7 b S2 Z
if(vl.x > rc->max_x) rc->max_x = vl.x;
+ v2 ^# C' M: H1 s* f if(vl.y > rc->max_y) rc->max_y = vl.y;
/ s, _! ]% t. h0 y6 B/ t }
% b& I% f( M6 L9 e }
6 C' M+ x9 l9 h1 R4 y8 y
+ j+ l; ^5 ~ D5 ?' X* G |/ z0 y/ ]# Y8 a
当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
6 _4 ]- U) u% ^2 r2 h( v9 y# k4 N* ~+ A Q) Z' d
具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:0 T5 C c% h: x0 q# l* z8 R
0 a# C% Y/ ^; u, q8 T! E (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
7 u* B% f! _, N. f' u0 i) f) \
2 x5 V- S7 e- d! Z; o (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
3 q! _5 |2 J) H& j3 b- M9 G- Q. x! g) G/ C8 Z
以下是引用片段:' ]; [: } z) ]/ B4 r i
/* p, q is on the same of line l */
% S* E$ Z$ o$ o ? static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
$ G0 k. [% q, L/ m ]" V1 z const vertex_t* p,
1 g: n3 k/ ~% k9 O- Z const vertex_t* q) 9 I z$ K# j: K
{ % O6 r3 j8 X) o `: P3 b7 U
double dx = l_end->x - l_start->x; . M. I8 D) T$ f j* y( }+ o
double dy = l_end->y - l_start->y; - S* T; C( J) q9 ]. m1 b0 S
double dx1= p->x - l_start->x; - ~5 W {5 |7 K3 d6 i
double dy1= p->y - l_start->y; 9 a5 e5 N1 K3 B+ R
double dx2= q->x - l_end->x; 5 M. W r! `- B4 B( a! p6 ]
double dy2= q->y - l_end->y;
1 ^- s4 \4 V2 |2 J' _4 V+ B4 x8 j7 M return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); # ^) b' A- @& Y% x2 ~
} 5 u, f4 I7 H( c' c4 R) u
/* 2 line segments (s1, s2) are intersect? */ 3 _1 ?: B' ~1 E3 N) |+ A4 w
static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
* a. z; p; y" y3 A2 e8 @ const vertex_t* s2_start, const vertex_t* s2_end) 7 k8 f& }7 O" t \( [% l( G
{
2 w; n, `' r" H return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && 9 w+ W& _* W0 f' Q0 I* P
is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; - O& h0 v& N% k2 o4 b* t
} , Y- b; b# d& V6 k
3 F, \, p( b/ H; e
, Q& q& ?! z3 C. h" i$ L2 b/ n4 P/ [ 下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
' `7 q4 g. q. k- y+ X1 S' i' ]! J: `4 _' f* L0 Y0 I i- f8 f! W; Z7 c/ a& l
以下是引用片段:7 m+ F! x% p- k
int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
, F5 e1 Q/ I4 D9 ~2 L: u' v4 j const vertex_t* v)
+ x! P5 B" c1 F' K, K7 ^5 c8 y; o2 s {
. O9 }" o4 I/ V- K, T5 t int i, j, k1, k2, c; 9 N: ] q% X0 C$ ~, i/ j) A, \
rect_t rc; ; y( U* o9 B+ g2 F1 {
vertex_t w;
2 d& V# D2 @7 t7 H2 W if (np < 3) ( |$ S' l6 l3 H2 U2 \+ x7 r
return 0; % C9 d" A5 E) p, h0 _) m9 j
vertices_get_extent(vl, np, &rc);
2 I, g, K9 u3 h6 n, e3 R4 k if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) . b% v v0 ~2 r5 M
return 0; 6 _0 Z5 |+ {3 W) ~2 G
/* Set a horizontal beam l(*v, w) from v to the ultra right */
6 p* F8 ^3 A) r w.x = rc.max_x + DBL_EPSILON; ! W5 ` t& m9 C/ n
w.y = v->y; ( d6 P/ e. w9 R0 C# p
c = 0; /* Intersection points counter */ : n3 x+ R4 a+ f+ h
for(i=0; i " B* P& \3 L/ ~7 Z$ {
{
) N2 T x1 d# d( W+ Q j = (i+1) % np; ; n7 n# a. c4 _# t) [; F0 c
if(is_intersect(vl+i, vl+j, v, &w))
% n: F* T0 J; D% l { 5 w( R# Q% r5 \$ f
C++; # W' x. P1 U! v" q G& f/ }5 k0 | ~$ C
}
5 X! r8 F# L7 [' b2 U else if(vl.y==w.y)
( M9 Z6 w4 g! H& {. T { 7 p- }4 N1 B4 N. S: q) A6 {
k1 = (np+i-1)%np;
l2 `2 Q9 P! k5 F# Y& G while(k1!=i && vl[k1].y==w.y)
) ^# T* x9 u# F( u k1 = (np+k1-1)%np;
8 b2 {, B0 ]( O. ^8 N0 M+ D k2 = (i+1)%np; ; h' ?0 o" \1 ]" r: {% w
while(k2!=i && vl[k2].y==w.y) # m8 z/ A5 A1 I$ f8 K, y( R
k2 = (k2+1)%np; 9 T! C8 W! _2 Q* x. }
if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
; `/ S x( f( w2 F: P( N C++; 6 T# M, }7 f- T) d
if(k2 <= i)
. I- E2 h) ~2 [- w break; : o& _1 V0 t0 a
i = k2; $ i( K& t1 |5 C' N R
}
8 I) o$ n: S7 ~# p }
1 h" ^5 H0 E* z return c%2;
8 ]7 ]/ Z, N, U( h9 K7 I* O/ p9 b' o }
; D( D: P8 ? P: K6 { t E' A8 H" ^4 o9 e9 t
( E* g1 \2 K; E4 Z0 E" \ 本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|