点在多边形内算法比较

点在多边形内部算法比较

  ----by wangsh 

 

判断点在多边形内外是计算机图形学的最基本算法,在计算机图形处理、模式识别、 CAD 、科学计算可视化以及 GIS 中有着广泛的应用。判断点在多边形内外的算法有主要有定向射线法、角度法、数学公式计算法和网格索引法等方法。角度法要使用复杂的三角运算,计算量大;在工程上应用最多的是定向射线法,这种方法简单、可靠,但其难以处理对边界点及边界与射线共线等特殊情况的处理。

 

常见的算法是射线法。

该算法顾名思义,即从给定点引一条射线,计算出该点与多边形交点的个数。若与多边形各边交点为偶数个,则在多边形外,否则就在多边形内。算法需要考虑一些边界条件:射线若正好通过多边形的顶点,射线与多边形的边重合等。若射线穿过多边形的顶点时,若共享顶点的两边在射线的同一侧,则交点计数为 2 ,否则为 1 。具体计数时,当一条边的两个端点 y 值都大于 y 0 ,即边处于上方时,计数加 1 ,否则不加。当射线与多边形边重合时,可以判断其重合边的前一节点和后一节点,若为与射线同一侧,计数为 0 ,否则为 1 。通过以上的补充原则,我们可以正确的判断点位于多边形内的测试。

 

另外一种在图形学中常见算法为夹角和判断法

假设某平面上有点 P 0 和多边形 P 1 P 2 P 3 P 4 P 5 。将点 P 0 分别与 Pi 相连,构成向量 V i = P iP 0 。假设角 P i P 0 P i+1  = аi 如果 ∑аi = 0 ,则点 P 0 在多边形之外,如果 ∑аi = 2 π ,则点 P 0 在多边形之内。可通过下列公式计算

аi = arcos((V i ·V i+1 )/(|V i ||V i+1 |))

这个公式计算量比较大。它涉及三个点积、两个开平方和一个反三角函数计算。另外,还必须判断角度的方向,一般是通过计算两个向量的叉积的符号来判断。实际上,用反正切函数求 аi ,计算更简单。令 S i = V i xV i+1 ,C i = V i ·V i+1 tg( аi ) = S i / C i , 所以 аi = arctg S i / C i )且 аi 的符号即代表角度的方向。

 

还有针对凸多边形的叉积判断法,假设判断点为 P 0 。多边形顶点按顺序排列为 P 1 ,P 2 , ··· ,P n 。令 V i =P iP 0 , i =1 ,2 , ··· n , V n+1 = V 1     那么, P 0 在多边形内的充分必要条件是叉积 V i+1 x V i (i =1 ,2 , ··· n ) 的符号都相同。

 

下面比较了 GIS 相关开源软件的判断点在多边形内的算法研究,其中 GEOS 采用的是扫描线算法、 CGAL Grass Ogre 中的方法、 ACM Graphics Gems Repositor 中的方法、某 Blog 中算法、还有按照数学公式向量计算的等多个版本。

 

首先是 CGAL 的代码:

代码位置为 CGAL-3.5/include/CGAL/Polygon_2/Polygon_2_algorithms_impl.h 中:

 

