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

|
C语言中显示 点在多边形内 算法
本文是采用射线法判断点是否在多边形内的C语言程序。多年前,我自己实现了这样一个算法。但是随着时间的推移,我决定重写这个代码。参考周培德的《计算几何》一书,结合我的实践和经验,我相信,在这个算法的实现上,这是你迄今为止遇到的最优的代码。- E3 b7 n E) b* P; `
" [5 K6 K! t e; h, G6 S9 z
这是个C语言的小算法的实现程序,本来不想放到这里。可是,当我自己要实现这样一个算法的时候,想在网上找个现成的,考察下来竟然一个符合需要的也没有。我对自己大学读书时写的代码没有信心,所以,决定重新写一个,并把它放到这里,以飨读者。也增加一下BLOG的点击量。3 y9 R* \- v7 d
6 K- W1 f4 E) `+ g1 h; q) i) O( I; _
首先定义点结构如下:
^) c* a5 D6 l z$ t: n+ E0 L# I r+ P9 T) U" }) F) O6 Z
以下是引用片段:
+ K4 M" y$ I3 I9 b! y& s+ w /* Vertex structure */ % {; u F; B3 r. j
typedef struct
9 ~3 i. ?$ D0 E4 }, ? q f {
$ Q2 T* m) R/ B# u& { double x, y;
' }- E1 |" J7 }! r6 d8 a: } } vertex_t; 9 g0 T6 P& q7 @/ Z
+ r [2 c+ _0 o f4 C l9 J4 N0 C9 }" b! ?! L; [: T8 X
本算法里所指的多边形,是指由一系列点序列组成的封闭简单多边形。它的首尾点可以是或不是同一个点(不强制要求首尾点是同一个点)。这样的多边形可以是任意形状的,包括多条边在一条绝对直线上。因此,定义多边形结构如下:
3 n; ~$ f1 h4 m$ C5 g
7 X; z2 f$ i& Y$ v以下是引用片段:. A/ d! d/ P0 N; v% l- o3 D x
/* Vertex list structure – polygon */ 8 f0 F6 m# |4 N- f) w
typedef struct
) J$ d2 }9 [% j$ n, | {
, b! j& K; T9 \; ~$ ?6 v5 M2 Y int num_vertices; /* Number of vertices in list */
+ t. L3 t' B) V/ H6 H vertex_t *vertex; /* Vertex array pointer */ 9 t }- f) l! M; ~
} vertexlist_t; i! r& A* _$ K6 R/ H# L0 s
Q+ h J1 h. z
4 G8 n1 g2 Y! Z( v( m I 为加快判别速度,首先计算多边形的外包矩形(rect_t),判断点是否落在外包矩形内,只有满足落在外包矩形内的条件的点,才进入下一步的计算。为此,引入外包矩形结构rect_t和求点集合的外包矩形内的方法vertices_get_extent,代码如下:
" i1 q& y7 G3 R! d0 F
) j0 s7 ? c1 d. u j$ ~7 [7 ]& |以下是引用片段:
4 n3 p4 c/ n9 U k7 h; \: Y; L3 k) I /* bounding rectangle type */
# [/ a2 C/ \5 H) K typedef struct
& E, o: d5 O! X2 e3 L: I! R {
8 I" J5 |( u2 a double min_x, min_y, max_x, max_y; # A7 d: X% G" n0 e- H2 z& h
} rect_t;
; S4 S% X) c7 j1 k" V /* gets extent of vertices */
2 j8 K$ i5 f8 ]: C- r8 I/ X; f void vertices_get_extent (const vertex_t* vl, int np, /* in vertices */
* [4 e' N# b# L$ M rect_t* rc /* out extent*/ ) # \( a0 f0 V1 ^' y" j' O7 M: n' Y/ F
{
0 n1 r, n4 r+ t U1 L3 t int i;
; ~( ^" n# T }4 d- Y9 t @( u if (np > 0){
- w( b* q! g4 B+ r' K0 Q9 L rc->min_x = rc->max_x = vl[0].x; rc->min_y = rc->max_y = vl[0].y; / i) z/ ~6 ~8 \
}else{
5 s5 B7 b4 ]. U# ^+ H! K% @5 Y rc->min_x = rc->min_y = rc->max_x = rc->max_y = 0; /* =0 ? no vertices at all */
m; `0 M3 S9 E8 t9 e) e }
7 k: q4 D# |8 L+ v& `/ d5 Q4 @ for(i=1; i , e' y3 T( k$ p5 }6 ]$ F' t
{ 7 ~6 ]% @0 M: z0 b0 w
if(vl.x < rc->min_x) rc->min_x = vl.x;
6 L/ J2 U S, U, b) m if(vl.y < rc->min_y) rc->min_y = vl.y;
! a, L( e7 g6 c5 g* {! K if(vl.x > rc->max_x) rc->max_x = vl.x; : `$ b* H7 a+ D0 _
if(vl.y > rc->max_y) rc->max_y = vl.y; " T- s' R$ O4 }+ s6 N% d
} . c) a9 B+ D d6 n0 g3 {. f
} , t/ G- U; i$ m O9 |3 h$ `
5 S; q% K7 e( N) _
2 N- A- ]8 M4 ]' I: a" c
当点满足落在多边形外包矩形内的条件,要进一步判断点(v)是否在多边形(vl:np)内。本程序采用射线法,由待测试点(v)水平引出一条射线B(v,w),计算B与vl边线的交点数目,记为c,根据奇内偶外原则(c为奇数说明v在vl内,否则v不在vl内)判断点是否在多边形内。
7 i5 M# D, o: J) L2 y8 Q& D& u; z. z6 R. U+ C' w
具体原理就不多说。为计算线段间是否存在交点,引入下面的函数:
! I) E0 _2 w9 \
8 s# W& \' o O5 e- q. ^! r (1)is_same判断2(p、q)个点是(1)否(0)在直线l(l_start,l_end)的同侧;( `* C$ _8 W5 ~! u5 @% r R
9 [4 r' S W4 B: @* J, i. `
(2)is_intersect用来判断2条线段(不是直线)s1、s2是(1)否(0)相交;
8 G' V) i/ p8 m4 ^* Q$ G H b. t$ O" j" r, l
以下是引用片段:
7 V* Y/ P# A9 f/ W# D, H! i /* p, q is on the same of line l */ " J) b& ~+ [; W" r- q
static int is_same(const vertex_t* l_start, const vertex_t* l_end, /* line l */ : l5 Q& e9 a7 q' y5 u" z+ p2 l
const vertex_t* p,
6 ]3 k$ {9 Q, Q- k) R4 ]2 C const vertex_t* q) - u% ~6 r0 ]8 q1 p/ p$ [0 `
{ / R8 q# ]1 Z, n) H2 m
double dx = l_end->x - l_start->x;
( X$ E0 u3 e8 U/ B: ^" g2 ~: K double dy = l_end->y - l_start->y;
* h# M1 }% m. Y& _# V double dx1= p->x - l_start->x;
( v( w9 V3 U$ M& ` double dy1= p->y - l_start->y;
" v( e" }) d. {, ?; t5 p double dx2= q->x - l_end->x;
" e* ~; g9 d" O double dy2= q->y - l_end->y; 9 _ a+ V% I; X
return ((dx*dy1-dy*dx1)*(dx*dy2-dy*dx2) > 0? 1 : 0);
6 e2 N9 W: b/ W4 J } - D9 d- M1 @% }: E7 V$ S( L
/* 2 line segments (s1, s2) are intersect? */
5 a1 b0 m! ~1 g4 F, ~ static int is_intersect(const vertex_t* s1_start, const vertex_t* s1_end, ! d2 R$ J. C0 O1 O8 C
const vertex_t* s2_start, const vertex_t* s2_end)
* F) I1 Y1 a# B! y$ l' }9 c2 V { 5 I, G# N' b% Q
return (is_same(s1_start, s1_end, s2_start, s2_end)==0 && . b0 G& a9 W/ y
is_same(s2_start, s2_end, s1_start, s1_end)==0)? 1: 0;
, Y4 x" d- u* `* l, a }
" j/ s7 ?0 \. ]$ D' x8 X) z& H" j3 N+ e2 D7 ^
9 C" w2 d3 V8 w, Q- c 下面的函数pt_in_poly就是判断点(v)是(1)否(0)在多边形(vl:np)内的程序:/ J- `) X5 i" ?+ |4 Y
' P7 l* l( q4 ?7 [$ }7 Q X. p- t& t以下是引用片段:( z! o0 X8 K/ {$ \
int pt_in_poly ( const vertex_t* vl, int np, /* polygon vl with np vertices */ ' X. I5 v% p& \3 _/ R! j& R
const vertex_t* v) 5 T3 n' b2 x' {( t0 P# v
{ A( j- h+ L+ }" b
int i, j, k1, k2, c;
' B+ V3 T3 _9 L rect_t rc;
! e9 q6 ^2 `; E, D1 g- O# v, x vertex_t w; . {3 O6 A" d, F0 _8 Q$ i* h
if (np < 3) / t7 ~; i$ D$ B4 ] X5 n0 V
return 0;
6 u3 {9 a! A$ v7 p2 q vertices_get_extent(vl, np, &rc); , y- x* n5 g+ A" T" j; M2 J9 i# X
if (v->x < rc.min_x || v->x > rc.max_x || v->y < rc.min_y || v->y > rc.max_y) 3 O- Q; I6 E; c
return 0;
( L2 p1 Q7 \: c/ J8 o7 ?7 I9 } /* Set a horizontal beam l(*v, w) from v to the ultra right */
3 q" D& e( v# ^' D/ D' R1 k w.x = rc.max_x + DBL_EPSILON;
+ C) L+ F; w* v3 _0 j w.y = v->y; " M; i2 Q/ t* y
c = 0; /* Intersection points counter */
( d& H9 j& l" l6 C for(i=0; i 8 k; K7 n% s: t% O3 V, i9 }
{ # \" b. D; o, V7 b% l4 g
j = (i+1) % np;
1 z9 y8 M; @. @) O0 H( y: M if(is_intersect(vl+i, vl+j, v, &w)) . N. [2 G K: m# `1 M) |7 o- f
{
! c' T0 F) ~2 I) Y C++;
+ ]# V. \( R# }* ]) n( ^ A }
h& U! L( w0 ^ S& j) y: k6 j/ Y3 e else if(vl.y==w.y)
1 Z" L$ t8 ~0 m3 Z& b2 A {
% `( t: x% g; ^9 B k1 = (np+i-1)%np;
z) K: }9 {4 K8 Z1 g while(k1!=i && vl[k1].y==w.y)
1 n& g; A$ a$ b: O. a( B& n$ N k1 = (np+k1-1)%np;
, n; \2 W* b- F k2 = (i+1)%np; 9 R9 R* Z) U1 u3 M' W. g
while(k2!=i && vl[k2].y==w.y)
5 c0 O$ [! @# @" e& d( W- d$ U k2 = (k2+1)%np;
) u4 W! F, ?4 u; Z3 x if(k1 != k2 && is_same(v, &w, vl+k1, vl+k2)==0) 1 k3 M0 C6 w, Z' M* T+ U" A3 o
C++;
" _( M @2 b) v" A if(k2 <= i)
: ~& m: ]9 s0 E2 r% { | break;
2 k0 f. ?7 @$ g5 b3 C i = k2; ! a. @2 p* l5 s# y
}
6 {' V, h9 L. @' U, w1 G+ S0 Z }
& X- p2 s( \: G5 C4 M9 @. v/ W return c%2; / Q5 z1 m; P' o$ s1 Q6 b( R+ f
}
! K; G: E7 g# @# S: F
6 Y9 A$ W, B3 ^$ d' y$ u' p" p9 m: e' _$ q1 Q N b
本想配些插图说明问题,但是,CSDN的文章里放图片我还没用过。以后再试吧!实践证明,本程序算法的适应性极强。但是,对于点正好落在多边形边上的极端情形,有可能得出2种不同的结果。 |
|