Fix slow selection time when calculating clearance
The clearance between two zones could be rather slow. This was in part
to trying to do triangle-triangle collisions between zones when we only
need outline collision and in part to the shape_line_chain collision
routine. The shape_line_chain collisions don't need to recreate
segments on each iteration and should instead create them once and using
this to check all collisions
Fixes https://gitlab.com/kicad/code/kicad/-/issues/17327
(cherry picked from commit 3cc1617f5a
)
This commit is contained in:
parent
b1677d5f47
commit
9be4bf0d14
|
@ -57,8 +57,19 @@ int SHAPE::GetClearance( const SHAPE* aOther ) const
|
|||
std::vector<const SHAPE*> a_shapes;
|
||||
std::vector<const SHAPE*> b_shapes;
|
||||
|
||||
GetIndexableSubshapes( a_shapes );
|
||||
aOther->GetIndexableSubshapes( b_shapes );
|
||||
/// POLY_SETs contain a bunch of polygons that are triangulated.
|
||||
/// But there are way more triangles than necessary for collision detection.
|
||||
/// Triangles check three vertices each but for the outline, we only need one.
|
||||
/// These are also fractured, so we don't need to worry about holes
|
||||
if( Type() == SHAPE_TYPE::SH_POLY_SET )
|
||||
a_shapes.push_back( &static_cast<const SHAPE_POLY_SET*>( this )->COutline( 0 ) );
|
||||
else
|
||||
GetIndexableSubshapes( a_shapes );
|
||||
|
||||
if( aOther->Type() == SHAPE_TYPE::SH_POLY_SET )
|
||||
b_shapes.push_back( &static_cast<const SHAPE_POLY_SET*>( aOther )->COutline( 0 ) );
|
||||
else
|
||||
aOther->GetIndexableSubshapes( b_shapes );
|
||||
|
||||
if( GetIndexableSubshapeCount() == 0 )
|
||||
a_shapes.push_back( this );
|
||||
|
|
|
@ -261,13 +261,18 @@ bool SHAPE_ARC::Collide( const SEG& aSeg, int aClearance, int* aActual, VECTOR2I
|
|||
candidatePts.push_back( aSeg.A );
|
||||
candidatePts.push_back( aSeg.B );
|
||||
|
||||
bool any_collides = false;
|
||||
|
||||
for( const VECTOR2I& candidate : candidatePts )
|
||||
{
|
||||
if( Collide( candidate, aClearance, aActual, aLocation ) )
|
||||
bool collides = Collide( candidate, aClearance, aActual, aLocation );
|
||||
any_collides |= collides;
|
||||
|
||||
if( collides && ( !aActual || *aActual == 0 ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return any_collides;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -304,52 +304,78 @@ static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_LINE_CH
|
|||
}
|
||||
else
|
||||
{
|
||||
for( size_t i = 0; i < aB.GetSegmentCount(); i++ )
|
||||
std::vector<SEG> a_segs;
|
||||
std::vector<SEG> b_segs;
|
||||
|
||||
for( size_t ii = 0; ii < aA.GetSegmentCount(); ii++ )
|
||||
{
|
||||
int collision_dist = 0;
|
||||
VECTOR2I pn;
|
||||
|
||||
if( aB.Type() == SH_LINE_CHAIN )
|
||||
if( aA.Type() != SH_LINE_CHAIN
|
||||
|| !static_cast<const SHAPE_LINE_CHAIN*>( &aA )->IsArcSegment( ii ) )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN* aB_line_chain = static_cast<const SHAPE_LINE_CHAIN*>( &aB );
|
||||
|
||||
// ignore arcs - we will collide these separately
|
||||
if( aB_line_chain->IsArcSegment( i ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
if( aA.Collide( aB.GetSegment( i ), aClearance,
|
||||
aActual || aLocation ? &collision_dist : nullptr,
|
||||
aLocation ? &pn : nullptr ) )
|
||||
{
|
||||
if( collision_dist < closest_dist )
|
||||
{
|
||||
nearest = pn;
|
||||
closest_dist = collision_dist;
|
||||
}
|
||||
|
||||
if( closest_dist == 0 )
|
||||
break;
|
||||
|
||||
// If we're not looking for aActual then any collision will do
|
||||
if( !aActual )
|
||||
break;
|
||||
a_segs.push_back( aA.GetSegment( ii ) );
|
||||
}
|
||||
}
|
||||
|
||||
if( aB.Type() == SH_LINE_CHAIN )
|
||||
for( size_t ii = 0; ii < aB.GetSegmentCount(); ii++ )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN* aB_line_chain = static_cast<const SHAPE_LINE_CHAIN*>( &aB );
|
||||
|
||||
for( size_t i = 0; i < aB_line_chain->ArcCount(); i++ )
|
||||
if( aB.Type() != SH_LINE_CHAIN
|
||||
|| !static_cast<const SHAPE_LINE_CHAIN*>( &aB )->IsArcSegment( ii ) )
|
||||
{
|
||||
const SHAPE_ARC& arc = aB_line_chain->Arc( i );
|
||||
b_segs.push_back( aB.GetSegment( ii ) );
|
||||
}
|
||||
}
|
||||
|
||||
// The arcs in the chain should have zero width
|
||||
wxASSERT_MSG( arc.GetWidth() == 0, wxT( "Invalid arc width - should be zero" ) );
|
||||
auto seg_sort = []( const SEG& a, const SEG& b )
|
||||
{
|
||||
return a.A.x < b.A.x || ( a.A.x == b.A.x && a.A.y < b.A.y );
|
||||
};
|
||||
|
||||
if( arc.Collide( &aA, aClearance, aActual, aLocation ) )
|
||||
return true;
|
||||
std::sort( a_segs.begin(), a_segs.end(), seg_sort );
|
||||
std::sort( b_segs.begin(), b_segs.end(), seg_sort );
|
||||
|
||||
for( const SEG& a_seg : a_segs )
|
||||
{
|
||||
for( const SEG& b_seg : b_segs )
|
||||
{
|
||||
int dist = 0;
|
||||
|
||||
if( a_seg.Collide( b_seg, aClearance, aActual || aLocation ? &dist : nullptr ) )
|
||||
{
|
||||
if( dist < closest_dist )
|
||||
{
|
||||
nearest = a_seg.NearestPoint( b_seg );
|
||||
closest_dist = dist;
|
||||
}
|
||||
|
||||
if( closest_dist == 0 )
|
||||
break;
|
||||
|
||||
// If we're not looking for aActual then any collision will do
|
||||
if( !aActual )
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( closest_dist > 0 && aActual )
|
||||
{
|
||||
std::vector<const SHAPE_LINE_CHAIN*> chains = {
|
||||
static_cast<const SHAPE_LINE_CHAIN*>( &aA ),
|
||||
static_cast<const SHAPE_LINE_CHAIN*>( &aB )
|
||||
};
|
||||
|
||||
for( int ii = 0; ii < 2; ii++ )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN* chain = chains[ii];
|
||||
const SHAPE_LINE_CHAIN* other = chains[( ii + 1 ) % 2];
|
||||
|
||||
for( size_t jj = 0; jj < chain->ArcCount(); jj++ )
|
||||
{
|
||||
const SHAPE_ARC& arc = chain->Arc( jj );
|
||||
|
||||
if( arc.Collide( other, aClearance, aActual, aLocation ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -540,11 +566,11 @@ static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_LINE_CHAIN& aB, int
|
|||
}
|
||||
else
|
||||
{
|
||||
int collision_dist = 0;
|
||||
VECTOR2I pn;
|
||||
|
||||
for( size_t i = 0; i < aB.GetSegmentCount(); i++ )
|
||||
{
|
||||
int collision_dist = 0;
|
||||
VECTOR2I pn;
|
||||
|
||||
// ignore arcs - we will collide these separately
|
||||
if( aB.IsArcSegment( i ) )
|
||||
continue;
|
||||
|
@ -575,8 +601,21 @@ static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_LINE_CHAIN& aB, int
|
|||
// The arcs in the chain should have zero width
|
||||
wxASSERT_MSG( arc.GetWidth() == 0, wxT( "Invalid arc width - should be zero" ) );
|
||||
|
||||
if( arc.Collide( &aA, aClearance, aActual, aLocation ) )
|
||||
return true;
|
||||
if( aA.Collide( &arc, aClearance, aActual || aLocation ? &collision_dist : nullptr,
|
||||
aLocation ? &pn : nullptr ) )
|
||||
{
|
||||
if( collision_dist < closest_dist )
|
||||
{
|
||||
nearest = pn;
|
||||
closest_dist = collision_dist;
|
||||
}
|
||||
|
||||
if( closest_dist == 0 )
|
||||
break;
|
||||
|
||||
if( !aActual )
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -655,6 +655,9 @@ bool SHAPE_LINE_CHAIN::Collide( const SEG& aSeg, int aClearance, int* aActual,
|
|||
return true;
|
||||
}
|
||||
|
||||
int dist = 0;
|
||||
int closest_dist = sqrt( closest_dist_sq );
|
||||
|
||||
// Collide arc segments
|
||||
for( size_t i = 0; i < ArcCount(); i++ )
|
||||
{
|
||||
|
@ -663,8 +666,28 @@ bool SHAPE_LINE_CHAIN::Collide( const SEG& aSeg, int aClearance, int* aActual,
|
|||
// The arcs in the chain should have zero width
|
||||
wxASSERT_MSG( arc.GetWidth() == 0, wxT( "Invalid arc width - should be zero" ) );
|
||||
|
||||
if( arc.Collide( aSeg, aClearance, aActual, aLocation ) )
|
||||
return true;
|
||||
if( arc.Collide( aSeg, aClearance, aActual || aLocation ? &dist : nullptr,
|
||||
aLocation ? &nearest : nullptr ) )
|
||||
{
|
||||
if( !aActual )
|
||||
return true;
|
||||
|
||||
if( dist < closest_dist )
|
||||
{
|
||||
closest_dist = dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( closest_dist_sq == 0 || closest_dist_sq < clearance_sq )
|
||||
{
|
||||
if( aLocation )
|
||||
*aLocation = nearest;
|
||||
|
||||
if( aActual )
|
||||
*aActual = sqrt( closest_dist_sq );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -1591,18 +1591,15 @@ int PCB_CONTROL::UpdateMessagePanel( const TOOL_EVENT& aEvent )
|
|||
msgItems.emplace_back( _( "Resolved clearance" ),
|
||||
m_frame->MessageTextFromValue( constraint.m_Value.Min() ) );
|
||||
|
||||
if( a->Type() != PCB_ZONE_T || b->Type() != PCB_ZONE_T )
|
||||
std::shared_ptr<SHAPE> a_shape( a_conn->GetEffectiveShape( layer ) );
|
||||
std::shared_ptr<SHAPE> b_shape( b_conn->GetEffectiveShape( layer ) );
|
||||
|
||||
int actual_clearance = a_shape->GetClearance( b_shape.get() );
|
||||
|
||||
if( actual_clearance > -1 && actual_clearance < std::numeric_limits<int>::max() )
|
||||
{
|
||||
std::shared_ptr<SHAPE> a_shape( a_conn->GetEffectiveShape( layer ) );
|
||||
std::shared_ptr<SHAPE> b_shape( b_conn->GetEffectiveShape( layer ) );
|
||||
|
||||
int actual_clearance = a_shape->GetClearance( b_shape.get() );
|
||||
|
||||
if( actual_clearance > -1 && actual_clearance < std::numeric_limits<int>::max() )
|
||||
{
|
||||
msgItems.emplace_back( _( "Actual clearance" ),
|
||||
m_frame->MessageTextFromValue( actual_clearance ) );
|
||||
}
|
||||
msgItems.emplace_back( _( "Actual clearance" ),
|
||||
m_frame->MessageTextFromValue( actual_clearance ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ set( QA_KIMATH_SRCS
|
|||
geometry/test_shape_poly_set_distance.cpp
|
||||
geometry/test_shape_poly_set_iterator.cpp
|
||||
geometry/test_shape_line_chain.cpp
|
||||
geometry/test_shape_line_chain_collision.cpp
|
||||
|
||||
math/test_box2.cpp
|
||||
math/test_matrix3x3.cpp
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2017 CERN
|
||||
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <qa_utils/wx_utils/unit_test_utils.h>
|
||||
|
||||
#include <geometry/shape.h>
|
||||
#include <geometry/shape_arc.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
|
||||
#include "fixtures_geometry.h"
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( SHAPE_LINE_CHAIN_COLLIDE_TEST )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( Collide_LineToLine )
|
||||
{
|
||||
SHAPE_LINE_CHAIN lineA;
|
||||
lineA.Append( VECTOR2I( 0, 0 ) );
|
||||
lineA.Append( VECTOR2I( 10, 0 ) );
|
||||
|
||||
SHAPE_LINE_CHAIN lineB;
|
||||
lineB.Append( VECTOR2I( 5, 5 ) );
|
||||
lineB.Append( VECTOR2I( 5, -5 ) );
|
||||
|
||||
VECTOR2I location;
|
||||
int actual = 0;
|
||||
bool collided = static_cast<SHAPE*>( &lineA )->Collide( &lineB, 0, &actual, &location );
|
||||
|
||||
BOOST_CHECK( collided );
|
||||
BOOST_CHECK( actual == 0 );
|
||||
BOOST_CHECK_MESSAGE( location == VECTOR2I( 5, 0 ), "Expected: " << VECTOR2I( 5, 0 ) << " Actual: " << location );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( Collide_LineToArc )
|
||||
{
|
||||
SHAPE_LINE_CHAIN lineA;
|
||||
lineA.Append( VECTOR2I( 0, 0 ) );
|
||||
lineA.Append( VECTOR2I( 10, 0 ) );
|
||||
|
||||
SHAPE_LINE_CHAIN arcB;
|
||||
arcB.Append( SHAPE_ARC( VECTOR2I( 5, 5 ), VECTOR2I( 6, 4 ), VECTOR2I( 7, 0 ), 0 ) );
|
||||
|
||||
VECTOR2I location;
|
||||
int actual = 0;
|
||||
bool collided = static_cast<SHAPE*>( &lineA )->Collide( &arcB, 0, &actual, &location );
|
||||
|
||||
BOOST_CHECK( collided );
|
||||
BOOST_CHECK( actual == 0 );
|
||||
BOOST_CHECK_MESSAGE( location == VECTOR2I( 7, 0 ), "Expected: " << VECTOR2I( 7, 0 ) << " Actual: " << location );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( Collide_ArcToArc )
|
||||
{
|
||||
SHAPE_LINE_CHAIN arcA;
|
||||
arcA.Append( SHAPE_ARC( VECTOR2I( 0, 0 ), VECTOR2I( 10, 0 ), VECTOR2I( 5, 5 ), 0 ) );
|
||||
|
||||
SHAPE_LINE_CHAIN arcB;
|
||||
arcB.Append( SHAPE_ARC( VECTOR2I( 5, 5 ), VECTOR2I( 5, -5 ), VECTOR2I( 10, 0 ), 0 ) );
|
||||
|
||||
VECTOR2I location;
|
||||
int actual = 0;
|
||||
bool collided = static_cast<SHAPE*>( &arcA )->Collide( &arcB, 0, &actual, &location );
|
||||
|
||||
BOOST_CHECK( collided );
|
||||
BOOST_CHECK( actual == 0 );
|
||||
BOOST_CHECK_MESSAGE( location == VECTOR2I( 5, 5 ), "Expected: " << VECTOR2I( 5, 5 ) << " Actual: " << location );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( Collide_WithClearance )
|
||||
{
|
||||
SHAPE_LINE_CHAIN lineA;
|
||||
lineA.Append( VECTOR2I( 0, 0 ) );
|
||||
lineA.Append( VECTOR2I( 10, 0 ) );
|
||||
|
||||
SHAPE_LINE_CHAIN lineB;
|
||||
lineB.Append( VECTOR2I( 5, 6 ) );
|
||||
lineB.Append( VECTOR2I( -5, 6 ) );
|
||||
|
||||
VECTOR2I location;
|
||||
int actual = 0;
|
||||
bool collided = static_cast<SHAPE*>( &lineA )->Collide( &lineB, 7, &actual, &location );
|
||||
|
||||
BOOST_CHECK( collided );
|
||||
BOOST_CHECK_MESSAGE( actual == 6, "Expected: " << 6 << " Actual: " << actual );
|
||||
BOOST_CHECK_MESSAGE( location == VECTOR2I( 0, 0 ), "Expected: " << VECTOR2I( 0, 0 ) << " Actual: " << location );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( Collide_NoClearance )
|
||||
{
|
||||
SHAPE_LINE_CHAIN lineA;
|
||||
lineA.Append( VECTOR2I( 0, 0 ) );
|
||||
lineA.Append( VECTOR2I( 10, 0 ) );
|
||||
|
||||
SHAPE_LINE_CHAIN lineB;
|
||||
lineB.Append( VECTOR2I( 5, 6 ) );
|
||||
lineB.Append( VECTOR2I( -5, 6 ) );
|
||||
|
||||
VECTOR2I location;
|
||||
int actual = 0;
|
||||
bool collided = static_cast<SHAPE*>( &lineA )->Collide( &lineB, 0, &actual, &location );
|
||||
|
||||
BOOST_CHECK( !collided );
|
||||
BOOST_CHECK_MESSAGE( actual == 0, "Expected: " << 0 << " Actual: " << actual );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
Loading…
Reference in New Issue