template < class  ForwardIterator,  class  Point, class  Traits >
Bounded_side bounded_side_2(ForwardIterator first,
                                      ForwardIterator last,
                                      
const  Point & point,
                                      
const  Traits & traits)
... {
  ForwardIterator current = first;
  
if  (current == last)  return  ON_UNBOUNDED_SIDE;

  ForwardIterator next = current; ++ next;
  if  (next  == last)  return  ON_UNBOUNDED_SIDE;

  typename Traits::Compare_x_2 compare_x_2  = traits.compare_x_2_object();
  typename Traits::Compare_y_2 compare_y_2 = traits.compare_y_2_object();
  typename Traits::Orientation_2 orientation_2 = traits.orientation_2_object();
  
bool  IsInside =  false ;
  Comparison_result cur_y_comp_res  = compare_y_2(*current, point);

  
do   //  check if the segment (current,next) intersects
     // the ray { (t,point.y()) | t >= point.x() }
   ... {
    Comparison_result next_y_comp_res = compare_y_2(*next, point);

    
switch  (cur_y_comp_res) ... {
      
case  SMALLER:
        
switch  (next_y_comp_res) ... {
          
case  SMALLER:
            
break ;
          
case  EQUAL:
            
switch  (compare_x_2(point, *next))  ... {
              
case SMALLER: IsInside = !IsInside;  break ;
              case EQUAL:    return  ON_BOUNDARY;
              case LARGER:   break ;
            }
            
break ;
          
case  LARGER:
        
switch  (i_polygon::which_side_in_slab(point, *current, *next,
                    orientation_2, compare_x_2)) 
... {
          
case -1: IsInside = !IsInside;  break ;
          case  0:  return  ON_BOUNDARY;
        }
            break ;
        }

        break ;
      
case  EQUAL:
        
switch  (next_y_comp_res) ... {
          
case  SMALLER:
            
switch  (compare_x_2(point, *current))  ... {
              
case SMALLER: IsInside = !IsInside;  break ;
              case EQUAL:    return  ON_BOUNDARY;
              case LARGER:   break ;
            }
            
break ;
          
case  EQUAL:
        
switch  (compare_x_2(point, *current))  ... {
          
case  SMALLER:
        
if  (compare_x_2(point, *next) !=  SMALLER)
            return  ON_BOUNDARY;
            
break ;
          
case EQUAL:  return  ON_BOUNDARY;
          case  LARGER:
        
if  (compare_x_2(point, *next) !=  LARGER)
            return  ON_BOUNDARY;
            
break ;
        }

            break ;
          
case  LARGER:
            
if  (compare_x_2(point, *current)  == EQUAL)  ... {
              
return  ON_BOUNDARY;
            }
            
break ;
        }

        break ;
      
case  LARGER:
        
switch  (next_y_comp_res) ... {
          
case  SMALLER:
        
switch  (i_polygon::which_side_in_slab(point, *next, *current,
                    orientation_2, compare_x_2)) 
... {
          
case -1: IsInside = !IsInside;  break ;
          case  0:  return  ON_BOUNDARY;
        }
            break ;
          
case  EQUAL:
            
if  (compare_x_2(point, *next)  == EQUAL)  ... {
              
return  ON_BOUNDARY;
            }
            
break ;
          
case  LARGER:
            
break ;
        }

        break ;
    }


    current =  next;
    cur_y_comp_res 
=  next_y_comp_res;
    ++next;
    
if  (next  ==  last) next = first;   
  }
  
while  (current != first);

  
return  IsInside ? ON_BOUNDED_SIDE : ON_UNBOUNDED_SIDE;
}

 

上面是 CGAL 中的相关代码,值得注意的是两点:

首先,边界条件处理。每条线段都认为是前闭后开,这样就避免了当射线通过顶点,重复统计的问题, 另外,如果射线和直线重合,直接忽略(其实这时点可能在多边形边界上,直接 return )。
   
其次,程序中判断射线是否和多边形各边是否相交的方 法:用了 N 个巨大的 switch 语句,仅通过比较边的顶点和射线端点的坐标大小关系,判断射线是否与边是否相交。这样代码虽然写起来比较麻烦,但是比之采 用了一般的求两条直线交点的方法(二元二次方程组),避免了比较慢的一次乘法和更加慢的一次除法(这还是在这条射线是水平放置或垂直放置的时候)。另外还 有一些小优化,比如说多边形边由于首尾相接,上次计算的顶点关系( next_y_comp_res )可以再次利用。

这里采用 n 个巨大的 switch 嵌套,估计会对算法效率有影响。

 

下面是 GRASS 相关代码:

代码位置: grass-6.3.0/lib/vector/Vlib   poly.c

 

