kicad/libs/kimath/include/geometry/poly_grid_partition.h

162 lines
5.0 KiB
C++

/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2016-2017 CERN
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __POLY_GRID_PARTITION_H
#define __POLY_GRID_PARTITION_H
#include <algorithm>
#include <functional>
#include <set>
#include <unordered_map>
#include <vector>
#include <geometry/seg.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_rect.h>
#include <math/vector2d.h>
/**
* Provide a fast test for point inside polygon.
*
* Takes a large poly and splits it into a grid of rectangular cells, forming a spatial hash table.
* Each cell contains only the edges that 'touch it' (any point of the edge belongs to the cell).
* Edges can be marked as leading or trailing. Leading edge indicates that space to the left of
* it (x-wise) is outside the polygon. Trailing edge, conversely, means space to the right is
* outside the polygon.
*
* The point inside check for point (p) works as follows:
* - determine the cell coordinates of (p) (poly2grid)
* - find the matching grid cell ( O(0), if the cell coordinates are outside the range, the point
* is not in the polygon ).
* - if the cell contains edges, find the first edge to the left or right of the point, whichever
* comes first.
* - if the edge to the left is the 'lead edge', the point is inside. if it's a trailing edge, the
* point is outside.
* - idem for the edge to the right of (p), just reverse the edge types.
* - if the cell doesn't contain any edges, scan horizontal cells to the left and right (switching
* sides with each iteration) until an edge if found.
*
* @note: The rescale_trunc() function is used for grid<->world coordinate conversion because it
* rounds towards 0 (not to nearest). It's important as rounding to nearest (which the
* standard rescale() function does) will shift the grid by half a cell.
*/
class POLY_GRID_PARTITION
{
public:
POLY_GRID_PARTITION( const SHAPE_LINE_CHAIN& aPolyOutline, int gridSize );
int ContainsPoint( const VECTOR2I& aP, int aClearance = 0 );
const BOX2I& BBox() const
{
return m_bbox;
}
private:
enum HASH_FLAG
{
LEAD_EDGE = 1,
TRAIL_EDGE = 2,
};
using EDGE_LIST = std::vector<int>;
template <class T>
inline void hash_combine( std::size_t& seed, const T& v )
{
std::hash<T> hasher;
seed ^= hasher( v ) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
struct segsEqual
{
bool operator()( const SEG& a, const SEG& b ) const
{
return (a.A == b.A && a.B == b.B) || (a.A == b.B && a.B == b.A);
}
};
struct segHash
{
std::size_t operator()( const SEG& a ) const
{
return a.A.x + a.B.x + a.A.y + a.B.y;
}
};
int containsPoint( const VECTOR2I& aP, bool debug = false ) const;
bool checkClearance( const VECTOR2I& aP, int aClearance );
int rescale_trunc( int aNumerator, int aValue, int aDenominator ) const;
// converts grid cell coordinates to the polygon coordinates
const VECTOR2I grid2poly( const VECTOR2I& p ) const;
int grid2polyX( int x ) const;
int grid2polyY( int y ) const;
const VECTOR2I poly2grid( const VECTOR2I& p ) const;
int poly2gridX( int x ) const;
int poly2gridY( int y ) const;
void build( const SHAPE_LINE_CHAIN& aPolyOutline, int gridSize );
bool inRange( int v1, int v2, int x ) const;
struct SCAN_STATE
{
SCAN_STATE()
{
dist_prev = INT_MAX;
dist_max = INT_MAX;
nearest = -1;
nearest_prev = -1;
};
int dist_prev;
int dist_max;
int nearest_prev;
int nearest;
};
void scanCell( SCAN_STATE& state, const EDGE_LIST& cell, const VECTOR2I& aP, int cx,
int cy ) const;
private:
int m_gridSize;
SHAPE_LINE_CHAIN m_outline;
BOX2I m_bbox;
std::vector<int> m_flags;
std::vector<EDGE_LIST> m_grid;
};
#endif