PNS: Use exact hulls for walkaround path generation
When we generate hulls, by default we subtract the clearance epsilon to prevent false collisions in the router that wouldn't be flagged by DRC. However, we need to use the actual hull with no epsilon when generating hulls for pathfinding in the walkaround system. Without this change, it is possible for the walkaround to generate a valid-seeming path that results in a DRC violation, for example when dragging a drag against a board edge. Fixes https://gitlab.com/kicad/code/kicad/-/issues/10536 Fixes https://gitlab.com/kicad/code/kicad/-/issues/11365 Fixes https://gitlab.com/kicad/code/kicad/-/issues/10710
This commit is contained in:
parent
2553da38b4
commit
2a91ba35c1
|
@ -108,24 +108,30 @@ public:
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if( m_linkedItems.Size() == 3
|
// There will be multiple VVIAs on joints between two locked segments, because we
|
||||||
&& m_linkedItems.Count( SEGMENT_T | ARC_T ) == 2
|
// naively add a VVIA to each end of a locked segment.
|
||||||
&& m_linkedItems.Count( VIA_T ) == 1 )
|
else if( ( m_linkedItems.Size() - m_linkedItems.Count( SEGMENT_T | ARC_T ) )
|
||||||
|
== m_linkedItems.Count( VIA_T ) )
|
||||||
{
|
{
|
||||||
const VIA* via = nullptr;
|
const VIA* via = nullptr;
|
||||||
|
bool hasNonVirtualVia = false;
|
||||||
|
|
||||||
for( const ITEM* item : m_linkedItems.CItems() )
|
for( const ITEM* item : m_linkedItems.CItems() )
|
||||||
{
|
{
|
||||||
if( item->Kind() == VIA_T )
|
if( item->Kind() == VIA_T )
|
||||||
{
|
{
|
||||||
via = static_cast<const VIA*>( item );
|
via = static_cast<const VIA*>( item );
|
||||||
break;
|
|
||||||
|
hasNonVirtualVia = !via->IsVirtual();
|
||||||
|
|
||||||
|
if( hasNonVirtualVia )
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert( via );
|
assert( via );
|
||||||
|
|
||||||
if( !via || !via->IsVirtual() )
|
if( !via || hasNonVirtualVia )
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -72,9 +72,12 @@ public:
|
||||||
PNS_PCBNEW_RULE_RESOLVER( BOARD* aBoard, PNS::ROUTER_IFACE* aRouterIface );
|
PNS_PCBNEW_RULE_RESOLVER( BOARD* aBoard, PNS::ROUTER_IFACE* aRouterIface );
|
||||||
virtual ~PNS_PCBNEW_RULE_RESOLVER();
|
virtual ~PNS_PCBNEW_RULE_RESOLVER();
|
||||||
|
|
||||||
virtual int Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB ) override;
|
virtual int Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
|
||||||
virtual int HoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB ) override;
|
bool aUseClearanceEpsilon = true ) override;
|
||||||
virtual int HoleToHoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB ) override;
|
virtual int HoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
|
||||||
|
bool aUseClearanceEpsilon = true ) override;
|
||||||
|
virtual int HoleToHoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
|
||||||
|
bool aUseClearanceEpsilon = true ) override;
|
||||||
|
|
||||||
virtual int DpCoupledNet( int aNet ) override;
|
virtual int DpCoupledNet( int aNet ) override;
|
||||||
virtual int DpNetPolarity( int aNet ) override;
|
virtual int DpNetPolarity( int aNet ) override;
|
||||||
|
@ -112,9 +115,11 @@ private:
|
||||||
PCB_VIA m_dummyVias[2];
|
PCB_VIA m_dummyVias[2];
|
||||||
int m_clearanceEpsilon;
|
int m_clearanceEpsilon;
|
||||||
|
|
||||||
std::map<std::pair<const PNS::ITEM*, const PNS::ITEM*>, int> m_clearanceCache;
|
typedef std::tuple<const PNS::ITEM*, const PNS::ITEM*, bool> CLEARANCE_CACHE_KEY;
|
||||||
std::map<std::pair<const PNS::ITEM*, const PNS::ITEM*>, int> m_holeClearanceCache;
|
|
||||||
std::map<std::pair<const PNS::ITEM*, const PNS::ITEM*>, int> m_holeToHoleClearanceCache;
|
std::map<CLEARANCE_CACHE_KEY, int> m_clearanceCache;
|
||||||
|
std::map<CLEARANCE_CACHE_KEY, int> m_holeClearanceCache;
|
||||||
|
std::map<CLEARANCE_CACHE_KEY, int> m_holeToHoleClearanceCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -298,13 +303,15 @@ bool PNS_PCBNEW_RULE_RESOLVER::QueryConstraint( PNS::CONSTRAINT_TYPE aType,
|
||||||
|
|
||||||
void PNS_PCBNEW_RULE_RESOLVER::ClearCacheForItem( const PNS::ITEM* aItem )
|
void PNS_PCBNEW_RULE_RESOLVER::ClearCacheForItem( const PNS::ITEM* aItem )
|
||||||
{
|
{
|
||||||
m_clearanceCache.erase( std::make_pair( aItem, nullptr ) );
|
m_clearanceCache.erase( std::make_tuple( aItem, nullptr, false ) );
|
||||||
|
m_clearanceCache.erase( std::make_tuple( aItem, nullptr, true ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB )
|
int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
|
||||||
|
bool aUseClearanceEpsilon )
|
||||||
{
|
{
|
||||||
std::pair<const PNS::ITEM*, const PNS::ITEM*> key( aA, aB );
|
CLEARANCE_CACHE_KEY key( aA, aB, aUseClearanceEpsilon );
|
||||||
auto it = m_clearanceCache.find( key );
|
auto it = m_clearanceCache.find( key );
|
||||||
|
|
||||||
if( it != m_clearanceCache.end() )
|
if( it != m_clearanceCache.end() )
|
||||||
|
@ -322,7 +329,7 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a
|
||||||
if( isCopper( aA ) && ( !aB || isCopper( aB ) ) )
|
if( isCopper( aA ) && ( !aB || isCopper( aB ) ) )
|
||||||
{
|
{
|
||||||
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, layer, &constraint ) )
|
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, layer, &constraint ) )
|
||||||
rv = constraint.m_Value.Min() - m_clearanceEpsilon;
|
rv = constraint.m_Value.Min();
|
||||||
}
|
}
|
||||||
|
|
||||||
if( isEdge( aA ) || ( aB && isEdge( aB ) ) )
|
if( isEdge( aA ) || ( aB && isEdge( aB ) ) )
|
||||||
|
@ -330,18 +337,22 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a
|
||||||
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE, aA, aB, layer, &constraint ) )
|
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE, aA, aB, layer, &constraint ) )
|
||||||
{
|
{
|
||||||
if( constraint.m_Value.Min() > rv )
|
if( constraint.m_Value.Min() > rv )
|
||||||
rv = constraint.m_Value.Min() - m_clearanceEpsilon;
|
rv = constraint.m_Value.Min();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( aUseClearanceEpsilon )
|
||||||
|
rv -= m_clearanceEpsilon;
|
||||||
|
|
||||||
m_clearanceCache[ key ] = rv;
|
m_clearanceCache[ key ] = rv;
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int PNS_PCBNEW_RULE_RESOLVER::HoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB )
|
int PNS_PCBNEW_RULE_RESOLVER::HoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
|
||||||
|
bool aUseClearanceEpsilon )
|
||||||
{
|
{
|
||||||
std::pair<const PNS::ITEM*, const PNS::ITEM*> key( aA, aB );
|
CLEARANCE_CACHE_KEY key( aA, aB, aUseClearanceEpsilon );
|
||||||
auto it = m_holeClearanceCache.find( key );
|
auto it = m_holeClearanceCache.find( key );
|
||||||
|
|
||||||
if( it != m_holeClearanceCache.end() )
|
if( it != m_holeClearanceCache.end() )
|
||||||
|
@ -357,16 +368,20 @@ int PNS_PCBNEW_RULE_RESOLVER::HoleClearance( const PNS::ITEM* aA, const PNS::ITE
|
||||||
layer = aB->Layer();
|
layer = aB->Layer();
|
||||||
|
|
||||||
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE, aA, aB, layer, &constraint ) )
|
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE, aA, aB, layer, &constraint ) )
|
||||||
rv = constraint.m_Value.Min() - m_clearanceEpsilon;
|
rv = constraint.m_Value.Min();
|
||||||
|
|
||||||
|
if( aUseClearanceEpsilon )
|
||||||
|
rv -= m_clearanceEpsilon;
|
||||||
|
|
||||||
m_holeClearanceCache[ key ] = rv;
|
m_holeClearanceCache[ key ] = rv;
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int PNS_PCBNEW_RULE_RESOLVER::HoleToHoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB )
|
int PNS_PCBNEW_RULE_RESOLVER::HoleToHoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
|
||||||
|
bool aUseClearanceEpsilon )
|
||||||
{
|
{
|
||||||
std::pair<const PNS::ITEM*, const PNS::ITEM*> key( aA, aB );
|
CLEARANCE_CACHE_KEY key( aA, aB, aUseClearanceEpsilon );
|
||||||
auto it = m_holeToHoleClearanceCache.find( key );
|
auto it = m_holeToHoleClearanceCache.find( key );
|
||||||
|
|
||||||
if( it != m_holeToHoleClearanceCache.end() )
|
if( it != m_holeToHoleClearanceCache.end() )
|
||||||
|
@ -382,7 +397,10 @@ int PNS_PCBNEW_RULE_RESOLVER::HoleToHoleClearance( const PNS::ITEM* aA, const PN
|
||||||
layer = aB->Layer();
|
layer = aB->Layer();
|
||||||
|
|
||||||
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE, aA, aB, layer, &constraint ) )
|
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE, aA, aB, layer, &constraint ) )
|
||||||
rv = constraint.m_Value.Min() - m_clearanceEpsilon;
|
rv = constraint.m_Value.Min();
|
||||||
|
|
||||||
|
if( aUseClearanceEpsilon )
|
||||||
|
rv -= m_clearanceEpsilon;
|
||||||
|
|
||||||
m_holeToHoleClearanceCache[ key ] = rv;
|
m_holeToHoleClearanceCache[ key ] = rv;
|
||||||
return rv;
|
return rv;
|
||||||
|
|
|
@ -99,7 +99,7 @@ NODE::~NODE()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int NODE::GetClearance( const ITEM* aA, const ITEM* aB ) const
|
int NODE::GetClearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilon ) const
|
||||||
{
|
{
|
||||||
if( !m_ruleResolver )
|
if( !m_ruleResolver )
|
||||||
return 100000;
|
return 100000;
|
||||||
|
@ -107,11 +107,11 @@ int NODE::GetClearance( const ITEM* aA, const ITEM* aB ) const
|
||||||
if( aA->IsVirtual() || aB->IsVirtual() )
|
if( aA->IsVirtual() || aB->IsVirtual() )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return m_ruleResolver->Clearance( aA, aB );
|
return m_ruleResolver->Clearance( aA, aB, aUseClearanceEpsilon );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int NODE::GetHoleClearance( const ITEM* aA, const ITEM* aB ) const
|
int NODE::GetHoleClearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilon ) const
|
||||||
{
|
{
|
||||||
if( !m_ruleResolver )
|
if( !m_ruleResolver )
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -119,11 +119,11 @@ int NODE::GetHoleClearance( const ITEM* aA, const ITEM* aB ) const
|
||||||
if( aA->IsVirtual() || aB->IsVirtual() )
|
if( aA->IsVirtual() || aB->IsVirtual() )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return m_ruleResolver->HoleClearance( aA, aB );
|
return m_ruleResolver->HoleClearance( aA, aB, aUseClearanceEpsilon );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int NODE::GetHoleToHoleClearance( const ITEM* aA, const ITEM* aB ) const
|
int NODE::GetHoleToHoleClearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilon ) const
|
||||||
{
|
{
|
||||||
if( !m_ruleResolver )
|
if( !m_ruleResolver )
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -131,7 +131,7 @@ int NODE::GetHoleToHoleClearance( const ITEM* aA, const ITEM* aB ) const
|
||||||
if( aA->IsVirtual() || aB->IsVirtual() )
|
if( aA->IsVirtual() || aB->IsVirtual() )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return m_ruleResolver->HoleToHoleClearance( aA, aB );
|
return m_ruleResolver->HoleToHoleClearance( aA, aB, aUseClearanceEpsilon );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -295,7 +295,8 @@ int NODE::QueryColliding( const ITEM* aItem, NODE::OBSTACLES& aObstacles, int aK
|
||||||
|
|
||||||
|
|
||||||
NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask,
|
NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask,
|
||||||
const std::set<ITEM*>* aRestrictedSet )
|
const std::set<ITEM*>* aRestrictedSet,
|
||||||
|
bool aUseClearanceEpsilon )
|
||||||
{
|
{
|
||||||
OBSTACLES obstacleList;
|
OBSTACLES obstacleList;
|
||||||
obstacleList.reserve( 100 );
|
obstacleList.reserve( 100 );
|
||||||
|
@ -345,8 +346,10 @@ NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask,
|
||||||
if( aRestrictedSet && aRestrictedSet->find( obstacle.m_item ) == aRestrictedSet->end() )
|
if( aRestrictedSet && aRestrictedSet->find( obstacle.m_item ) == aRestrictedSet->end() )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int clearance = GetClearance( obstacle.m_item, aLine ) + aLine->Width() / 2;
|
int clearance =
|
||||||
obstacleHull = obstacle.m_item->Hull( clearance + PNS_HULL_MARGIN, 0, layer );
|
GetClearance( obstacle.m_item, aLine, aUseClearanceEpsilon ) + aLine->Width() / 2;
|
||||||
|
|
||||||
|
obstacleHull = obstacle.m_item->Hull( clearance, 0, layer );
|
||||||
//debugDecorator->AddLine( obstacleHull, 2, 40000, "obstacle-hull-test" );
|
//debugDecorator->AddLine( obstacleHull, 2, 40000, "obstacle-hull-test" );
|
||||||
//debugDecorator->AddLine( aLine->CLine(), 5, 40000, "obstacle-test-line" );
|
//debugDecorator->AddLine( aLine->CLine(), 5, 40000, "obstacle-test-line" );
|
||||||
|
|
||||||
|
@ -373,7 +376,7 @@ NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask,
|
||||||
if( holeClearance > viaClearance )
|
if( holeClearance > viaClearance )
|
||||||
viaClearance = holeClearance;
|
viaClearance = holeClearance;
|
||||||
|
|
||||||
obstacleHull = obstacle.m_item->Hull( viaClearance + PNS_HULL_MARGIN, 0, layer );
|
obstacleHull = obstacle.m_item->Hull( viaClearance, 0, layer );
|
||||||
//debugDecorator->AddLine( obstacleHull, 3 );
|
//debugDecorator->AddLine( obstacleHull, 3 );
|
||||||
|
|
||||||
intersectingPts.clear();
|
intersectingPts.clear();
|
||||||
|
@ -388,7 +391,7 @@ NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask,
|
||||||
if( obstacle.m_item->Hole() )
|
if( obstacle.m_item->Hole() )
|
||||||
{
|
{
|
||||||
clearance = GetHoleClearance( obstacle.m_item, aLine ) + aLine->Width() / 2;
|
clearance = GetHoleClearance( obstacle.m_item, aLine ) + aLine->Width() / 2;
|
||||||
obstacleHull = obstacle.m_item->HoleHull( clearance + PNS_HULL_MARGIN, 0, layer );
|
obstacleHull = obstacle.m_item->HoleHull( clearance, 0, layer );
|
||||||
//debugDecorator->AddLine( obstacleHull, 4 );
|
//debugDecorator->AddLine( obstacleHull, 4 );
|
||||||
|
|
||||||
intersectingPts.clear();
|
intersectingPts.clear();
|
||||||
|
|
|
@ -79,9 +79,11 @@ class RULE_RESOLVER
|
||||||
public:
|
public:
|
||||||
virtual ~RULE_RESOLVER() {}
|
virtual ~RULE_RESOLVER() {}
|
||||||
|
|
||||||
virtual int Clearance( const ITEM* aA, const ITEM* aB ) = 0;
|
virtual int Clearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilon = true ) = 0;
|
||||||
virtual int HoleClearance( const ITEM* aA, const ITEM* aB ) = 0;
|
virtual int HoleClearance( const ITEM* aA, const ITEM* aB,
|
||||||
virtual int HoleToHoleClearance( const ITEM* aA, const ITEM* aB ) = 0;
|
bool aUseClearanceEpsilon = true ) = 0;
|
||||||
|
virtual int HoleToHoleClearance( const ITEM* aA, const ITEM* aB,
|
||||||
|
bool aUseClearanceEpsilon = true ) = 0;
|
||||||
|
|
||||||
virtual int DpCoupledNet( int aNet ) = 0;
|
virtual int DpCoupledNet( int aNet ) = 0;
|
||||||
virtual int DpNetPolarity( int aNet ) = 0;
|
virtual int DpNetPolarity( int aNet ) = 0;
|
||||||
|
@ -154,9 +156,10 @@ public:
|
||||||
~NODE();
|
~NODE();
|
||||||
|
|
||||||
///< Return the expected clearance between items a and b.
|
///< Return the expected clearance between items a and b.
|
||||||
int GetClearance( const ITEM* aA, const ITEM* aB ) const;
|
int GetClearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilon = true ) const;
|
||||||
int GetHoleClearance( const ITEM* aA, const ITEM* aB ) const;
|
int GetHoleClearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilon = true ) const;
|
||||||
int GetHoleToHoleClearance( const ITEM* aA, const ITEM* aB ) const;
|
int GetHoleToHoleClearance( const ITEM* aA, const ITEM* aB,
|
||||||
|
bool aUseClearanceEpsilon = true ) const;
|
||||||
|
|
||||||
///< Return the pre-set worst case clearance between any pair of items.
|
///< Return the pre-set worst case clearance between any pair of items.
|
||||||
int GetMaxClearance() const
|
int GetMaxClearance() const
|
||||||
|
@ -214,10 +217,13 @@ public:
|
||||||
*
|
*
|
||||||
* @param aLine the item to find collisions with
|
* @param aLine the item to find collisions with
|
||||||
* @param aKindMask mask of obstacle types to take into account
|
* @param aKindMask mask of obstacle types to take into account
|
||||||
|
* @param aRestrictedSet is an optional set of items that should be considered as obstacles
|
||||||
|
* @param aUseClearanceEpsilon determines if the epsilon is subtracted from the hull size
|
||||||
* @return the obstacle, if found, otherwise empty.
|
* @return the obstacle, if found, otherwise empty.
|
||||||
*/
|
*/
|
||||||
OPT_OBSTACLE NearestObstacle( const LINE* aLine, int aKindMask = ITEM::ANY_T,
|
OPT_OBSTACLE NearestObstacle( const LINE* aLine, int aKindMask = ITEM::ANY_T,
|
||||||
const std::set<ITEM*>* aRestrictedSet = nullptr );
|
const std::set<ITEM*>* aRestrictedSet = nullptr,
|
||||||
|
bool aUseClearanceEpsilon = true );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the item collides with anything else in the world, and if found, returns the
|
* Check if the item collides with anything else in the world, and if found, returns the
|
||||||
|
|
|
@ -41,7 +41,7 @@ void WALKAROUND::start( const LINE& aInitialPath )
|
||||||
NODE::OPT_OBSTACLE WALKAROUND::nearestObstacle( const LINE& aPath )
|
NODE::OPT_OBSTACLE WALKAROUND::nearestObstacle( const LINE& aPath )
|
||||||
{
|
{
|
||||||
NODE::OPT_OBSTACLE obs = m_world->NearestObstacle(
|
NODE::OPT_OBSTACLE obs = m_world->NearestObstacle(
|
||||||
&aPath, m_itemMask, m_restrictedSet.empty() ? nullptr : &m_restrictedSet );
|
&aPath, m_itemMask, m_restrictedSet.empty() ? nullptr : &m_restrictedSet, false );
|
||||||
|
|
||||||
if( m_restrictedSet.empty() )
|
if( m_restrictedSet.empty() )
|
||||||
return obs;
|
return obs;
|
||||||
|
|
Loading…
Reference in New Issue