判定一点是否在多边形内

本文介绍了两种判断点是否在多边形内的算法:射线交点法和绕数法。射线交点法通过统计射线与多边形边的交点数量判定,而绕数法则计算多边形绕点的次数。针对特定测试点,不同算法可能得出不同结果,例如在某些实现中,点落在边上的情况可能会导致误判。

判断一点P是否在多边形内部的算法一般有两种:射线交点法(ray crossing number method),绕数法(windig number method)


射线交点法(ray crossing number method)
   以从此点为原点引一射线,然后统计此射线与多边形各边交点的数量。如果与射线相交的边的数量是偶数,则此点落在多变形外,否则落在多边形内。


绕数法(windig number method)
   统计多边形绕点P的次数(绕数)。如果这个绕数是0,则点在多边形内,否则点在多边形外。

[1]给出了射线交点法的C++语言的一个实现,根据[1]给出的代码,将点坐标数据类型由int转换为double

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <vector>

static double ESP = 0.0001;
double max( double a, double b )
{
	  return (a > b)? a:b;
} 
double min( double a, double b )
{
	  return (a < b)? a:b;
} 

class Grid
{
public:
	Grid()
	{
		_X = 0.0;
		_Y = 0.0;
		_Z = 0.0;
	}
	Grid( double X, double Y, double Z )
	{
		_X = X;
		_Y = Y;
		_Z = Z;
         }
	;
	virtual ~Grid()
	{
	}
	;
public:
	bool isSame(Grid theGrid )
	{
		bool result = false;
		if (fabs(_X - theGrid._X) > ESP)
		{
			return result;
		}
		if (fabs(_Y - theGrid._Y) > ESP)
		{
			return result;
		}
		if (fabs(_Z - theGrid._Z) > ESP)
		{
			return result;
		}
		result = true;
		return result;
	}
       void show()
      {
	   printf("[%f, %f, %f]\n", _X, _Y, _Z);
      }
public:
	double _X;
	double _Y;
	double _Z;
};

// http://www.geeksforgeeks.org/how-to-check-if-a-given-point-lies-inside-a-polygon/
// Define Infinite (Using INT_MAX caused overflow problems)
#define INF 10000
 
double max( double a, double b )
{
	  return (a > b)? a:b;
} 
double min( double a, double b )
{
	  return (a < b)? a:b;
}  
//struct Point
//{
//    int x;
//    int y;
//};
 
// http://www.geeksforgeeks.org/how-to-check-if-a-given-point-lies-inside-a-polygon/
// Given three colinear points p, q, r, the function checks if
// point q lies on line segment 'pr'
bool onSegment(Grid p, Grid q, Grid r)
{
    if (q._X <= max(p._X, r._X) && q._X >= min(p._X, r._X) &&
            q._Y <= max(p._Y, r._Y) && q._Y >= min(p._Y, r._Y))
        return true;
    return false;
}
 
// http://www.geeksforgeeks.org/how-to-check-if-a-given-point-lies-inside-a-polygon/
// To find orientation of ordered triplet (p, q, r).
// The function returns following values
// 0 --> p, q and r are colinear
// 1 --> Clockwise
// 2 --> Counterclockwise
int orientation(Grid p, Grid q, Grid r)
{
    int val = (q._Y - p._Y) * (r._X - q._X) -
              (q._X - p._X) * (r._Y - q._Y);
 
    if (val == 0) return 0;  // colinear
    return (val > 0)? 1: 2; // clock or counterclock wise
}
 
// http://www.geeksforgeeks.org/how-to-check-if-a-given-point-lies-inside-a-polygon/
// The function that returns true if line segment 'p1q1'
// and 'p2q2' intersect.
bool doIntersect(Grid p1, Grid q1, Grid p2, Grid q2)
{
    // Find the four orientations needed for general and
    // special cases
    int o1 = orientation(p1, q1, p2);
    int o2 = orientation(p1, q1, q2);
    int o3 = orientation(p2, q2, p1);
    int o4 = orientation(p2, q2, q1);
 
    // General case
    if (o1 != o2 && o3 != o4)
        return true;
 
    // Special Cases
    // p1, q1 and p2 are colinear and p2 lies on segment p1q1
    if (o1 == 0 && onSegment(p1, p2, q1)) return true;
 
    // p1, q1 and p2 are colinear and q2 lies on segment p1q1
    if (o2 == 0 && onSegment(p1, q2, q1)) return true;
 
    // p2, q2 and p1 are colinear and p1 lies on segment p2q2
    if (o3 == 0 && onSegment(p2, p1, q2)) return true;
 
     // p2, q2 and q1 are colinear and q1 lies on segment p2q2
    if (o4 == 0 && onSegment(p2, q1, q2)) return true;
 
    return false; // Doesn't fall in any of the above cases
}
 
