/* * 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 * * 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 #include #include #include #include #include #include #include #include /** * 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; template inline void hash_combine( std::size_t& seed, const T& v ) { std::hash 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 m_flags; std::vector m_grid; }; #endif