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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。
9 m" \. n- ~4 A. e; _4 _- `8 a2 R: i; f" _; c& {
这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
) S9 n2 Z0 _$ ~% K5 [
" s1 j# Y! x* J+ m P* X 首先定义点结构如下:
7 t$ P( ?3 R( V6 i2 S
; l: Z4 T2 {, U* _4 z% U2 q% k以下是引用片段:/ y3 ^/ j' B+ P$ j7 q$ w9 d$ x. l3 y4 A
/* Vertex structure */ ; A e ]7 @$ | n& j5 c: n5 ?
typedef struct : e( M2 _) L, S; w. b) j
{
) I$ ~5 Q! U8 J& j double x, y;
$ H8 T5 `5 M' G% p* o+ L# I+ t1 D } vertex_t; 7 i1 G2 t4 X0 j7 k, K
' ?, W: @6 Y/ k) F; E9 }
+ ]* S2 P. b( e+ l( r9 J. p 本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:% `4 @) U$ Q. B6 a
! _ B# R; Y( L! u0 A
以下是引用片段:$ y! w% k8 B9 ]- u! A7 [
/* Vertex list structure – polygon */ 7 u8 {1 m( w6 y
typedef struct
# _, G H1 s }) l5 {$ _ {
2 D8 I: Y) }; l# N& Y int num_vertices; /* Number of vertices in list */ 2 R+ H( ~, A: ^8 M
vertex_t *vertex; /* Vertex array pointer */
6 W0 I# z+ k6 y$ u2 h } vertexlist_t;
8 m0 C: m. D- F# D! c6 b3 }& {
0 A2 `7 n9 J. T L5 {& u3 K2 I( H. `( i( x, y5 k
为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
5 }% ?& T+ D5 [1 b
8 w! w# {/ S! K8 c. d以下是引用片段:
/ G% k! @# }9 X% O /* bounding rectangle type */ 6 ~; i0 M) R! t7 Q( `
typedef struct
. f# k, N" _ B# C5 Y {
7 R% s8 }8 F1 g% ~ double min_x, min_y, max_x, max_y;
& B0 r, r' b* I# ?: Q# T } rect_t;
1 y' f, E' G3 Z9 @% r /* gets extent of vertices */
7 H% a% f, r; ^ void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ 9 \% W# C& j3 c/ L( Y! n
rect_t* rc /* out extent*/ ) ! U' o" G8 O/ Y- g
{
1 z+ {1 Q* F% K; P* ~/ Y& b5 w. H int i; + u% i U0 I- k
if (np > 0){
* G7 n9 g3 v* k2 d' t5 Z/ S rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
6 i% ^9 h6 a! `3 B }else{
6 u4 K0 }& w! _, X* N- [5 Q rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
# k3 c) g# S8 f! m" \7 N. W }
8 K3 L7 v, E$ O! k( G2 G( z for(i=1; i / t; ^0 d* \) I
{ , j& u5 e& t3 _' n( u; u
if(vl.x < rc->min_x) rc->min_x = vl.x;
- b1 C: F6 C( t7 D9 `. I3 K if(vl.y < rc->min_y) rc->min_y = vl.y; , o. R8 A \6 U2 M
if(vl.x > rc->max_x) rc->max_x = vl.x; ! M) _+ V5 ~- _2 w( h3 _
if(vl.y > rc->max_y) rc->max_y = vl.y; # j3 `2 V1 @/ v# z0 B
} # j) `# U- _$ ?) L ~
} + ^% E2 I7 @6 j7 `3 d! L, _
- f' V% y* s+ ~* I
; w. H; I" ]9 g! x* B) e: c 当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。/ g+ Z6 C& g5 @# w- v
! `5 N: ~3 P' ~7 c' d3 ~' B, M; z$ g 具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
' S1 M. f0 k, f+ a" \( f' G& b* e0 k$ s6 C9 k( Q
(1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
1 W8 h1 f. f' p# u! x' X
$ w# B. @- d# \. b& A (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
: z4 @ x; {, Y5 l% f% W/ h+ R( y9 U1 f" H, P
以下是引用片段:4 {5 ^# I/ |, R- x8 \: l
/* p, q is on the same of line l */
0 S; [+ c& i/ W1 s* s: [ static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ 4 Z1 L. P$ ]/ [8 f; K Z$ F
const vertex_t* p, 9 N' f& Z6 S" Y) r7 r, A
const vertex_t* q) # }. I1 N! l G U m: k
{ ) P5 Y% r: \. ?8 J/ `. p
double dx = l_end->x - l_start->x; ; |5 r9 v0 \% w$ \
double dy = l_end->y - l_start->y;
6 O# z# {' \9 e1 k' b double dx1= p->x - l_start->x; # z& ~, p) R2 e2 S b. m6 H
double dy1= p->y - l_start->y; ) T# k5 t; s3 P- y5 A$ V- E7 c! R
double dx2= q->x - l_end->x;
/ X% h' c- y$ X double dy2= q->y - l_end->y;
' \5 Y y7 M' {! @3 v3 ]6 Y return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
) J0 @2 J# [ q$ e } 6 ] P! J% x. j" I( K
/* 2 line segments (s1, s2) are intersect? */ 5 a6 l1 a* U- B& M# @) ]" k
static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, . r i* z+ V9 _- D" W
const vertex_t* s2_start, const vertex_t* s2_end) 1 K* y0 ^8 n: V# f8 E/ ]
{ 0 G6 k& p8 C' U+ @, A4 l5 J0 r! O9 P
return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && 8 M) c# M) d h# k. @# ]( o* Z
is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; ; B' [4 x" O0 t% C% N( b
} / k, O- _/ }; S9 U8 K
; A3 k# U" \( h& Y2 i. G, Y
) O# o- P% m | 下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
* E: ]3 U9 M/ Z1 o8 l+ p6 M
% q! {( i: Q# q- K, e以下是引用片段:
+ d3 r) O3 F+ B# [& j/ U9 U3 ?1 d7 C int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
2 L9 }5 s4 [3 l. j5 \ const vertex_t* v) ' ^/ {1 j# g- [) v
{
; P ]. J5 c* @: p# \( E/ w int i, j, k1, k2, c; 1 a. q# U8 n3 P5 |& [
rect_t rc; 5 W4 k9 k- J3 m* P/ j$ M
vertex_t w;
8 l; r9 I+ t$ B3 W& b; t; r if (np < 3) . ?. W2 [: P/ [' G
return 0; , I7 b$ _# Q! L3 n' N
vertices_get_extent(vl, np, &rc);
3 c# B7 a* P# M$ t$ J d if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) ( L5 k5 w- z4 H) R
return 0; . S/ z/ S# C* _: ]1 O
/* Set a horizontal beam l(*v, w) from v to the ultra right */
+ N* R: u% Q* z* \9 o% t9 ?) q w.x = rc.max_x + DBL_EPSILON; 4 D, P4 s) ^! k4 x' ]# u; F8 {
w.y = v->y; 9 D( ~5 ?" t) D+ O. p% _& C5 t
c = 0; /* Intersection points counter */ ! U2 v; R( u' u3 u/ g# M4 Z8 t% K
for(i=0; i
4 x9 u9 B/ Y" d- h9 M. W7 R {
- G- T/ h( E. z4 A8 m j = (i+1) % np; $ M) s5 d# y3 Y1 y+ q5 B& F
if(is_intersect(vl+i, vl+j, v, &w)) ; ~' x9 o" {, _# X& |
{
1 ~8 o2 K, Z, {0 y C++;
- @1 C+ H% {) j/ H! Z! C# r } " \: G6 v" E# o' W; M
else if(vl.y==w.y) : x- O7 N% J- }. B* Q
{
- F; `: g+ ?& r7 @" a& k. W k1 = (np+i-1)%np; . u8 v! s {7 s2 N
while(k1!=i && vl[k1].y==w.y) $ H) o; m3 X0 K) J1 L7 e
k1 = (np+k1-1)%np;
, @8 O4 q( o- U* |& I; F7 z k2 = (i+1)%np; $ F2 j6 C8 f. _( t* F5 a
while(k2!=i && vl[k2].y==w.y) - t/ F$ r8 A* N" o4 e1 ~- H
k2 = (k2+1)%np;
- d/ |; ?5 C5 P! E7 Q6 a) \ if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) . b# t( O( @4 ~1 }
C++;
! n6 d, n b" U( g" e8 Q if(k2 <= i) ! p0 g4 I4 c( }/ `% U6 _
break;
% f, |; t. f+ m2 K2 |6 c i = k2; " x5 S: X+ e' O% T0 M
}
; i* _! D; Y! E: ]( ^. G1 p } , g/ ~7 z* E: E* _& Q8 Z7 {
return c%2; 9 T! p- F5 Y2 F. W/ {: \
} 9 _- }. i; r. b6 y3 W0 [$ F. c( w
* Y3 u8 O0 ?' D" z6 Q
, u$ s# L# X7 Y; d( y
本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|