// http://www.geeksforgeeks.org/how-to-check-if-a-given-point-lies-inside-a-polygon/
// Returns true if the point p lies inside the polygon[] with n vertices
bool isInside(std::vector<Grid> polygon,  Grid p)
{
    int n = polygon.size();
    // There must be at least 3 vertices in polygon[]
    if (n < 3)  return false;
 
    // Create a point for line segment from p to infinite
    Grid extreme = Grid(INF, p._Y, 0);
 
    // Count intersections of the above line with sides of polygon
    int count = 0, i = 0;
    do
    {
        int next = (i+1)%n;
 
        // Check if the line segment from 'p' to 'extreme' intersects
        // with the line segment from 'polygon[i]' to 'polygon[next]'
        if (doIntersect(polygon[i], polygon[next], p, extreme))
        {
            // If the point 'p' is colinear with line segment 'i-next',
            // then check if it lies on segment. If it lies, return true,
            // otherwise false
            if (orientation(polygon[i], p, polygon[next]) == 0)
               return onSegment(polygon[i], p, polygon[next]);
 
            count++;
        }
        i = next;
    } while (i != 0);
 
    // Return true if count is odd, false otherwise
    return count&1;  // Same as (count%2 == 1)
}
 

int main( int argc, char* argv )
{
    Grid P1( 10.000000, 20.000000, 10.000000 );                                                    
    Grid P2( -10.000000, 20.000000,  10.000000 );  
    Grid P3( -10.000000, 10.000000, -10.00 );                                                     
    Grid P4( 10.000000, -10.000000, 10.0  );                                                     

    std::vector<Grid>  polygon1 ;
    polygon1.push_back( P1  );
    polygon1.push_back( P2  );
    polygon1.push_back( P3  );
    polygon1.push_back( P4  ) ; 

    Grid section2P1( -10.000000, 10.000000, 10.000000);  
    isInside( polygon1, section2P1)? printf("Inside.\n"): printf("Outside.\n");
}

上述代码的算例中,测试点落在多边形的边上,[1]算法给出的结果是inside,

对于判定一点是否在多边形内,[2][3] 给出了绕数法的C++语言代码, 但对 这个算例给出的结果是outside。

[4]分别给出了射线交点法和绕数法的C++语言代码, 但对 这个算例给出的结果都是outside。下面是[4]的代码

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <map>


static double ESP = 0.01;
static double dotMultiply(double* v1, double* v2)
{
	double result = v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
	return result;
}

static void crossMultiply(double* v1, double* v2, double* result)
{
 
	result[0] = v1[1] * v2[2] - v1[2] * v2[1];
	result[1] = v1[2] * v2[0] - v1[0] * v2[2];
	result[2] = v1[0] * v2[1] - v1[1] * v2[0];
	return;
}


class Grid
{
public:
	Grid()
	{
		_ID = -1;
		_CoordID = 0;
		_X = 0.0;
		_Y = 0.0;
		_Z = 0.0;
	}
	Grid( double X, double Y, double Z )
	{
		_ID = -1;
		_CoordID = 0;
		_X = X;
		_Y = Y;
		_Z = Z;
         }
	;
	virtual ~Grid()
	{
	}
	;
public:
	bool isSame(Grid theGrid, double error)
	{
		bool result = false;
		if (fabs(_X - theGrid._X) > error)
		{
			return result;
		}
		if (fabs(_Y - theGrid._Y) > error)
		{
			return result;
		}
		if (fabs(_Z - theGrid._Z) > error)
		{
			return result;
		}
		if (_CoordID != theGrid._CoordID)
		{
			return result;
		}
		result = true;
		return result;
	}
       void show()
      {
	   printf("Grid %d: [%f, %f, %f]\n", _ID, _X, _Y, _Z);
      }
public:
	int _ID;
	int _CoordID;
	double _X;
	double _Y;
	double _Z;

};

