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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。6 V- \3 W& D1 @5 w8 @
% f' o$ X4 y: O6 F5 S 这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。
- K( m/ i$ L0 w: q1 E
. Q2 P8 C2 \+ c1 i 首先定义点结构如下:+ |+ }0 F' J9 q
! J$ Y! \" Z2 k2 p7 k
以下是引用片段:6 y. c1 f1 e6 s3 }+ u" a
/* Vertex structure */
# j2 W( `0 u( I4 f% a T! g! y typedef struct
( e c o2 e; S. F7 e7 d+ Q {
5 A* x6 `9 q) Z9 E% c double x, y; 3 I) N' P2 U# h7 [$ D
} vertex_t; - w g% P# |6 V$ e+ O8 \) K7 R7 d
% n/ e% X4 r2 R. \" m1 d; b
9 x6 q8 f) ~: P& Y8 S. y/ o% u) B: T
本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:7 h4 G- Z! A1 K+ Y1 N/ I+ Y, ]
' F7 X" e( P5 s- ~ g! |! U$ h
以下是引用片段:5 j7 u. M1 b; e. \$ K
/* Vertex list structure – polygon */
1 m+ s1 }/ _0 F) w2 s3 q6 U typedef struct : s7 \# s+ x* a4 B/ K+ r* [
{ % N8 O! K# Q$ _0 L
int num_vertices; /* Number of vertices in list */ ^6 U+ }( ^* J. \
vertex_t *vertex; /* Vertex array pointer */
" N1 C6 \7 A" e } vertexlist_t; ) t: C2 C$ o s1 Q( p2 q) {/ O- L
; f& ^/ t5 w* a- e' \" y
1 x! G" R: n9 i# S: J& z2 R3 L 为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
5 B2 M1 k" S6 }+ @' W- d
8 f3 r5 j% @6 [2 |% e; _以下是引用片段:
0 S; v1 N, N0 A ?0 A* O /* bounding rectangle type */ ) i- @8 o1 j3 ^9 N/ A. Z
typedef struct . k, ^) j% b: l- x
{ 2 h( G, B" ^! }+ l8 m0 u9 }
double min_x, min_y, max_x, max_y; . d/ [. J7 N2 |! R! U2 \* F0 A
} rect_t;
- Z" @+ ^# M1 N. x3 c7 G! N6 L /* gets extent of vertices */
$ P6 t/ ~# J: N# {3 e void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */ n" V. f. L: W ]$ \! r; |; T+ c: Y- b3 U
rect_t* rc /* out extent*/ ) " n* n! P- m! X" [% ?
{ 8 e4 {0 U- `% i9 U
int i; 4 `5 H- S% ~8 o* v
if (np > 0){
, j, x* }, D6 r7 F1 w- w# N4 R rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y;
: X: o5 @, d0 ~: m% U( \, x6 w% i+ r }else{
! I, I4 r# b/ X, \! ]" M rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
( n/ {9 f' ^/ s+ i }
2 Y: q/ V5 f8 v$ H5 w8 C% j for(i=1; i
$ {* B9 I. c- O7 F; f5 z { . j& @7 S/ q& e* ]
if(vl.x < rc->min_x) rc->min_x = vl.x;
' ~( C( O8 `" W, s$ o- [5 [7 _. s9 Z if(vl.y < rc->min_y) rc->min_y = vl.y; + I/ N+ u1 l& |" E
if(vl.x > rc->max_x) rc->max_x = vl.x;
7 e; ^* n) r+ X% y$ @ if(vl.y > rc->max_y) rc->max_y = vl.y; $ x: j9 T8 Z- j1 B6 y; a4 F, T+ b
}
; E8 B( U' O* ^ } 8 U) n/ k( G3 i5 J; w
& E, U# t v* m2 g3 P# I
) n3 B% A3 O- @7 _8 r$ f7 s
当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。0 c, R# T" R0 R' ]. p
, H$ I1 b6 W- j, W
具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
* i" s6 Q. _$ E3 B/ u9 `$ W8 z* @& U j8 C" y9 S& Q7 Q
(1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;
7 m2 S" P# ~: N7 ?' h( a% S3 `8 T; @2 @" w! V& B$ C7 a" d
(2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;+ W- g+ f7 O+ w* `& S
6 g _3 g4 ]- i# N7 G3 n以下是引用片段:0 _4 Y9 _( M$ \! A# S1 M
/* p, q is on the same of line l */ 8 ^. e$ S L0 ?! {' R0 z8 c
static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
7 P; \1 r1 {; _2 n1 h const vertex_t* p,
/ K! S9 R) I. n6 t$ h% e const vertex_t* q)
& r; y/ B% r. V { 7 }+ J3 U3 d/ b) C8 Q. ^
double dx = l_end->x - l_start->x;
# S+ \5 L* C9 n+ w& Y: C6 X( J double dy = l_end->y - l_start->y; % B: ~6 Y! d' t3 Y1 v6 |
double dx1= p->x - l_start->x; 3 u* i* i) N. |! @
double dy1= p->y - l_start->y;
9 c9 O0 `2 X; {# V/ J# o double dx2= q->x - l_end->x; , a! Y3 |/ C9 G! L& A: p
double dy2= q->y - l_end->y;
+ `* {+ l& d$ v/ Z0 s return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
- J4 X( e/ ~7 r( r' @; O* T* d# ^ }
" G3 m3 ^! c# ]7 f* q. q /* 2 line segments (s1, s2) are intersect? */ & [, Q" Q7 x& y S4 j$ L( V9 C- u" a
static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, 5 R! R+ T0 V; z) d u
const vertex_t* s2_start, const vertex_t* s2_end)
# l" W- K7 h5 }+ S5 }0 a { / |% |: s- o3 i% ~! G
return (is_same(s1_start, s1_end, s2_start, s2_end)==0 &&
/ V( W" V+ H/ A6 c is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; 5 p+ @# O# S7 F3 c7 K7 |
} ' `: K1 ?& O" J) b4 j
. f0 s7 X; B: R- W7 y! A6 V9 t# J7 p. [* {' o
下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
# S6 P3 [ O# P# l3 ^6 h) K/ {
5 S9 I3 z# h9 g6 N以下是引用片段:
1 o; O8 F/ @) Q" H8 j int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ u4 z. [2 G6 }
const vertex_t* v) 9 E& @7 Q9 B8 c) n8 J e
{ " @+ J6 P0 L- G/ i
int i, j, k1, k2, c;
/ G; s0 U( z. F: Q* ~# z6 L rect_t rc; 7 _1 v8 ~) x( H( X" Y0 ^# g! h
vertex_t w; ' ]6 e/ p( z- D5 h& M
if (np < 3) " _- L( u b% Q3 i; f0 i, h
return 0;
, y# z" Y' n) s5 ? vertices_get_extent(vl, np, &rc); * L$ i# Z- y) Q, _
if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
' g e' G1 `* `/ s, Q* R; X return 0;
. ^4 q2 K9 s2 E$ O, x) k /* Set a horizontal beam l(*v, w) from v to the ultra right */
* X. b0 Q# }. ^$ M5 d w.x = rc.max_x + DBL_EPSILON; ! W: S m+ c/ Z& ^
w.y = v->y;
( [3 ]) k4 @' e$ _+ z J2 r c = 0; /* Intersection points counter */
w2 U" a0 V' L ] for(i=0; i
2 x* [4 m) B$ |* L; e3 P {
9 R A# N t/ C/ g8 z8 @& P1 i" } j = (i+1) % np; 3 v r* C: T3 ^- p9 I% c
if(is_intersect(vl+i, vl+j, v, &w))
) Z5 c, A7 K1 J- _& v9 O0 W { 0 M( c! a/ G( c0 Z6 H
C++; ; i$ v1 t8 C: m8 f6 X5 D
} ! L) g: r+ i+ m/ d8 B
else if(vl.y==w.y) ; }: r' d, N5 B6 L
{ 4 `: \% d' F: b1 |2 l- D7 B& I
k1 = (np+i-1)%np;
( H. E6 w+ U6 E/ n, R# W6 I1 `+ a while(k1!=i && vl[k1].y==w.y)
# D+ s- O# P6 t- J$ K9 ]" e k1 = (np+k1-1)%np;
3 K6 a4 X& s- [- y; ` k2 = (i+1)%np;
3 t) _% x) R" ? while(k2!=i && vl[k2].y==w.y) ' ^9 E4 b/ t! f% X# H5 \- R
k2 = (k2+1)%np;
8 @9 G! Z, t; t* b if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
5 Q8 Y5 [6 L) G C++;
1 i B, s' ^% W/ `8 L) `9 ` if(k2 <= i) # C, p# u( v1 {/ h, h
break; & g& @, n% o6 e& f3 S9 p0 N
i = k2;
1 u1 }$ ]1 d6 f- X2 j! \ }
. r( t9 d- m- v r z3 \- ^ }
$ _* ?9 }5 U, A& t$ t% r return c%2;
3 _$ c/ h1 G: q( |+ t } + Y6 q8 A$ Q* D
' s' v: D. @7 A0 ?. D
5 Z9 k# S; m6 S( [# k
本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|