int Vect_point_in_area_outer_ring (  double  X, double  Y,  struct Map_info *Map,  int  area)
... {
    
static   int  first = 1;
    
int  n_intersects, inter;
    
int  i, line;
    
static   struct  line_pnts *Points;
    
struct  Plus_head *Plus;
    P_LINE *Line;
    P_AREA *Area;

    G_debug ( 
3 , "Vect_point_in_area_outer_ring(): x = %f y = %f area = %d", X, Y, area );

    
if  (first == 1)  ... {
        Points 
=  Vect_new_line_struct();
        first 
=  0;
    }


    Plus = &(Map -> plus);
    Area = Plus
-> Area[area];

    
/**/ /* First it must be in box */
    
if  ( X  < Area->|| X  > Area->|| Y  > Area->|| Y  < Area-> S ) return  0;
    
    n_intersects 
=  0;
    
for  (i = 0; i  < Area-> n_lines; i++)  ... {
        line 
= abs(Area-> lines[i]);
        G_debug ( 3 , "  line[%d] = %d", i, line );
    
        Line 
= Plus-> Line[line];    
    
    /**/ /* dont check lines that obviously do not intersect with test ray */
    
if  ((Line ->N <  Y) || (Line ->S >  Y) || (Line ->E <  X)) continue ;

    Vect_read_line (Map, Points, NULL, line );
    
    inter = segments_x_ray ( X, Y, Points);
        G_debug ( 
3 , "  inter = %d", inter );
    
    
if  ( inter  == -1 )  return  2; 
        n_intersects += inter;
        G_debug ( 
3 , "  n_intersects = %d", n_intersects );
    }

    
    if  (n_intersects % 2)
        
return  1;
    
else
        
return  0;
}

int   segments_x_ray (  double X,  double  Y, struct  line_pnts *Points)
... {
    
double  x1, x2, y1, y2;
    
double  x_inter;
    
int  n_intersects;
    
int  n;

    G_debug ( 
3, "segments_x_ray(): x = %f y = %f n_points = %d", X, Y, Points-> n_points );

    /**/ /* Follow the ray from X,Y along positive x and find number of intersections.
     * Coordinates exactly on ray are considered to be slightly above. */

    
    n_intersects 
=  0;
    
for  ( n = 0; n  < Points-> n_points-1; n++... {
    x1 = Points
-> x[n];
    y1 = Points
-> y[n];
    x2 = Points
-> x[n+1];
    y2 = Points
-> y[n+1];

        G_debug ( 
3 , "X = %f Y = %f x1 = %f y1 = %f x2 = %f y2 = %f", X, Y, x1, y1, x2, y2 );
    
        
/**/ /* I know, it should be possible to do that with less conditions, but it should be 
     * enough readable also! */

    
    
/**/ /* segment left from X -> no intersection */
    
if  ( x1  < X &&  x2 < X )  continue ;
    
    
/**/ /* point on vertex */
    
if  ( (x1  == X &&  y1 == Y)  || (x2 == X && y2 == Y) )  return  -1;

    
/**/ /* on vertical boundary */
    
if  ( (x1  == x2 &&  x1 == X)  && ( (y1 <=  Y && y2  >= Y) || (y1 >=  Y && y2  <= Y) ) )  return   -1;
    
    
/**/ /* on horizontal boundary */
    
if  ( (y1  == y2 &&  y1 == Y)  && ( (x1 <=  X && x2  >= X) || (x1 >=  X && x2  <= X) ) )  return   -1;
    
    
/**/ /* segment on ray (X is not important) */
    
if  ( y1  == Y &&  y2 == Y )  continue ;

    
/**/ /* segment above (X is not important) */
    
if  ( y1  > Y &&  y2 > Y )  continue ;
    
    
/**/ /* segment below (X is not important) */
    
if  ( y1  < Y &&  y2 < Y )  continue ;
    
    
/**/ /* one end on Y second above (X is not important) */
    
if  ( (y1  == Y &&  y2 > Y)  || (y2 == Y && y1 > Y) )  continue ;

    
/**/ /* For following cases we know that at least one of x1 and x2 is  >= X */
    
    
/**/ /* one end of segment on Y second below Y */
    
if  ( y1 == Y  && y2 <  Y) ...
        
if  ( x1 >= X)   /**/ /* x of the end on the ray is >= X */
            n_intersects++;
            
continue ;
        }

    if  ( y2 == Y  && y1 <  Y ) ... {
        
if  ( x2  >=  X)
            n_intersects++;
            
continue ;
        }

        
    /**/ /* one end of segment above Y second below Y */
    
if  ( (y1 < Y  && y2 >  Y) || (y1  > Y &&  y2 < Y) )  ... {
        
if  ( x1 >= X  && x2 >=  X ) ... {
        n_intersects++;
            
continue ;
        }

        
        /**/ /* now either x1 < X && x2 > X or x1 > X && x2 < X -> calculate intersection */
            x_inter = dig_x_intersect ( x1, x2, y1, y2, Y);
            G_debug ( 3, "x_inter = %f", x_inter );
        
if  ( x_inter == X ) 
        
return  1;
        
else   if (x_inter >  X) 
            n_intersects ++;
        
        
continue/**/ /*  would not be necessary, just to check, see below */
    }

    /**/ /* should not be reached (one condition is not necessary, but it is may be better readable
     * and it is a check) */

    G_warning ( 
" segments_x_ray() conditions failed:" );
        G_warning ( "X = %f Y = %f x1 = %f y1 = %f x2 = %f y2 = %f", X, Y, x1, y1, x2, y2 );
    }


    return   n_intersects;
}

 