//
//  http://geomalgorithms.com/a03-_inclusion.html
//

// Copyright 2000 softSurfer, 2012 Dan Sunday
// This code may be freely used and modified for any purpose
// providing that this copyright notice is included with it.
// SoftSurfer makes no warranty for this code, and cannot be held
// liable for any real or imagined damage resulting from its use.
// Users of this code must verify correctness for their application.
 

// a Point is defined by its coordinates {int x, y;}
//===================================================================
 

// isLeft(): tests if a point is Left|On|Right of an infinite line.
//    Input:  three points P0, P1, and P2
//    Return: >0 for P2 left of the line through P0 and P1
//            =0 for P2  on the line
//            <0 for P2  right of the line
//    See: Algorithm 1 "Area of Triangles and Polygons"
inline int
isLeft( Grid P0, Grid P1, Grid P2 )
{
    return ( (P1._X - P0._X) * (P2._Y - P0._Y)
            - (P2._X -  P0._X) * (P1._Y - P0._Y) );
}
//===================================================================


// cn_PnPoly(): crossing number test for a point in a polygon
//      Input:   P = a point,
//               V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
//      Return:  0 = outside, 1 = inside
// This code is patterned after [Franklin, 2000]
int cn_PnPoly( Grid P, std::vector<Grid> V, int n )
{
    int    cn = 0;    // the  crossing number counter

    // loop through all edges of the polygon
    for (int i=0; i<n; i++) {    // edge from V[i]  to V[i+1]
       if (((V[i]._Y <= P._Y) && (V[i+1]._Y > P._Y))     // an upward crossing
        || ((V[i]._Y > P._Y) && (V[i+1]._Y <=  P._Y))) { // a downward crossing
            // compute  the actual edge-ray intersect x-coordinate
            float vt = (float)(P._Y  - V[i]._Y) / (V[i+1]._Y - V[i]._Y);
            if (P._X <  V[i]._X + vt * (V[i+1]._X - V[i]._X)) // P._X < intersect
                 ++cn;   // a valid crossing of y=P._Y right of P._X
        }
    }
    return (cn&1);    // 0 if even (out), and 1 if  odd (in)

}
//===================================================================


// wn_PnPoly(): winding number test for a point in a polygon
//      Input:   P = a point,
//               V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
//      Return:  wn = the winding number (=0 only when P is outside)
int wn_PnPoly( Grid P, std::vector<Grid> V, int n )
{
    int    wn = 0;    // the  winding number counter

    // loop through all edges of the polygon
    for (int i=0; i<n; i++) {   // edge from V[i] to  V[i+1]
        if (V[i]._Y <= P._Y) {          // start y <= P._Y
            if (V[i+1]._Y  > P._Y)      // an upward crossing
                 if (isLeft( V[i], V[i+1], P) > 0)  // P left of  edge
                     ++wn;            // have  a valid up intersect
        }
        else {                        // start y > P._Y (no test needed)
            if (V[i+1]._Y  <= P._Y)     // a downward crossing
                 if (isLeft( V[i], V[i+1], P) < 0)  // P right of  edge
                     --wn;            // have  a valid down intersect
        }
    }
    return wn;
}
//===================================================================




int main( int argc, char* argv )
{
    //  
    Grid section1P3( -10.000000, 10.000000, -10.00 );                                                     
    Grid section1P4( 10.000000, -10.000000, 10.0  );                                                     
    Grid section1P1( 10.000000, 20.000000, 10.000000 );                                                    
    Grid section1P2( -10.000000, 20.000000,  10.000000 );  

    std::vector<Grid>  prism1BaseVertex ;
    prism1BaseVertex.push_back( section1P1  );
    prism1BaseVertex.push_back( section1P2  );
    prism1BaseVertex.push_back( section1P3  );
    prism1BaseVertex.push_back( section1P4  ) ; 

    Grid section2P1( -10.000000, 10.000000, 10.000000);  

    int r = wn_PnPoly( section2P1, prism1BaseVertex, prism1BaseVertex.size() );
    printf("winding number result = %d.\n", r );  


    r = cn_PnPoly( section2P1, prism1BaseVertex, prism1BaseVertex.size() );
    printf("crosing number result = %d.\n", r );  
}

