Fixed point-in-polygon test bugs. Connectivity now correctly recognizes zone outlines that have non-zero width

This commit is contained in:
Tomasz Włostowski 2017-05-04 15:47:12 +02:00
parent d8018ded0b
commit 655d851d48
2 changed files with 98 additions and 35 deletions

View File

@ -27,6 +27,7 @@
#include <geometry/seg.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_rect.h>
#include <vector>
#include <algorithm>
@ -87,6 +88,12 @@ private:
return VECTOR2I( px, py );
}
void stupid_test() const
{
for(int i = 0; i < 16;i++)
assert( poly2gridX(grid2polyX(i)) == i);
}
int grid2polyX( int x ) const
{
return rescale( x, m_bbox.GetWidth(), m_gridSize ) + m_bbox.GetPosition().x;
@ -146,6 +153,10 @@ private:
void build( const SHAPE_LINE_CHAIN& aPolyOutline, int gridSize )
{
m_outline = aPolyOutline;
//if (orientation(m_outline) < 0)
// m_outline = m_outline.Reverse();
m_bbox = m_outline.BBox();
m_gridSize = gridSize;
@ -190,21 +201,19 @@ private:
if( edgeSet[edge] == 1 )
{
if( dir.Dot( ref_h ) > 0 )
if( dir.Dot( ref_h ) < 0 )
{
flags |= LEAD_H;
}
else if( dir.Dot( ref_h ) < 0 )
else if( dir.Dot( ref_h ) > 0 )
{
flags |= TRAIL_H;
}
}
m_flags.push_back( flags );
if( !flags )
continue;
std::set<int> indices;
indices.insert( m_gridSize * poly2gridY( edge.A.y ) + poly2gridX( edge.A.x ) );
@ -217,7 +226,7 @@ private:
if( dir.x != 0 )
{
int gx0 = poly2gridX( edge.A.x ) + 1;
int gx0 = poly2gridX( edge.A.x );
int gx1 = poly2gridX( edge.B.x );
for( int x = gx0; x <= gx1; x++ )
@ -227,9 +236,9 @@ private:
int yy = poly2gridY( py );
indices.insert( m_gridSize * yy + x );
if( x > 0 )
if( x > 0 )
indices.insert( m_gridSize * yy + x - 1 );
}
}
@ -240,7 +249,7 @@ private:
if( dir.y != 0 )
{
int gy0 = poly2gridY( edge.A.y ) + 1;
int gy0 = poly2gridY( edge.A.y );
int gy1 = poly2gridY( edge.B.y );
for( int y = gy0; y <= gy1; y++ )
@ -250,8 +259,7 @@ private:
int xx = poly2gridX( px );
indices.insert( m_gridSize * y + xx );
if( y > 0 )
if( y > 0 )
indices.insert( m_gridSize * (y - 1) + xx );
}
}
@ -259,8 +267,10 @@ private:
for( auto idx : indices )
m_grid[idx].push_back( i );
}
}
bool inRange( int v1, int v2, int x ) const
{
if( v1 < v2 )
@ -286,44 +296,52 @@ private:
int nearest;
};
void scanCell( SCAN_STATE& state, const EDGE_LIST& cell, const VECTOR2I& aP ) const
void scanCell( SCAN_STATE& state, const EDGE_LIST& cell, const VECTOR2I& aP, int cx, int cy ) const
{
int cx0 = grid2polyX(cx);
int cx1 = grid2polyX(cx + 1);
for( auto index : cell )
{
const SEG& edge = m_outline.CSegment( index );
if( edge.A.y == edge.B.y ) // horizontal edge
continue;
if( m_flags[index] == 0 ) // a slit
continue;
if( m_flags[index] == 0 )
{
if ( aP.y == edge.A.y && inRange( edge.A.x, edge.B.x, aP.x ) ) // we belong to the outline
{
state.nearest = index;
state.dist_max = 0;
return;
} else {
continue;
}
}
if( inRange( edge.A.y, edge.B.y, aP.y ) )
{
int dist = 0;
int x0;
if( edge.A.y == aP.y )
{
dist = -(aP.x - edge.A.x);
x0 = edge.A.x;
}
else if( edge.B.y == aP.y )
{
dist = -(aP.x - edge.B.x);
x0 = edge.B.x;
}
else
{
const VECTOR2I e( edge.B - edge.A );
const VECTOR2I ff( 1, 0 );
const VECTOR2I ac( aP - edge.A );
auto d = ff.Cross( e );
auto q = e.Cross( ac );
using ecoord = VECTOR2I::extended_type;
dist = rescale( q, (ecoord) 1, d );
x0 = edge.A.x + rescale( ( edge.B.x - edge.A.x ), (aP.y - edge.A.y), (edge.B.y - edge.A.y ) );
}
if( x0 < cx0 || x0 > cx1 )
{
continue;
}
dist = aP.x - x0;
if( dist == 0 )
{
if( state.nearest_prev < 0 || state.nearest != index )
@ -359,7 +377,7 @@ public:
build( aPolyOutline, gridSize );
}
int ContainsPoint( const VECTOR2I& aP ) // const
int containsPoint( const VECTOR2I& aP ) // const
{
const auto gridPoint = poly2grid( aP );
@ -369,7 +387,7 @@ public:
SCAN_STATE state;
const EDGE_LIST& cell = m_grid[ m_gridSize * gridPoint.y + gridPoint.x ];
scanCell( state, cell, aP );
scanCell( state, cell, aP, gridPoint.x, gridPoint.y );
if( state.nearest < 0 )
{
@ -383,7 +401,7 @@ public:
if( xl >= 0 )
{
const EDGE_LIST& cell2 = m_grid[ m_gridSize * gridPoint.y + xl ];
scanCell( state, cell2, aP );
scanCell( state, cell2, aP, xl, gridPoint.y );
if( state.nearest >= 0 )
break;
@ -392,7 +410,7 @@ public:
if( xh < m_gridSize )
{
const EDGE_LIST& cell2 = m_grid[ m_gridSize * gridPoint.y + xh ];
scanCell( state, cell2, aP );
scanCell( state, cell2, aP, xh, gridPoint.y );
if( state.nearest >= 0 )
break;
@ -430,6 +448,49 @@ public:
}
}
bool checkClearance( const VECTOR2I& aP, int aClearance )
{
int gx0 = poly2gridX( aP.x - aClearance - 1);
int gx1 = poly2gridX( aP.x + aClearance + 1);
int gy0 = poly2gridY( aP.y - aClearance - 1);
int gy1 = poly2gridY( aP.y + aClearance + 1);
using ecoord = VECTOR2I::extended_type;
ecoord dist = (ecoord) aClearance * aClearance;
for ( int gx = gx0; gx <= gx1; gx++ )
{
for ( int gy = gy0; gy <= gy1; gy++ )
{
const auto& cell = m_grid [ m_gridSize * gy + gx];
for ( auto index : cell )
{
const auto& seg = m_outline.CSegment(index);
if ( seg.SquaredDistance(aP) <= dist )
return true;
}
}
}
return false;
}
int ContainsPoint( const VECTOR2I& aP, int aClearance = 0 ) // const
{
if( containsPoint(aP) )
return 1;
if( aClearance > 0 )
return checkClearance ( aP, aClearance );
return 0;
}
const BOX2I& BBox() const
{
return m_bbox;

View File

@ -622,12 +622,14 @@ public:
bool ContainsAnchor( const CN_ANCHOR_PTR anchor ) const
{
return m_cachedPoly->ContainsPoint( anchor->Pos() );
auto zone = static_cast<ZONE_CONTAINER*> ( Parent() );
return m_cachedPoly->ContainsPoint( anchor->Pos(), zone->GetMinThickness() );
}
bool ContainsPoint( const VECTOR2I p ) const
{
return m_cachedPoly->ContainsPoint( p );
auto zone = static_cast<ZONE_CONTAINER*> ( Parent() );
return m_cachedPoly->ContainsPoint( p, zone->GetMinThickness() );
}
const BOX2I& BBox() const