通过分析 Segments_x_ray 中的代码,可以看出算法核心思想是选用射线法判断点的位置。不过这次采用无数 if 组成的大分支结构,看上去没有 CGAL 中的 switch 清晰.但是主函数 Vect_point_in_area_outer_ring 中的两处优化值得一提,首先就是判断点是否在多边形的外接矩形内,这一个不起眼的筛选在很多时候能极大的提高效率(这就是跨立实验,用来排除大量大概率发生情况,这个非常关键),因为判断点在不在矩形内这个操作的开销非常小,而一般来说点不在多边形内的几率比较高。其次就是在选取多边形边界线段的时候,只选择有可能和射线相交的线段,这样对于复杂多边形,性能会进一步提高。

 

下面是 GEOS 的扫描算法:

代码位置: geos31/source/algorithm/CGAlgorithms.cpp

/*public static*/

bool

CGAlgorithms ::isPointInRing (const Coordinate & p ,

         const Coordinate ::ConstVect & ring )

{

     double xInt ;  // x intersection of segment with ray

     int crossings = 0;  // number of segment/ray crossings

     double x1 ;    // translated coordinates

     double y1 ;

     double x2 ;

     double y2 ;

 

     /*

       * For each segment l = (i-1, i), see if it crosses ray from

       * test point in positive x direction.

       */

     for (size_t i =1, nPts =ring .size (); i <nPts ; ++i )

     {

         const Coordinate *p1 =ring [i ];

         const Coordinate *p2 =ring [i -1];

         x1 = p1 ->x - p .x ;

         y1 = p1 ->y - p .y ;

         x2 = p2 ->x - p .x ;

         y2 = p2 ->y - p .y ;

 

         if (((y1 > 0) && (y2 <= 0)) ||

              ((y2 > 0) && (y1 <= 0)))

         {

              /*

                *  segment straddles x axis, so compute intersection.

                */

              xInt = RobustDeterminant ::signOfDet2x2 (x1 , y1 , x2 , y2 )

                   / (y2 - y1 );

 

              /*

                *  crosses ray if strictly positive intersection.

                */

              if (0.0 < xInt ) crossings ++;

         }

     }

 

     /*

       *  p is inside if number of crossings is odd.

       */

     if ((crossings % 2) == 1) return true ;

     return false ;

}

 

还有一个调用接口为:

代码位置: geos31/source/algorithm/RobustDeterminant.cpp

int RobustDeterminant ::signOfDet2x2 (double x1 ,double y1 ,double x2 ,double y2 )

{

     // returns -1 if the determinant is negative,

     // returns  1 if the determinant is positive,

     // retunrs  0 if the determinant is null.

     int sign =1;

     double swap ;

     double k ;

     long count =0;

 

     /*

     *  testing null entries

     */

     if ((x1 ==0.0) || (y2 ==0.0)) {

         if ((y1 ==0.0) || (x2 ==0.0)) {

              return 0;

         } else if (y1 >0) {

              if (x2 >0) {

                   return -sign ;

              } else {

                   return sign ;

              }

         } else {

              if (x2 >0) {

                   return sign ;

              } else {

                   return -sign ;

              }

         }

     }

     if ((y1 ==0.0) || (x2 ==0.0)) {

         if (y2 >0) {

              if (x1 >0) {

                   return sign ;

              } else {

                   return -sign ;

              }

         } else {

              if (x1 >0) {

                   return -sign ;

              } else {

                   return sign ;

              }

         }

     }

 

     /*

     *  making y coordinates positive and permuting the entries

     *  so that y2 is the biggest one

     */

     if (0.0<y1 ) {

         if (0.0<y2 ) {

              if (y1 <=y2 ) {

                   ;

              } else {

                   sign =-sign ;

                   swap =x1 ;

                   x1 =x2 ;

                  x2 =swap ;

                   swap =y1 ;

                   y1 =y2 ;

                   y2 =swap ;

              }

         } else {

              if (y1 <=-y2 ) {

                   sign =-sign ;

                   x2 =-x2 ;

                   y2 =-y2 ;

              } else {

                   swap =x1 ;

                   x1 =-x2 ;

                   x2 =swap ;

                   swap =y1 ;

                   y1 =-y2 ;

                   y2 =swap ;

              }

         }

     } else {

         if (0.0<y2 ) {

              if (-y1 <=y2 ) {

                   sign =-sign ;

                   x1 =-x1 ;

                   y1 =-y1 ;

              } else {

                   swap =-x1 ;

                   x1 =x2 ;

                   x2 =swap ;

                   swap =-y1 ;

                   y1 =y2 ;

                   y2 =swap ;

              }

         } else {

              if (y1 >=y2 ) {

                   x1 =-x1 ;

                   y1 =-y1 ;

                   x2 =-x2 ;

                   y2 =-y2 ;

              } else {

                   sign =-sign ;

                   swap =-x1 ;

                   x1 =-x2 ;

                   x2 =swap ;

                   swap =-y1 ;

                   y1 =-y2 ;

                   y2 =swap ;

              }

         }

     }

 

     /*

     *  making x coordinates positive

     */

     /*

     *  if |x2|<|x1| one can conclude

     */

     if (0.0<x1 ) {

         if (0.0<x2 ) {

              if (x1 <= x2 ) {

                   ;

              } else {

                   return sign ;

              }

         } else {

              return sign ;

         }

     } else {

         if (0.0<x2 ) {

              return -sign ;

         } else {

              if (x1 >= x2 ) {

                   sign =-sign ;

                   x1 =-x1 ;

                   x2 =-x2 ;

              } else {

                   return -sign ;

              }

         }

     }

 

     /*

     *  all entries strictly positive   x1 <= x2 and y1 <= y2

     */

     while (true ) {

         count =count +1;

         k =std ::floor (x2 /x1 );

         x2 =x2 -k *x1 ;

         y2 =y2 -k *y1 ;

 

         /*

         *  testing if R (new U2) is in U1 rectangle

         */

         if (y2 <0.0) {

              return -sign ;

         }

         if (y2 >y1 ) {

              return sign ;

         }

 

         /*

         *  finding R'

         */

         if (x1 >x2 +x2 ) {

              if (y1 <y2 +y2 ) {

                   return sign ;

              }

         } else {

              if (y1 >y2 +y2 ) {

                   return -sign ;

              } else {

                   x2 =x1 -x2 ;

                   y2 =y1 -y2 ;

                   sign =-sign ;

              }

         }

         if (y2 ==0.0) {

              if (x2 ==0.0) {

                   return 0;

              } else {

                   return -sign ;

              }

         }

         if (x2 ==0.0) {

              return sign ;

         }

 

         /*

         *  exchange 1 and 2 role.

         */

         k =std ::floor (x1 /x2 );

         x1 =x1 -k *x2 ;

         y1 =y1 -k *y2 ;

        

         /*

         *  testing if R (new U1) is in U2 rectangle

         */

         if (y1 <0.0) {

              return sign ;

         }

         if (y1 >y2 ) {

              return -sign ;

         }

 

         /*

         *  finding R'

         */

         if (x2 >x1 +x1 ) {

              if (y2 <y1 +y1 ) {

                   return -sign ;

              }

         } else {

              if (y2 >y1 +y1 ) {

                   return sign ;

              } else {

                   x1 =x2 -x1 ;

                   y1 =y2 -y1 ;

                   sign =-sign ;

              }

         }

         if (y1 ==0.0) {

              if (x1 ==0.0) {

                   return 0;

              } else {

                   return sign ;

              }

         }

         if (x1 ==0.0) {

              return -sign ;

         }

     }

}

 

 

