返回列表 发帖

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种不同的结果。

返回列表
【捌玖网络】已经运行: