Be smarter about checking SHAPE_POLY_SET intersect

The segment iterator creates segments when needed, so using it in a dual
loop creates NlgN more copies than we need.  This shifts to a single
copy algorithm that then uses a sorting to preemptively abort the search
once the segments are outside of the search bounding box

Fixes https://gitlab.com/kicad/code/kicad/issues/13191
This commit is contained in:
Seth Hillbrand 2022-12-18 07:57:55 -08:00
parent ca2dde23d0
commit 035ff27b76
2 changed files with 35 additions and 8 deletions

View File

@ -358,6 +358,13 @@ public:
return SEG( B, A );
}
bool IsAdjacent( SEG& aOther ) const
{
int diff = m_index - aOther.m_index;
return diff == 1 || diff == -1;
}
///< Returns the center point of the line
VECTOR2I Center() const
{

View File

@ -463,23 +463,43 @@ bool SHAPE_POLY_SET::GetNeighbourIndexes( int aGlobalIndex, int* aPrevious, int*
bool SHAPE_POLY_SET::IsPolygonSelfIntersecting( int aPolygonIndex ) const
{
CONST_SEGMENT_ITERATOR iterator = CIterateSegmentsWithHoles( aPolygonIndex );
CONST_SEGMENT_ITERATOR innerIterator;
std::vector<SEG> segments;
segments.reserve( FullPointCount() );
for( iterator = CIterateSegmentsWithHoles( aPolygonIndex ); iterator; iterator++ )
for( CONST_SEGMENT_ITERATOR it = CIterateSegmentsWithHoles( aPolygonIndex ); it; it++ )
segments.emplace_back( *it );
std::sort( segments.begin(), segments.end(), []( const SEG& a, const SEG& b )
{
int min_a_x = std::min( a.A.x, a.B.x );
int min_b_x = std::min( b.A.x, b.B.x );
return min_a_x < min_b_x || ( min_a_x == min_b_x && std::min( a.A.y, a.B.y ) < std::min( b.A.y, b.B.y ) );
} );
for( auto it = segments.begin(); it != segments.end(); ++it )
{
SEG firstSegment = *iterator;
SEG& firstSegment = *it;
// Iterate through all remaining segments.
innerIterator = iterator;
auto innerIterator = it;
int max_x = std::max( firstSegment.A.x, firstSegment.B.x );
int max_y = std::max( firstSegment.A.y, firstSegment.B.y );
// Start in the next segment, we don't want to check collision between a segment and itself
for( innerIterator++; innerIterator; innerIterator++ )
for( innerIterator++; innerIterator != segments.end(); innerIterator++ )
{
SEG secondSegment = *innerIterator;
SEG& secondSegment = *innerIterator;
int min_x = std::min( secondSegment.A.x, secondSegment.B.x );
int min_y = std::min( secondSegment.A.y, secondSegment.B.y );
// We are ordered in minimum point order, so checking the static max (first segment) against
// the ordered min will tell us if any of the following segments are withing the BBox
if( max_x < min_x || ( max_x == min_x && max_y < min_y ) )
break;
// Check whether the two segments built collide, only when they are not adjacent.
if( !iterator.IsAdjacent( innerIterator ) && firstSegment.Collide( secondSegment, 0 ) )
if( !firstSegment.IsAdjacent( secondSegment ) && !firstSegment.Collide( secondSegment, 0 ) )
return true;
}
}