利用数学公式的代码,摘自某 Blog

 

int  insidepolygon( int  vcount,POINT Polygon[],POINT q) 
...
    
int  c = 0,i,n; 
    LINESEG l1,l2; 
    
bool  bintersect_a,bonline1,bonline2,bonline3; 
    
double  r1,r2; 

    l1.s=q; 
    l1.e=q; 
    l1.e.x=
double (INF); 
    n =vcount; 
    
for  (i =0;i<vcount;i ++
    
...
        l2.s
= Polygon[i]; 
        l2.e
= Polygon[(i+1)%n]; 
        
if (online(l2,q)) return  1;  //  
如果点在边上,返回
         if  ((bintersect_a=intersect_A(l1,l2))||  //  
相交且不在端点  
            ( 
            (bonline1=online(l1,Polygon[(i+1)%n]))
&&  //  
第二个端点在射线上  
            ( 
            (!(bonline2=online(l1,Polygon[(i+2)%n])))
&&  /**/ /* 
前一个端点和后一个端点在射线两侧  */  
            ((r1
=multiply(Polygon[i],Polygon[(i+1)%n],l1.s) *multiply(Polygon[(i+1)%n],Polygon[(i+2)%n],l1.s)) > 0) ||    
            (bonline3=online(l1,Polygon[(i+2)%n]))
&&      /**/ /* 
下一条边是水平线,前一个端点和后一个端点在射线两侧   */  
            ((r2
=multiply(Polygon[i],Polygon[(i+2)%n],l1.s) * multiply(Polygon[(i+2)%n], 
            Polygon[(i+3)%n],l1.s))
> 0) 
        ))) 
        c++; 
    }
 
    if (c % 2 == 1) 
        
return  0; 
    
else  
        
return  2; 