Tarjan’s Algorithm to find Strongly Connected Components[4]

// A C++ program to find strongly connected components in a given
// directed graph using Tarjan's algorithm (single DFS)
#include<iostream>
#include <list>
#include <stack>
#define NIL -1
using namespace std;

double max( double a, double b )
{
	  return (a > b)? a:b;
} 
double min( double a, double b )
{
	  return (a < b)? a:b;
}  

// A class that represents an directed graph
class Graph
{
    int V;    // No. of vertices
    list<int> *adj;    // A dynamic array of adjacency lists

    // A Recursive DFS based function used by SCC()
    void SCCUtil(int u, int disc[], int low[],
                 stack<int> *st, bool stackMember[]);
public:
    Graph(int V);   // Constructor
    void addEdge(int v, int w);   // function to add an edge to graph
    void SCC();    // prints strongly connected components
};

Graph::Graph(int V)
{
    this->V = V;
    adj = new list<int>[V];
}

void Graph::addEdge(int v, int w)
{
    adj[v].push_back(w);
}

// A recursive function that finds and prints strongly connected
// components using DFS traversal
// u --> The vertex to be visited next
// disc[] --> Stores discovery times of visited vertices
// low[] -- >> earliest visited vertex (the vertex with minimum
//             discovery time) that can be reached from subtree
//             rooted with current vertex
// *st -- >> To store all the connected ancestors (could be part
//           of SCC)
// stackMember[] --> bit/index array for faster check whether
//                  a node is in stack
void Graph::SCCUtil(int u, int disc[], int low[], stack<int> *st,
                    bool stackMember[])
{
    // A static variable is used for simplicity, we can avoid use
    // of static variable by passing a pointer.
    static int time = 0;

    // Initialize discovery time and low value
    disc[u] = low[u] = ++time;
    st->push(u);
    stackMember[u] = true;

    // Go through all vertices adjacent to this
    list<int>::iterator i;
    for (i = adj[u].begin(); i != adj[u].end(); ++i)
    {
        int v = *i;  // v is current adjacent of 'u'

        // If v is not visited yet, then recur for it
        if (disc[v] == -1)
        {
            SCCUtil(v, disc, low, st, stackMember);

            // Check if the subtree rooted with 'v' has a
            // connection to one of the ancestors of 'u'
            // Case 1 (per above discussion on Disc and Low value)
            low[u]  = min(low[u], low[v]);
        }

        // Update low value of 'u' only of 'v' is still in stack
        // (i.e. it's a back edge, not cross edge).
        // Case 2 (per above discussion on Disc and Low value)
        else if (stackMember[v] == true)
            low[u]  = min(low[u], disc[v]);
    }

    // head node found, pop the stack and print an SCC
    int w = 0;  // To store stack extracted vertices
    if (low[u] == disc[u])
    {
        while (st->top() != u)
        {
            w = (int) st->top();
            cout << w << " ";
            stackMember[w] = false;
            st->pop();
        }
        w = (int) st->top();
        cout << w << "\n";
        stackMember[w] = false;
        st->pop();
    }
}

// The function to do DFS traversal. It uses SCCUtil()
void Graph::SCC()
{
    int *disc = new int[V];
    int *low = new int[V];
    bool *stackMember = new bool[V];
    stack<int> *st = new stack<int>();

    // Initialize disc and low, and stackMember arrays
    for (int i = 0; i < V; i++)
    {
        disc[i] = NIL;
        low[i] = NIL;
        stackMember[i] = false;
    }

    // Call the recursive helper function to find strongly
    // connected components in DFS tree with vertex 'i'
    for (int i = 0; i < V; i++)
        if (disc[i] == NIL)
            SCCUtil(i, disc, low, st, stackMember);
}

