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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。+ t, h% |* S" f' W0 a/ l0 D6 T6 j
; g ^) ^1 f" V3 Y' G0 Y/ @ 这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
4 T9 ]1 q7 w& ?3 J l. C3 W
1 H6 Z$ f, Y, M; s% m 首先定义点结构如下:' J- b9 ?! S6 b' i0 y( A K6 o
8 J& g4 V) J1 N4 |5 g: y
以下是引用片段:
* n5 A! X6 l6 P5 V9 k /* Vertex structure */ $ k- |3 g2 F% M
typedef struct - Y# {1 [" E# I& ^6 [1 m" M9 \
{ $ ]! ^- q& N# y, L5 Z" ?6 X6 h: y
double x, y; 3 j6 A' S1 ?) p* u' I* w6 M
} vertex_t;
; B' ^$ P6 Z4 {: K, B5 M% g3 l, ^$ Q. ]8 U5 l8 N9 P1 K1 g5 ?, h- j
0 y2 Z1 X7 R) ]" L# M! g
本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:$ Y$ G) Z0 u) i! u
2 \* `; c8 k" ^! z; ~, Q
以下是引用片段:
- u. U: _& r" |7 r& h8 d. q4 h* _ /* Vertex list structure – polygon */ - J! Y" C9 `" l4 F9 ]4 w% q
typedef struct 3 f9 y+ M2 X" t+ I/ B
{ " g, Y% c( \: a/ @
int num_vertices; /* Number of vertices in list */
* @+ ]2 J9 g7 s vertex_t *vertex; /* Vertex array pointer */
2 A; Y5 \2 u" p: \( z2 ` } vertexlist_t;
/ Z$ b; ^$ [: M6 X @2 b
' K' C2 T5 {. d$ t. T* }0 ~7 \$ O3 O( V; r
为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
6 s& ~* `5 k" j0 j
7 G" [( a, S( }1 B d, n; a! d以下是引用片段:
) v" c; `! a/ \2 M /* bounding rectangle type */ 8 s; B# C& J* o* ~* \
typedef struct
) Q* ]/ f$ g) t- U4 `9 \ {
) I) B3 n9 f8 j6 H- | X: R" I double min_x, min_y, max_x, max_y; 6 V, p9 N% v; y! X
} rect_t; w1 z* ?; o1 W/ N v# a6 D% C, V% C8 Q$ m
/* gets extent of vertices */
: }$ L9 D o2 _- W0 | void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ $ Z( C* N1 G/ a" y c
rect_t* rc /* out extent*/ ) 7 ?# R f, s$ Y1 y) m- ?$ z1 ^, Z
{
: {9 c8 h6 s y2 o1 E int i; ( x1 o8 c" D9 C8 s1 r
if (np > 0){ * q v; Y F( d! d* n" ?+ o2 a
rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; ( B, O& Z+ c; L: r' n" ^
}else{
% `( E$ Y! S- W/ U rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
) w4 v9 n/ R' a# c2 M# B8 t } z. V8 g6 B+ F+ G$ M
for(i=1; i / a+ M( W9 `; F0 m G* y, z. m
{
8 w% w8 ~9 w7 n: J- @. I if(vl.x < rc->min_x) rc->min_x = vl.x; + t& A: H5 K- r# c/ O" A
if(vl.y < rc->min_y) rc->min_y = vl.y;
# z& @) r- r! q/ y2 s: v if(vl.x > rc->max_x) rc->max_x = vl.x; 3 C+ r9 v4 V& ?( @# B0 C. ]9 C
if(vl.y > rc->max_y) rc->max_y = vl.y;
3 ^4 z; ^2 K4 j0 R5 E2 I+ v3 s } 6 c* H# q/ d7 @, x1 V% x( G3 Q9 z
}
1 o }2 t* Z9 }, Y3 P/ j7 T0 T
! C# r7 {* A0 ]% t! }6 m
C0 e, F \* Q" c# t 当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
( y3 t8 P6 |2 d% @. E
! V: u- @3 b' o* V& I, s 具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:+ Y' b# { O1 r
3 {! P8 `* S$ e: ~) e: C' b (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
' T/ b/ o: w1 l4 L/ J y4 l" ^+ ]$ w; Q) {* y
(2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;! N2 T* V P3 c, E* D+ _3 J
! r0 l: h N: \7 R7 P! b以下是引用片段:6 R+ C* ?& @4 R6 R) d& v9 l3 Y- R. I
/* p, q is on the same of line l */
# G/ _$ e, H% j9 \5 N static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ - f8 W2 _" S7 v" h% e" i+ T3 W1 P
const vertex_t* p,
* E) o o3 |' ]8 P' R1 }; Q5 U1 H const vertex_t* q) % p& |0 R! l& C5 ?/ z/ H$ Q
{ # g9 F3 O- I, i2 d2 z
double dx = l_end->x - l_start->x; ' B" J- ?* |! {' u
double dy = l_end->y - l_start->y;
Q j8 D+ o; \' ^' _ double dx1= p->x - l_start->x;
; [# T$ {# g- l& b double dy1= p->y - l_start->y;
c. _1 {6 i$ d2 N double dx2= q->x - l_end->x; & p, c8 D% ]2 Q
double dy2= q->y - l_end->y;
4 {- ~* n/ m6 B) w W4 ]# Q return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
* B# S2 z& f: o- T, T( s7 T6 {# ~; o5 } } ! _! s9 T) J% z/ o, p0 `. N U
/* 2 line segments (s1, s2) are intersect? */ 4 N P5 X9 Q2 V3 G' k1 V* h
static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
5 {5 u* K; r$ P# m' N( o+ p2 v& s, U const vertex_t* s2_start, const vertex_t* s2_end) 4 |7 R+ ^2 Z# `( x
{ 8 F$ i% _: I6 g
return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
2 J! H* z/ @$ O8 b: l( z [ is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
; F& z# r9 `1 ^; r }
+ U5 i; r* s# |: q% Z
$ u6 e( I* P, p# ]
3 X) [1 o7 A5 `$ o% j% A( R- ] 下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
# x2 k* D" z1 W) D6 |
. a( P% ]9 g9 K% K- M以下是引用片段:
8 ?3 ^* o; c/ L4 |, m! M5 M4 M4 p6 g int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
7 r: m7 D3 v8 i const vertex_t* v)
3 ~! x" C4 @% k t2 \! L3 S {
3 D% ?6 L, k% X9 }3 X7 b; p int i, j, k1, k2, c;
- H8 C' t' l3 N% x W" S' S. @ rect_t rc; 3 {! P* v3 J1 p3 Z z
vertex_t w; ! `1 r( u; [ M' f8 g
if (np < 3)
, ]' G2 p7 K# q return 0;
1 y0 @' r: @. @( n4 g vertices_get_extent(vl, np, &rc); 2 J* G) n" z' ]& z4 `6 |
if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
* F) y$ z! R7 A6 ~- r! ^8 x# z8 T return 0;
% k8 J' I, h! N$ W' n5 e /* Set a horizontal beam l(*v, w) from v to the ultra right */ ) u% R3 Q4 K9 k7 K% R" M7 J
w.x = rc.max_x + DBL_EPSILON;
7 X( ^6 z7 b! u: `. M0 k1 b5 X! ? w.y = v->y;
; {6 x1 [+ j# {2 W, e1 j% k# g% A c = 0; /* Intersection points counter */
: o, B" B2 b8 M$ z# V7 K! g. {. m for(i=0; i / E4 I6 j/ ?' r" O4 A( x# [2 k
{
# I2 W2 s# m0 V$ p. L" x j = (i+1) % np;
7 \+ X" g# u" h5 n' y% M& x0 y; L if(is_intersect(vl+i, vl+j, v, &w))
/ a& k2 U) ]- e" ]. @) @ {
9 h- @, l) _2 h" y; g# J) N C++;
2 _7 C! e6 U* f } % n( @/ L- A' [* q1 B+ w
else if(vl.y==w.y) {3 X1 ]3 q4 Z0 b- u
{ ( f. ^: }. C9 ^1 r. h
k1 = (np+i-1)%np;
& c# T9 F' |7 q/ r while(k1!=i && vl[k1].y==w.y) 0 r. C4 p2 A t9 |3 l- r
k1 = (np+k1-1)%np;
" Y/ @6 U, K" W; n! {( A7 _5 Q k2 = (i+1)%np; $ `4 O' L" w2 y* y3 o
while(k2!=i && vl[k2].y==w.y) ; P% K" `) J( `. r# c$ ~7 f: h9 H
k2 = (k2+1)%np;
5 t4 `; P7 }$ b# I, W' T if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) ( f. W7 ^8 z, X! h6 O
C++; + _- z& y: i; [+ R/ j* b: |" P7 v3 g
if(k2 <= i) / ^3 g* b, o% G) c& _
break; ( u& w" @% N# F5 C" v
i = k2;
7 i- G" D2 w& x, }, d/ O, ] } 9 N7 u+ C$ @8 w0 A: E, ~/ ^( s6 e
} 0 y8 n% O% S8 {6 A1 r" }; q! e" W
return c%2;
/ k6 |0 r& x1 P1 n- K: R& z }
7 x, z7 W1 g, F. e
* W0 I6 H$ }- g3 G1 h/ }" @
" J5 U& |& }. H0 C8 E1 R" y/ ? 本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|