//  (
线段 u v 相交 )&&( 交点不是双方的端点 时返回 true    
bool  intersect_A(LINESEG u,LINESEG v) 
...
    
return ((intersect(u,v))&&  
       ( !online(u,v.s))&& 
       (!online(u,v.e))&& 
       (!online(v,u.e))&& 
       (!online(v,u.s))); 


// 
如果线段 u v 相交 ( 包括相交在端点处 ) 时,返回 true 
bool  intersect(LINESEG u,LINESEG v) 
...
    
return ((max(u.s.x,u.e.x)>= min(v.s.x,v.e.x))&&                      // 排斥实验  
        (max(v.s.x,v.e.x)>= min(u.s.x,u.e.x)) && 
        (max(u.s.y,u.e.y)>=min(v.s.y,v.e.y))&& 
        (max(v.s.y,v.e.y)>=min(u.s.y,u.e.y))&& 
        (multiply(v.s,u.e,u.s)*multiply(u.e,v.e,u.s)>=
0)&&          //
跨立实验  
        (multiply(u.s,v.e,v.s)*multiply(v.e,u.e,v.s)>= 0 )); 


/**/ /* 
判断点 p 是否在线段 l 上,条件: (p 在线段 l 所在的直线 )&& ( p 在以线段 l 为对角线的矩形内 */  
bool  online(LINESEG l,POINT p) 
...
    
return ((multiply(l.e,p,l.s)==0) 
    &&( ( (p.x
-l.s.x)*(p.x-l.e.x)<= 0 )&& ( (p.y-l.s.y) *(p.y-l.e.y)<= 0  ) ) ); 


double  multiply(POINT sp,POINT ep,POINT op) 
...
    
return ((sp.x - op.x)*(ep.y-op.y)-(ep.x-op.x)*(sp.y-op.y)); 

从数学上来说,这段代码比上面两段代码都好看,采用向量积的形式判断三点关系,用这个运算为基础,推出 online intersect 操作,它们组合成 point in area 的代码.虽然这里也用了射线法,但是其中无数的乘法运算和未作任何筛选就进行的判断,使这段程序的效率几乎是三段程序中最低的。

 

还有 ogre1.6 代码:

代码位置:

ogre/OgreMain/src/OgrePolygon.cpp

       //-----------------------------------------------------------------------

       bool Polygon::isPointInside(const Vector3& point) const

       {

              // sum the angles

              Real anglesum = 0;

              size_t n = getVertexCount();

              for (size_t i = 0; i < n; i++)

              {

                     const Vector3& p1 = getVertex(i);

                     const Vector3& p2 = getVertex((i + 1) % n);

 

                     Vector3 v1 = p1 - point;

                     Vector3 v2 = p2 - point;

 

                     Real len1 = v1.length();

                     Real len2 = v2.length();

                     if (Math::RealEqual(len1 * len2, 0.0f, 1e-4f))

                     {

                            // We are on a vertex so consider this inside

                            return true;

                     }

                     else

                     {

                            Real costheta = v1.dotProduct(v2) / (len1 * len2);

                            anglesum += acos(costheta);

                     }

              }

              // result should be 2*PI if point is inside poly

              return Math::RealEqual(anglesum, Math::TWO_PI, 1e-4f);

       }

      

       该方法采用的是计算该点与多边形边的角之和是否等于 360 度,难道做过测试该算法效率优于射线法?

 

下面是 Graphics Gems 的方法这里仅仅列出方法链接,供读者去学习参考;

http://webdocs.cs.ualberta.ca/~graphics/books/GraphicsGems/gemsiv/ptpoly_haines/

http://webdocs.cs.ualberta.ca/~graphics/books/GraphicsGems/gemsiv/ptpoly_weiler/

其中,前者的链接是 Graphics Gems 第四卷 Eric Haines Point in Polygon Strategies 的源码,后者链接是 Graphics Gems 第四卷中 Kevin Weiler An Incremental Angle Point in Polygon Test 的源码。相比而言,前者的代码更全面一些,做了几个算法的比较,值得大家学习,揣摩。

 

另外在 cheungmine Blog 中用 C 语言实现了点在多边形内算法,但是源码中对于点正好落在多边形边上的极端情形,有可能得出 2 种不同的结果没有做进一步的处理(源码见参考资料 11 )。

 

通过这些算法代码的学习,可以看出这个最基本的算法其中蕴含了众多的思想,考核了一个合格程序员的理性思维。大家不妨自己实现一个判断点在多边形内算法,进行对比学习。

 

参考资料

1.        GRASS GIS GRASS GIS (Geographic Resources Analysis Support System http://grass.itc.it/

2.        CGAL, Computational Geometry Algorithms Library. http://www.cgal.org

3.        GEOS Geometry Engine - Open Source http://trac.osgeo.org/geos/

4.        Point in Polygon http://blog.csdn.net/Zricepig/archive/2007/03/14/1529393.aspx

5.        Point in Polygon Strategies http://erich.realtimerendering.com/ptinpoly/

6.        How Reliable Are Practical Point in Polygon Strategies?  http://wwwisg.cs.uni-magdeburg.de/ag/pointInPolygonReliability/

7.        ftp://ftp-graphics.stanford.edu/pub/Graphics/GraphicsGems/

8.        http://webdocs.cs.ualberta.ca/~graphics/books/GraphicsGems/gemsiv/ptpoly_haines/

9.        Graphics Gems Repositor http://tog.acm.org/resources/GraphicsGems/

10.    http://www.ogre3d.org/ http://ogre.cvs.sourceforge.net/ogre/

11.    http://blog.csdn.net/cheungmine/archive/2007/09/24/1798095.aspx

 

 

转载请注明:http://blog.csdn.net/wsh6759/article/details/5490951

 

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值