// Driver program to test above function
int main()
{
    cout << "\nSCCs in first graph \n";
    Graph g1(5);
    g1.addEdge(1, 0);
    g1.addEdge(0, 2);
    g1.addEdge(2, 1);
    g1.addEdge(0, 3);
    g1.addEdge(3, 4);
    g1.SCC();
 


    cout << "\nSCCs in second graph \n";
    Graph g2(4);
    g2.addEdge(0, 1);
    g2.addEdge(1, 2);
    g2.addEdge(2, 3);
    g2.SCC();

    cout << "\nSCCs in third graph \n";
    Graph g3(7);
    g3.addEdge(0, 1);
    g3.addEdge(1, 2);
    g3.addEdge(2, 0);
    g3.addEdge(1, 3);
    g3.addEdge(1, 4);
    g3.addEdge(1, 6);
    g3.addEdge(3, 5);
    g3.addEdge(4, 5);
    g3.SCC();

    cout << "\nSCCs in fourth graph \n";
    Graph g4(11);
    g4.addEdge(0,1);g4.addEdge(0,3);
    g4.addEdge(1,2);g4.addEdge(1,4);
    g4.addEdge(2,0);g4.addEdge(2,6);
    g4.addEdge(3,2);
    g4.addEdge(4,5);g4.addEdge(4,6);
    g4.addEdge(5,6);g4.addEdge(5,7);g4.addEdge(5,8);g4.addEdge(5,9);
    g4.addEdge(6,4);
    g4.addEdge(7,9);
    g4.addEdge(8,9);
    g4.addEdge(9,8);
    g4.SCC();

    cout << "\nSCCs in fifth graph \n";
    Graph g5(5);
    g5.addEdge(0,1);
    g5.addEdge(1,2);
    g5.addEdge(2,3);
    g5.addEdge(2,4);
    g5.addEdge(3,0);
    g5.addEdge(4,2);
    g5.SCC();

    return 0;
}

Triangle


Triangle is a 2D quality mesh generator and Delaunay triangulator. 


下载到源程序后,如果是Windows操作系统,还需要在triangle.h之前做些配置,如定义以下几个宏[8]:


#define REAL double
#define ANSI_DECLARATORS
#include "triangle.h"
#undef REAL
在triangle.c中定义宏:#define NO_TIMER。有了上面的宏定义,可以编译出一个triangle.exe程序了。如果要将triangle用在自己的程序中,还需要定义#define TRILIBRARY。更多宏定义可以参考源程序。




 Doubly connected edge list(DCEL)
 

    https://en.wikipedia.org/wiki/Doubly_connected_edge_list

    http://www.holmes3d.net/graphics/dcel/

    http://www.cnblogs.com/lihao102/archive/2013/04/14/3020238.html

    http://www.sintef.no/projectweb/geometry-toolkits/

    http://what-when-how.com/advanced-methods-in-computer-graphics/mesh-processing-advanced-methods-in-computer-graphics-part-2/ 

    http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/model/

     http://imr.sandia.gov/papers/imr22/IMR22_25_Dyedov.pdf

     http://cg.cs.tsinghua.edu.cn/people/~Yongjin/CAD13-exact-geodesic.pdf

    https://www.researchgate.net/publication/255983160_AHF_Array-based_half-facet_data_structures_for_mixed-dimensional_and_non-manifold_meshes

    http://www.industrial-geometry.at/events/strobl2006/2006-06steiner.pdf

    http://staff.ustc.edu.cn/~lgliu/Courses/DGP_2012_spring-summer/Courseware/DGP04_MeshDataStructure.pdf

    https://www2.cs.arizona.edu/classes/cs437/fall07/Lecture2.pdf

     http://www.iue.tuwien.ac.at/phd/halama/node110.html  

     https://github.com/PeddleSpam/PolygonGraph

Reference 

[1] http://www.geeksforgeeks.org/how-to-check-if-a-given-point-lies-inside-a-polygon/  
   http://www.dcs.gla.ac.uk/~pat/52233/slides/Geometry1x1.pdf

[2] https://stackoverflow.com/questions/11716268/point-in-polygon-algorithm
[3] http://alienryderflex.com/polygon/  

[4]  http://geomalgorithms.com/a03-_inclusion.html

[5]  Tarjan’s Algorithm to find Strongly Connected Components
    http://www.geeksforgeeks.org/tarjan-algorithm-find-strongly-connected-components/

[6] https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm

[7 http://www.ics.uci.edu/~eppstein/161/960220.html#sca 

[8] http://www.cnblogs.com/opencascade/p/3632705.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值