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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。/ d# i, D9 z% e$ _5 I0 e
3 `( |' I' U' x9 Y5 Z! S 这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。4 l! W0 }( z/ N7 O
6 c; k3 q6 \( e8 {
首先定义点结构如下:
. d7 Q4 @5 S4 }8 g' p' h* K/ L8 }
g+ O- O; x9 i9 V$ A8 o: w1 y; Z4 S以下是引用片段:3 |/ }- `. y1 N1 j1 K
/* Vertex structure */ 1 g' H K2 [. \
typedef struct
# b" y; a& `3 k! {. c! s% s7 L { 1 ]+ S" ] S1 j( U3 n
double x, y; ! o4 k: F( h7 j; u% B; X3 I
} vertex_t;
. u- Z- o' b/ t* z5 Q; u9 L& C
, P ?' a. j) M2 s9 }% [0 t0 t6 E+ u% ?( C' b0 q" K; l8 V
本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:% P/ s9 [$ Q) O5 U. }$ O
! E8 ~2 c' f: |+ n! u
以下是引用片段:- x) h' W+ D% F" Q
/* Vertex list structure – polygon */
3 w2 U( t3 j. t. z ] typedef struct
4 @$ A6 i+ l: Y1 }( L/ J1 a; L; ` {
% _& S# T7 `5 ]- O* ~! w int num_vertices; /* Number of vertices in list */ 5 S, ]1 g: h; Q( c7 r8 ]+ |- V
vertex_t *vertex; /* Vertex array pointer */
6 q& }, b! y- F7 M/ Q* z } vertexlist_t; ( |4 w2 \+ U4 D- D
/ u. G( z) a. l+ Z3 ^% i
# f" c) s! W" n$ L- V! L' q9 K7 ` 为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
, y* e7 X* ^0 a$ R8 R6 K: b5 M0 |$ L K( m, } F9 P' ?! m
以下是引用片段:
* c3 U; S) a; b /* bounding rectangle type */
m+ h* r: [% b" J" m8 @ typedef struct ( ]& k. z& ?5 S. U7 m
{ : K! ^% z% N$ _% n* \
double min_x, min_y, max_x, max_y;
) r! N9 z# ]8 { } rect_t;
- L7 L( G2 {% H /* gets extent of vertices */
& M) o: L$ o" y8 b+ S) U void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
! ]- u9 y9 z7 c rect_t* rc /* out extent*/ ) ' L3 l$ e# G+ d5 O- ~# \
{
, v, f0 i! S( b6 u+ G9 b; z int i; # f6 _/ I5 z+ H$ R3 Y3 j- f+ r
if (np > 0){
/ O& _: P* T9 m rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; 2 w/ Y( x1 _9 V$ Z
}else{
" I8 P* F2 h% K% g; T' Z* ]3 ~ rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
{' l, Z' @; L- P3 g } # b' I4 B* y% C/ ^+ q
for(i=1; i $ Z+ h" x2 z$ B8 W8 |; A- S
{
% i, e6 Y, [ s( X6 [/ n7 X2 E if(vl.x < rc->min_x) rc->min_x = vl.x; % M# a: f; ~9 z5 \! f
if(vl.y < rc->min_y) rc->min_y = vl.y;
- ?+ C& i9 Q: O% ?( a* g' |* c; J if(vl.x > rc->max_x) rc->max_x = vl.x; 2 D6 @5 d; u* b" i
if(vl.y > rc->max_y) rc->max_y = vl.y; ( ]# j) p- k3 j- R* y9 A# R7 T
}
6 T0 I/ T7 }2 x3 x: |( r" N } 2 k: J5 J& A, a3 y5 q
" D# v9 a' U, d r$ f' i- b: v5 o& |% _1 i9 }' V& }( u- Z* N1 v8 T, t
当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
8 @: e8 g9 S0 c6 q3 a& @0 ~
" k* T3 v/ ^+ P/ D6 p: K 具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
! L, N3 e- b4 f2 W8 Q7 e
7 b% K/ Y: l) f# i0 d- ?( d (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;& q6 u9 p9 F, n ~; h- D
+ s- v) y$ T0 z+ s& f$ v (2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
/ O9 k/ a+ c& P& y$ D2 v1 ?6 c5 I- \( z5 g; M% s
以下是引用片段:
2 x! s+ v; T k N, }7 {- y /* p, q is on the same of line l */ 0 h$ u% C% n6 j4 O. r" p# j
static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */
- _% j2 H4 _( [" f8 x) F& _ const vertex_t* p, - }& F# ^% q6 M% j% j
const vertex_t* q) " X" ]; T4 @* n; j; n
{ 6 `8 f, E. X0 }+ c+ a1 q+ t% g
double dx = l_end->x - l_start->x; ' i2 e2 e" L" X) d. M: N
double dy = l_end->y - l_start->y;
4 V( |" i5 a; Y# K double dx1= p->x - l_start->x;
I1 }- @: R' o9 S; [/ C) [: P double dy1= p->y - l_start->y; 4 E& U/ k x) B E/ W
double dx2= q->x - l_end->x;
3 }& |7 W6 P1 P* I' P6 u, V double dy2= q->y - l_end->y; ; d8 j! ~! }6 P% M+ d9 F
return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0); : N ^+ p% s3 p6 K+ |, D
} 0 H1 g+ p/ g- [# P) E
/* 2 line segments (s1, s2) are intersect? */
- H4 T. k! Z) D5 x$ R static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end,
7 s( X# V% u9 C {' ` const vertex_t* s2_start, const vertex_t* s2_end)
2 o% S/ K) j6 S) _( B8 g0 K {
5 ~$ P* }% F5 y C3 w return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && % @! } }" S) H6 f5 \
is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0; + G+ y0 p, |8 f& Z) h Q- O
}
/ q5 P p3 ?6 Z+ W D, u T9 G% N. T5 g( v. O
2 y1 c6 |/ S i# l6 n, K 下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:
7 L% r7 S. Q1 ~7 `
' P# M7 o9 _' O8 g以下是引用片段:
/ @# r7 \8 }! E6 ?2 u+ T( { int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */
/ g- t; N" O4 \/ G& V const vertex_t* v)
6 k: Y5 b1 U2 @6 j8 H! _6 c { 9 W, v$ O8 B0 N1 W
int i, j, k1, k2, c; : Q2 c: b) J* _/ H [$ ?5 P, y
rect_t rc; 2 A4 A& u8 v, u! ~% V
vertex_t w; $ r6 s+ s/ m/ [- w+ i" _
if (np < 3)
1 r9 J; Q0 L [4 f0 k6 H return 0;
) y3 l& ]' c6 Q% L3 D3 |4 j vertices_get_extent(vl, np, &rc);
3 ]* _2 y' e# ~& M( v0 B7 J if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y)
% c9 _" T4 `$ u. m. X" X. ~. n return 0; ; M5 b2 b& U4 j. B
/* Set a horizontal beam l(*v, w) from v to the ultra right */
, o. ~+ U$ W) [7 a# K- V w.x = rc.max_x + DBL_EPSILON; 8 g& ^+ ]" i7 y% ~' |# f: V2 o4 |' |
w.y = v->y;
, p( Z7 j* s9 a N5 F9 i3 M c = 0; /* Intersection points counter */
! `! h8 J' s" S- H' y7 x) G* t for(i=0; i
$ M9 Z9 U/ Y, v$ v {
* _+ k' d4 S. l! E7 V j = (i+1) % np; - h+ Z; F- T6 Q
if(is_intersect(vl+i, vl+j, v, &w)) $ }6 p. x- N( Y/ N
{
! L g7 p* G7 A, Y C++;
, E# c( B8 `0 b }
5 p4 r& A& d) a' U+ `* ~4 o/ l else if(vl.y==w.y)
' W$ z l' q% f { 3 L* ?8 `0 e3 i6 h6 M; k2 b Y
k1 = (np+i-1)%np; - P6 P! L" E( O/ |5 G$ O
while(k1!=i && vl[k1].y==w.y)
+ q- v7 U5 x7 a$ s% Z k1 = (np+k1-1)%np; . `: `, _7 l) _
k2 = (i+1)%np;
% a; u \9 `$ s; x5 k while(k2!=i && vl[k2].y==w.y) & z5 \! ~- z! u$ v: t1 j7 X
k2 = (k2+1)%np;
" u+ N% X( B# e3 U if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0)
' } x4 ~2 ?- y( S* b C++; 4 |) v2 C+ C$ o* f
if(k2 <= i) ' e' z6 A+ h* H6 W) X: b9 K' X9 k( y, n
break; 0 ]1 l- `. i/ A1 N9 M* Z
i = k2; * [3 v7 H* { S8 o
} & K( u+ _( H1 L. b& H/ J
}
]9 M: o* p {% e return c%2;
) \* z9 v3 z& `8 W } ' o' b; D4 y7 q6 g1 H1 W9 q
% q9 B# ?, N' F m8 n' K8 T
- [. k, O+ y% ?# V) h- g' Y
本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|