Move a bunch of DRC tests to RTrees.
This commit is contained in:
parent
98ece9df72
commit
d5addb692c
|
@ -199,7 +199,13 @@ void RC_TREE_MODEL::rebuildModel( RC_ITEMS_PROVIDER* aProvider, int aSeverities
|
|||
|
||||
m_tree.clear();
|
||||
|
||||
for( int i = 0; m_rcItemsProvider && i < m_rcItemsProvider->GetCount(); ++i )
|
||||
// wxDataView::ExpandAll() pukes with large lists
|
||||
int count = 0;
|
||||
|
||||
if( m_rcItemsProvider )
|
||||
count = std::min( 1000, m_rcItemsProvider->GetCount() );
|
||||
|
||||
for( int i = 0; i < count; ++i )
|
||||
{
|
||||
std::shared_ptr<RC_ITEM> rcItem = m_rcItemsProvider->GetItem( i );
|
||||
|
||||
|
|
|
@ -192,6 +192,10 @@ bool D_PAD::FlashLayer( LSET aLayers ) const
|
|||
|
||||
bool D_PAD::FlashLayer( int aLayer ) const
|
||||
{
|
||||
// Return the "normal" shape if the caller doesn't specify a particular layer
|
||||
if( aLayer == UNDEFINED_LAYER )
|
||||
return true;
|
||||
|
||||
BOARD* board = GetBoard();
|
||||
|
||||
if( !board )
|
||||
|
|
|
@ -480,6 +480,10 @@ bool VIA::FlashLayer( LSET aLayers ) const
|
|||
|
||||
bool VIA::FlashLayer( int aLayer ) const
|
||||
{
|
||||
// Return the "normal" shape if the caller doesn't specify a particular layer
|
||||
if( aLayer == UNDEFINED_LAYER )
|
||||
return true;
|
||||
|
||||
BOARD* board = GetBoard();
|
||||
|
||||
if( !board )
|
||||
|
@ -984,8 +988,10 @@ std::shared_ptr<SHAPE> TRACK::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
|
|||
|
||||
std::shared_ptr<SHAPE> VIA::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
|
||||
{
|
||||
// fixme: pad stack support (depending on aLayer )
|
||||
return std::make_shared<SHAPE_CIRCLE>( m_Start, m_Width / 2 );
|
||||
if( FlashLayer( aLayer ) )
|
||||
return std::make_shared<SHAPE_CIRCLE>( m_Start, m_Width / 2 );
|
||||
else
|
||||
return std::make_shared<SHAPE_CIRCLE>( m_Start, GetDrillValue() / 2 );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -48,10 +48,13 @@ public:
|
|||
|
||||
struct ITEM_WITH_SHAPE
|
||||
{
|
||||
ITEM_WITH_SHAPE( BOARD_ITEM *aParent, SHAPE* aShape, std::shared_ptr<SHAPE> aParentShape = nullptr ) :
|
||||
ITEM_WITH_SHAPE( BOARD_ITEM *aParent, SHAPE* aShape,
|
||||
std::shared_ptr<SHAPE> aParentShape = nullptr ) :
|
||||
parent ( aParent ),
|
||||
shape ( aShape ),
|
||||
parentShape( aParentShape ) {};
|
||||
parentShape( aParentShape )
|
||||
{};
|
||||
|
||||
BOARD_ITEM* parent;
|
||||
SHAPE* shape;
|
||||
std::shared_ptr<SHAPE> parentShape;
|
||||
|
@ -81,93 +84,46 @@ public:
|
|||
* Function Insert()
|
||||
* Inserts an item into the tree. Item's bounding box is taken via its GetBoundingBox() method.
|
||||
*/
|
||||
void insert( BOARD_ITEM* aItem )
|
||||
void insert( BOARD_ITEM* aItem, int aWorstClearance = 0, int aLayer = UNDEFINED_LAYER )
|
||||
{
|
||||
std::vector<SHAPE*> subshapes;
|
||||
|
||||
for( int layer : aItem->GetLayerSet().Seq() )
|
||||
auto addLayer =
|
||||
[&]( PCB_LAYER_ID layer )
|
||||
{
|
||||
std::shared_ptr<SHAPE> shape = aItem->GetEffectiveShape( layer );
|
||||
|
||||
if( shape->HasIndexableSubshapes() )
|
||||
shape->GetIndexableSubshapes( subshapes );
|
||||
else
|
||||
subshapes.push_back( shape.get() );
|
||||
|
||||
for( SHAPE* subshape : subshapes )
|
||||
{
|
||||
BOX2I bbox = subshape->BBox();
|
||||
|
||||
bbox.Inflate( aWorstClearance );
|
||||
|
||||
const int mmin[2] = { bbox.GetX(), bbox.GetY() };
|
||||
const int mmax[2] = { bbox.GetRight(), bbox.GetBottom() };
|
||||
|
||||
m_tree[layer]->Insert( mmin, mmax,
|
||||
new ITEM_WITH_SHAPE( aItem, subshape, shape ) );
|
||||
m_count++;
|
||||
}
|
||||
};
|
||||
|
||||
if( aLayer != UNDEFINED_LAYER )
|
||||
{
|
||||
std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( (PCB_LAYER_ID) layer );
|
||||
|
||||
if( itemShape->HasIndexableSubshapes() )
|
||||
{
|
||||
itemShape->GetIndexableSubshapes( subshapes );
|
||||
}
|
||||
else
|
||||
{
|
||||
subshapes.push_back( itemShape.get() );
|
||||
}
|
||||
|
||||
for( auto subshape : subshapes )
|
||||
{
|
||||
BOX2I bbox = subshape->BBox();
|
||||
const int mmin[2] = { bbox.GetX(), bbox.GetY() };
|
||||
const int mmax[2] = { bbox.GetRight(), bbox.GetBottom() };
|
||||
|
||||
m_tree[layer]->Insert( mmin, mmax, new ITEM_WITH_SHAPE( aItem, subshape, itemShape ) );
|
||||
m_count++;
|
||||
}
|
||||
addLayer( (PCB_LAYER_ID) aLayer );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int layer : aItem->GetLayerSet().Seq() )
|
||||
addLayer( (PCB_LAYER_ID) layer );
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Function Remove()
|
||||
* Removes an item from the tree. Removal is done by comparing pointers, attempting
|
||||
* to remove a copy of the item will fail.
|
||||
*/
|
||||
bool remove( BOARD_ITEM* aItem )
|
||||
{
|
||||
// First, attempt to remove the item using its given BBox
|
||||
const EDA_RECT& bbox = aItem->GetBoundingBox();
|
||||
const int mmin[2] = { bbox.GetX(), bbox.GetY() };
|
||||
const int mmax[2] = { bbox.GetRight(), bbox.GetBottom() };
|
||||
bool removed = false;
|
||||
|
||||
for( auto layer : aItem->GetLayerSet().Seq() )
|
||||
{
|
||||
if( ZONE_CONTAINER* zone = dyn_cast<ZONE_CONTAINER*>( aItem ) )
|
||||
{
|
||||
// Continue removing the zone elements from the tree until they cannot be found
|
||||
while( !m_tree[int( layer )]->Remove( mmin, mmax, aItem ) )
|
||||
;
|
||||
|
||||
const int mmin2[2] = { INT_MIN, INT_MIN };
|
||||
const int mmax2[2] = { INT_MAX, INT_MAX };
|
||||
|
||||
// If we are not successful ( true == not found ), then we expand
|
||||
// the search to the full tree
|
||||
while( !m_tree[int( layer )]->Remove( mmin2, mmax2, aItem ) )
|
||||
;
|
||||
|
||||
// Loop to the next layer
|
||||
continue;
|
||||
}
|
||||
|
||||
// The non-zone search expects only a single element in the tree with the same
|
||||
// pointer aItem
|
||||
if( m_tree[int( layer )]->Remove( mmin, mmax, aItem ) )
|
||||
{
|
||||
// N.B. We must search the whole tree for the pointer to remove
|
||||
// because the item may have been moved before we have the chance to
|
||||
// delete it from the tree
|
||||
const int mmin2[2] = { INT_MIN, INT_MIN };
|
||||
const int mmax2[2] = { INT_MAX, INT_MAX };
|
||||
|
||||
if( m_tree[int( layer )]->Remove( mmin2, mmax2, aItem ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
removed = true;
|
||||
}
|
||||
|
||||
m_count -= int( removed );
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Function RemoveAll()
|
||||
* Removes all items from the RTree
|
||||
|
@ -180,66 +136,7 @@ public:
|
|||
m_count = 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Determine if a given item exists in the tree. Note that this does not search the full tree
|
||||
* so if the item has been moved, this will return false when it should be true.
|
||||
*
|
||||
* @param aItem Item that may potentially exist in the tree
|
||||
* @param aRobust If true, search the whole tree, not just the bounding box
|
||||
* @return true if the item definitely exists, false if it does not exist within bbox
|
||||
*/
|
||||
bool contains( BOARD_ITEM* aItem, bool aRobust = false )
|
||||
{
|
||||
const EDA_RECT& bbox = aItem->GetBoundingBox();
|
||||
const int mmin[2] = { bbox.GetX(), bbox.GetY() };
|
||||
const int mmax[2] = { bbox.GetRight(), bbox.GetBottom() };
|
||||
bool found = false;
|
||||
|
||||
auto search = [&found, &aItem]( const ITEM_WITH_SHAPE* aSearchItem ) {
|
||||
if( aSearchItem->parent == aItem )
|
||||
{
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
for( int layer : aItem->GetLayerSet().Seq() )
|
||||
{
|
||||
m_tree[layer]->Search( mmin, mmax, search );
|
||||
|
||||
if( found )
|
||||
break;
|
||||
}
|
||||
|
||||
if( !found && aRobust )
|
||||
{
|
||||
for( int layer : LSET::AllCuMask().Seq() )
|
||||
{
|
||||
// N.B. We must search the whole tree for the pointer to remove
|
||||
// because the item may have been moved. We do not expand the item
|
||||
// layer search as this should not change.
|
||||
|
||||
const int mmin2[2] = { INT_MIN, INT_MIN };
|
||||
const int mmax2[2] = { INT_MAX, INT_MAX };
|
||||
|
||||
m_tree[layer]->Search( mmin2, mmax2, search );
|
||||
|
||||
if( found )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool CheckColliding( SHAPE* aRefShape,
|
||||
PCB_LAYER_ID aTargetLayer,
|
||||
int aClearance = 0,
|
||||
bool CheckColliding( SHAPE* aRefShape, PCB_LAYER_ID aTargetLayer, int aClearance = 0,
|
||||
std::function<bool( BOARD_ITEM*)> aFilter = nullptr ) const
|
||||
{
|
||||
BOX2I box = aRefShape->BBox();
|
||||
|
@ -405,60 +302,6 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
std::vector<std::pair<int, BOARD_ITEM*>> GetNearest( const wxPoint &aPoint,
|
||||
PCB_LAYER_ID aLayer, int aLimit )
|
||||
{
|
||||
|
||||
const int point[2] = { aPoint.x, aPoint.y };
|
||||
auto result = m_tree[int( aLayer )]->NearestNeighbors( point,
|
||||
[aLimit]( std::size_t a_count, int a_maxDist ) -> bool
|
||||
{
|
||||
return a_count >= aLimit;
|
||||
},
|
||||
[]( BOARD_ITEM* aElement) -> bool
|
||||
{
|
||||
// Don't remove any elements from the list
|
||||
return false;
|
||||
},
|
||||
[aLayer]( const int* a_point, BOARD_ITEM* a_data ) -> int
|
||||
{
|
||||
switch( a_data->Type() )
|
||||
{
|
||||
case PCB_TRACE_T:
|
||||
{
|
||||
TRACK* track = static_cast<TRACK*>( a_data );
|
||||
SEG seg( track->GetStart(), track->GetEnd() );
|
||||
return seg.Distance( VECTOR2I( a_point[0], a_point[1] ) ) -
|
||||
( track->GetWidth() + 1 ) / 2;
|
||||
}
|
||||
case PCB_VIA_T:
|
||||
{
|
||||
VIA* via = static_cast<VIA*>( a_data );
|
||||
return ( VECTOR2I( via->GetPosition() ) -
|
||||
VECTOR2I( a_point[0], a_point[1] ) ).EuclideanNorm() -
|
||||
( via->GetWidth() + 1 ) / 2;
|
||||
|
||||
}
|
||||
default:
|
||||
{
|
||||
VECTOR2I point( a_point[0], a_point[1] );
|
||||
int dist = 0;
|
||||
auto shape = a_data->GetEffectiveShape( aLayer );
|
||||
|
||||
// Here we use a hack to get the distance by colliding with a large area
|
||||
// However, we can't use just MAX_INT because we will overflow the collision calculations
|
||||
shape->Collide( point, std::numeric_limits<int>::max() / 2, &dist);
|
||||
return dist;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns the number of items in the tree
|
||||
* @return number of elements in the tree;
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
// A list of all basic (ie: non-compound) board geometry items
|
||||
std::vector<KICAD_T> DRC_TEST_PROVIDER::s_allBasicItems;
|
||||
std::vector<KICAD_T> DRC_TEST_PROVIDER::s_allBasicItemsButZones;
|
||||
|
||||
|
||||
DRC_TEST_PROVIDER::DRC_TEST_PROVIDER() :
|
||||
|
@ -136,7 +137,12 @@ int DRC_TEST_PROVIDER::forEachGeometryItem( const std::vector<KICAD_T>& aTypes,
|
|||
for( int i = 0; i < MAX_STRUCT_TYPE_ID; i++ )
|
||||
{
|
||||
if( i != PCB_MODULE_T && i != PCB_GROUP_T )
|
||||
{
|
||||
s_allBasicItems.push_back( (KICAD_T) i );
|
||||
|
||||
if( i != PCB_ZONE_AREA_T && i != PCB_FP_ZONE_AREA_T )
|
||||
s_allBasicItemsButZones.push_back( (KICAD_T) i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -124,6 +124,7 @@ protected:
|
|||
|
||||
// List of basic (ie: non-compound) geometry items
|
||||
static std::vector<KICAD_T> s_allBasicItems;
|
||||
static std::vector<KICAD_T> s_allBasicItemsButZones;
|
||||
|
||||
EDA_UNITS userUnits() const;
|
||||
DRC_ENGINE* m_drcEngine;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -22,7 +22,6 @@
|
|||
*/
|
||||
|
||||
#include <geometry/shape_poly_set.h>
|
||||
#include <drc/drc_engine.h>
|
||||
#include <drc/drc_item.h>
|
||||
#include <drc/drc_rule.h>
|
||||
#include <drc/drc_test_provider_clearance_base.h>
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <drc/drc_item.h>
|
||||
#include <drc/drc_rule.h>
|
||||
#include <drc/drc_test_provider_clearance_base.h>
|
||||
#include "drc_rtree.h"
|
||||
|
||||
/*
|
||||
Board edge clearance test. Checks all items for their mechanical clearances against the board
|
||||
|
@ -70,9 +71,53 @@ public:
|
|||
virtual std::set<DRC_CONSTRAINT_TYPE_T> GetConstraintTypes() const override;
|
||||
|
||||
int GetNumPhases() const override;
|
||||
|
||||
private:
|
||||
bool testAgainstEdge( BOARD_ITEM* item, SHAPE* itemShape, BOARD_ITEM* other,
|
||||
DRC_CONSTRAINT_TYPE_T aConstraintType, PCB_DRC_CODE aErrorCode );
|
||||
};
|
||||
|
||||
|
||||
bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::testAgainstEdge( BOARD_ITEM* item, SHAPE* itemShape,
|
||||
BOARD_ITEM* edge,
|
||||
DRC_CONSTRAINT_TYPE_T aConstraintType,
|
||||
PCB_DRC_CODE aErrorCode )
|
||||
{
|
||||
const std::shared_ptr<SHAPE>& edgeShape = edge->GetEffectiveShape( Edge_Cuts );
|
||||
|
||||
auto constraint = m_drcEngine->EvalRulesForItems( aConstraintType, edge, item );
|
||||
|
||||
int minClearance = constraint.GetValue().Min();
|
||||
int actual;
|
||||
VECTOR2I pos;
|
||||
|
||||
accountCheck( constraint );
|
||||
|
||||
if( itemShape->Collide( edgeShape.get(), minClearance, &actual, &pos ) )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( aErrorCode );
|
||||
|
||||
// Only report clearance info if there is any; otherwise it's just a straight collision
|
||||
if( minClearance > 0 )
|
||||
{
|
||||
m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
|
||||
constraint.GetName(),
|
||||
MessageTextFromValue( userUnits(), minClearance ),
|
||||
MessageTextFromValue( userUnits(), actual ) );
|
||||
|
||||
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
|
||||
}
|
||||
|
||||
drce->SetItems( edge->m_Uuid, item->m_Uuid );
|
||||
drce->SetViolatingRule( constraint.GetParentRule() );
|
||||
|
||||
reportViolation( drce, (wxPoint) pos );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run()
|
||||
{
|
||||
m_board = m_drcEngine->GetBoard();
|
||||
|
@ -90,8 +135,9 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run()
|
|||
if( !reportPhase( _( "Checking board edge clearances..." ) ) )
|
||||
return false;
|
||||
|
||||
std::vector<std::unique_ptr<PCB_SHAPE>> boardOutline;
|
||||
std::vector<BOARD_ITEM*> boardItems;
|
||||
std::vector<std::unique_ptr<PCB_SHAPE>> edges; // we own these
|
||||
DRC_RTREE edgesTree;
|
||||
std::vector<BOARD_ITEM*> boardItems; // we don't own these
|
||||
|
||||
auto queryBoardOutlineItems =
|
||||
[&]( BOARD_ITEM *item ) -> bool
|
||||
|
@ -102,18 +148,18 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run()
|
|||
{
|
||||
// A single rectangle for the board would make the RTree useless, so
|
||||
// convert to 4 edges
|
||||
boardOutline.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
|
||||
boardOutline.back()->SetShape( S_SEGMENT );
|
||||
boardOutline.back()->SetEndX( shape->GetStartX() );
|
||||
boardOutline.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
|
||||
boardOutline.back()->SetShape( S_SEGMENT );
|
||||
boardOutline.back()->SetEndY( shape->GetStartY() );
|
||||
boardOutline.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
|
||||
boardOutline.back()->SetShape( S_SEGMENT );
|
||||
boardOutline.back()->SetStartX( shape->GetEndX() );
|
||||
boardOutline.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
|
||||
boardOutline.back()->SetShape( S_SEGMENT );
|
||||
boardOutline.back()->SetStartY( shape->GetEndY() );
|
||||
edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
|
||||
edges.back()->SetShape( S_SEGMENT );
|
||||
edges.back()->SetEndX( shape->GetStartX() );
|
||||
edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
|
||||
edges.back()->SetShape( S_SEGMENT );
|
||||
edges.back()->SetEndY( shape->GetStartY() );
|
||||
edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
|
||||
edges.back()->SetShape( S_SEGMENT );
|
||||
edges.back()->SetStartX( shape->GetEndX() );
|
||||
edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
|
||||
edges.back()->SetShape( S_SEGMENT );
|
||||
edges.back()->SetStartY( shape->GetEndY() );
|
||||
return true;
|
||||
}
|
||||
else if( shape->GetShape() == S_POLYGON )
|
||||
|
@ -124,140 +170,78 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run()
|
|||
for( size_t ii = 0; ii < poly.GetSegmentCount(); ++ii )
|
||||
{
|
||||
SEG seg = poly.CSegment( ii );
|
||||
boardOutline.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
|
||||
boardOutline.back()->SetShape( S_SEGMENT );
|
||||
boardOutline.back()->SetStart( (wxPoint) seg.A );
|
||||
boardOutline.back()->SetEnd( (wxPoint) seg.B );
|
||||
edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
|
||||
edges.back()->SetShape( S_SEGMENT );
|
||||
edges.back()->SetStart((wxPoint) seg.A );
|
||||
edges.back()->SetEnd((wxPoint) seg.B );
|
||||
}
|
||||
}
|
||||
|
||||
boardOutline.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
|
||||
boardOutline.back()->SetWidth( 0 );
|
||||
edges.emplace_back( static_cast<PCB_SHAPE*>( shape->Clone() ) );
|
||||
edges.back()->SetWidth( 0 );
|
||||
return true;
|
||||
};
|
||||
|
||||
auto queryBoardGeometryItems =
|
||||
[&]( BOARD_ITEM *item ) -> bool
|
||||
{
|
||||
boardItems.push_back( item );
|
||||
if( !isInvisibleText( item ) )
|
||||
boardItems.push_back( item );
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
forEachGeometryItem( { PCB_SHAPE_T }, LSET( Edge_Cuts ), queryBoardOutlineItems );
|
||||
forEachGeometryItem( s_allBasicItems, LSET::AllCuMask(), queryBoardGeometryItems );
|
||||
forEachGeometryItem( s_allBasicItemsButZones, LSET::AllCuMask(), queryBoardGeometryItems );
|
||||
|
||||
for( const std::unique_ptr<PCB_SHAPE>& edge : edges )
|
||||
edgesTree.insert( edge.get(), m_largestClearance );
|
||||
|
||||
wxString val;
|
||||
wxGetEnv( "WXTRACE", &val );
|
||||
|
||||
drc_dbg( 2, "outline: %d items, board: %d items\n",
|
||||
(int) boardOutline.size(), (int) boardItems.size() );
|
||||
(int) edges.size(), (int) boardItems.size() );
|
||||
|
||||
for( const std::unique_ptr<PCB_SHAPE>& outlineItem : boardOutline )
|
||||
// This is the number of tests between 2 calls to the progress bar
|
||||
const int delta = 50;
|
||||
int ii = 0;
|
||||
|
||||
for( BOARD_ITEM* item : boardItems )
|
||||
{
|
||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_COPPER_EDGE_CLEARANCE ) )
|
||||
bool testCopper = !m_drcEngine->IsErrorLimitExceeded( DRCE_COPPER_EDGE_CLEARANCE );
|
||||
bool testSilk = !m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE );
|
||||
|
||||
if( !testCopper && !testSilk )
|
||||
break;
|
||||
|
||||
const std::shared_ptr<SHAPE>& refShape = outlineItem->GetEffectiveShape();
|
||||
if( !reportProgress( ii++, boardItems.size(), delta ) )
|
||||
break;
|
||||
|
||||
for( BOARD_ITEM* boardItem : boardItems )
|
||||
const std::shared_ptr<SHAPE>& itemShape = item->GetEffectiveShape();
|
||||
|
||||
if( testCopper && item->IsOnCopperLayer() )
|
||||
{
|
||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_COPPER_EDGE_CLEARANCE ) )
|
||||
break;
|
||||
|
||||
drc_dbg( 10, "RefT %d %p %s %d\n", outlineItem->Type(), outlineItem.get(),
|
||||
outlineItem->GetClass(), outlineItem->GetLayer() );
|
||||
drc_dbg( 10, "BoardT %d %p %s %d\n", boardItem->Type(), boardItem,
|
||||
boardItem->GetClass(), boardItem->GetLayer() );
|
||||
|
||||
if ( isInvisibleText( boardItem ) )
|
||||
continue;
|
||||
|
||||
const std::shared_ptr<SHAPE>& shape = boardItem->GetEffectiveShape();
|
||||
|
||||
auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_EDGE_CLEARANCE,
|
||||
outlineItem.get(), boardItem );
|
||||
|
||||
int minClearance = constraint.GetValue().Min();
|
||||
int actual;
|
||||
VECTOR2I pos;
|
||||
|
||||
accountCheck( constraint );
|
||||
|
||||
if( refShape->Collide( shape.get(), minClearance, &actual, &pos ) )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_COPPER_EDGE_CLEARANCE );
|
||||
|
||||
m_msg.Printf( drcItem->GetErrorText() + wxS( " " ) + _( "(%s clearance %s; actual %s)" ),
|
||||
constraint.GetName(),
|
||||
MessageTextFromValue( userUnits(), minClearance ),
|
||||
MessageTextFromValue( userUnits(), actual ) );
|
||||
|
||||
drcItem->SetErrorMessage( m_msg );
|
||||
drcItem->SetItems( outlineItem->m_Uuid, boardItem->m_Uuid );
|
||||
drcItem->SetViolatingRule( constraint.GetParentRule() );
|
||||
|
||||
reportViolation( drcItem, (wxPoint) pos );
|
||||
}
|
||||
edgesTree.QueryColliding( item, UNDEFINED_LAYER, Edge_Cuts, nullptr,
|
||||
[&]( BOARD_ITEM* edge, int ) -> bool
|
||||
{
|
||||
return testAgainstEdge( item, itemShape.get(), edge,
|
||||
DRC_CONSTRAINT_TYPE_EDGE_CLEARANCE,
|
||||
DRCE_COPPER_EDGE_CLEARANCE );
|
||||
},
|
||||
m_largestClearance );
|
||||
}
|
||||
}
|
||||
|
||||
if( !reportPhase( _( "Checking silkscreen to board edge clearances..." ) ) )
|
||||
return false;
|
||||
|
||||
boardItems.clear();
|
||||
forEachGeometryItem( s_allBasicItems, LSET( 2, F_SilkS, B_SilkS ),
|
||||
queryBoardGeometryItems );
|
||||
|
||||
for( const std::unique_ptr<PCB_SHAPE>& outlineItem : boardOutline )
|
||||
{
|
||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE ) )
|
||||
break;
|
||||
|
||||
const std::shared_ptr<SHAPE>& refShape = outlineItem->GetEffectiveShape();
|
||||
|
||||
for( BOARD_ITEM* boardItem : boardItems )
|
||||
if( testSilk && ( item->GetLayer() == F_SilkS || item->GetLayer() == B_SilkS ) )
|
||||
{
|
||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE ) )
|
||||
break;
|
||||
|
||||
drc_dbg( 10, "RefT %d %p %s %d\n", outlineItem->Type(), outlineItem.get(),
|
||||
outlineItem->GetClass(), outlineItem->GetLayer() );
|
||||
drc_dbg( 10, "BoardT %d %p %s %d\n", boardItem->Type(), boardItem,
|
||||
boardItem->GetClass(), boardItem->GetLayer() );
|
||||
|
||||
if( isInvisibleText( boardItem ) )
|
||||
continue;
|
||||
|
||||
const std::shared_ptr<SHAPE>& shape = boardItem->GetEffectiveShape();
|
||||
|
||||
auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_SILK_CLEARANCE,
|
||||
outlineItem.get(), boardItem );
|
||||
|
||||
int minClearance = constraint.GetValue().Min();
|
||||
int actual;
|
||||
VECTOR2I pos;
|
||||
|
||||
accountCheck( constraint );
|
||||
|
||||
if( refShape->Collide( shape.get(), minClearance, &actual, &pos ) )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SILK_MASK_CLEARANCE );
|
||||
|
||||
if( minClearance > 0 )
|
||||
{
|
||||
m_msg.Printf( drcItem->GetErrorText() + wxS( " " ) + _( "(%s clearance %s; actual %s)" ),
|
||||
constraint.GetName(),
|
||||
MessageTextFromValue( userUnits(), minClearance ),
|
||||
MessageTextFromValue( userUnits(), actual ) );
|
||||
|
||||
drcItem->SetErrorMessage( m_msg );
|
||||
}
|
||||
|
||||
drcItem->SetItems( outlineItem->m_Uuid, boardItem->m_Uuid );
|
||||
drcItem->SetViolatingRule( constraint.GetParentRule() );
|
||||
|
||||
reportViolation( drcItem, (wxPoint) pos );
|
||||
}
|
||||
edgesTree.QueryColliding( item, UNDEFINED_LAYER, Edge_Cuts, nullptr,
|
||||
[&]( BOARD_ITEM* edge, int ) -> bool
|
||||
{
|
||||
return testAgainstEdge( item, itemShape.get(), edge,
|
||||
DRC_CONSTRAINT_TYPE_SILK_CLEARANCE,
|
||||
DRCE_SILK_MASK_CLEARANCE );
|
||||
},
|
||||
m_largestClearance );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <drc/drc_item.h>
|
||||
#include <drc/drc_rule.h>
|
||||
#include <drc/drc_test_provider_clearance_base.h>
|
||||
#include <libs/kimath/include/geometry/shape_circle.h>
|
||||
#include "drc_rtree.h"
|
||||
|
||||
/*
|
||||
Holes clearance test. Checks pad and via holes for their mechanical clearances.
|
||||
|
@ -43,10 +45,9 @@ class DRC_TEST_PROVIDER_HOLE_CLEARANCE : public DRC_TEST_PROVIDER_CLEARANCE_BASE
|
|||
public:
|
||||
DRC_TEST_PROVIDER_HOLE_CLEARANCE () :
|
||||
DRC_TEST_PROVIDER_CLEARANCE_BASE(),
|
||||
m_board( nullptr ),
|
||||
m_largestRadius( 0 )
|
||||
{
|
||||
}
|
||||
m_board( nullptr )
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~DRC_TEST_PROVIDER_HOLE_CLEARANCE()
|
||||
{
|
||||
|
@ -61,7 +62,7 @@ public:
|
|||
|
||||
virtual const wxString GetDescription() const override
|
||||
{
|
||||
return "Tests clearance of holes (via/pad drills)";
|
||||
return "Tests hole to hole spacing";
|
||||
}
|
||||
|
||||
virtual std::set<DRC_CONSTRAINT_TYPE_T> GetConstraintTypes() const override;
|
||||
|
@ -69,44 +70,34 @@ public:
|
|||
int GetNumPhases() const override;
|
||||
|
||||
private:
|
||||
void addHole( const VECTOR2I& aLocation, int aRadius, BOARD_ITEM* aOwner );
|
||||
|
||||
void buildDrilledHoleList();
|
||||
void testHoles2Holes();
|
||||
void testPads2Holes();
|
||||
|
||||
/**
|
||||
* Test clearance of a pad hole with the pad hole of other pads.
|
||||
* @param aSortedPadsList is the sorted by X pos of all pads
|
||||
* @param aRefPadIdx is the index of pad to test inside aSortedPadsList
|
||||
* @param aX_limit is the max X pos of others pads that need to be tested
|
||||
* To speed up the test, aSortedPadsList is a pad list sorted by X position, and only pads
|
||||
* after the pad to test are tested, so this function must be called for each pad for the
|
||||
* first in list to the last in list
|
||||
*/
|
||||
bool doPadToPadHoleDrc( int aRefPadIdx, std::vector<D_PAD*>& aSortedPadsList, int aX_limit );
|
||||
|
||||
struct DRILLED_HOLE
|
||||
{
|
||||
VECTOR2I m_location;
|
||||
int m_drillRadius = 0;
|
||||
BOARD_ITEM* m_owner = nullptr;
|
||||
};
|
||||
|
||||
BOARD* m_board;
|
||||
std::vector<DRILLED_HOLE> m_drilledHoles;
|
||||
int m_largestRadius;
|
||||
bool testHoleAgainst( BOARD_ITEM* aItem, SHAPE_CIRCLE* aHole, BOARD_ITEM* aOther );
|
||||
|
||||
BOARD* m_board;
|
||||
DRC_RTREE m_holeTree;
|
||||
};
|
||||
|
||||
|
||||
static std::shared_ptr<SHAPE_CIRCLE> getDrilledHoleShape( BOARD_ITEM* aItem )
|
||||
{
|
||||
if( aItem->Type() == PCB_VIA_T )
|
||||
{
|
||||
VIA* via = static_cast<VIA*>( aItem );
|
||||
return std::make_shared<SHAPE_CIRCLE>( via->GetCenter(), via->GetDrillValue() / 2 );
|
||||
}
|
||||
else if( aItem->Type() == PCB_PAD_T )
|
||||
{
|
||||
D_PAD* pad = static_cast<D_PAD*>( aItem );
|
||||
return std::make_shared<SHAPE_CIRCLE>( pad->GetPosition(), pad->GetDrillSize().x / 2 );
|
||||
}
|
||||
|
||||
return std::make_shared<SHAPE_CIRCLE>( VECTOR2I( 0, 0 ), 0 );
|
||||
}
|
||||
|
||||
|
||||
bool DRC_TEST_PROVIDER_HOLE_CLEARANCE::Run()
|
||||
{
|
||||
m_board = m_drcEngine->GetBoard();
|
||||
|
||||
m_largestClearance = 0;
|
||||
m_largestRadius = 0;
|
||||
|
||||
DRC_CONSTRAINT worstClearanceConstraint;
|
||||
|
||||
if( m_drcEngine->QueryWorstConstraint( DRC_CONSTRAINT_TYPE_HOLE_CLEARANCE,
|
||||
|
@ -121,17 +112,114 @@ bool DRC_TEST_PROVIDER_HOLE_CLEARANCE::Run()
|
|||
return false;
|
||||
}
|
||||
|
||||
buildDrilledHoleList();
|
||||
// This is the number of tests between 2 calls to the progress bar
|
||||
const size_t delta = 50;
|
||||
size_t count = 0;
|
||||
size_t ii = 0;
|
||||
|
||||
if( !reportPhase( _( "Checking hole to pad clearances..." ) ) )
|
||||
return false;
|
||||
auto countItems =
|
||||
[&]( BOARD_ITEM* item ) -> bool
|
||||
{
|
||||
if( item->Type() == PCB_PAD_T )
|
||||
++count;
|
||||
else if( item->Type() == PCB_VIA_T )
|
||||
++count;
|
||||
|
||||
testPads2Holes();
|
||||
return true;
|
||||
};
|
||||
|
||||
auto addToHoleTree =
|
||||
[&]( BOARD_ITEM* item ) -> bool
|
||||
{
|
||||
if( !reportProgress( ii++, count, delta ) )
|
||||
return false;
|
||||
|
||||
item->ClearFlags( SKIP_STRUCT );
|
||||
|
||||
if( item->Type() == PCB_PAD_T )
|
||||
{
|
||||
D_PAD* pad = static_cast<D_PAD*>( item );
|
||||
|
||||
// Check for round hole
|
||||
if( pad->GetDrillSize().x && pad->GetDrillSize().x == pad->GetDrillSize().y )
|
||||
m_holeTree.insert( item, m_largestClearance, F_Cu );
|
||||
}
|
||||
else if( item->Type() == PCB_VIA_T )
|
||||
{
|
||||
VIA* via = static_cast<VIA*>( item );
|
||||
|
||||
// Check for drilled hole
|
||||
if( via->GetViaType() == VIATYPE::THROUGH )
|
||||
m_holeTree.insert( item, m_largestClearance, F_Cu );
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if( !reportPhase( _( "Checking hole to hole clearances..." ) ) )
|
||||
return false;
|
||||
|
||||
testHoles2Holes();
|
||||
forEachGeometryItem( { PCB_PAD_T, PCB_VIA_T }, LSET::AllLayersMask(), countItems );
|
||||
|
||||
count *= 2; // One for adding to tree; one for checking
|
||||
|
||||
forEachGeometryItem( { PCB_PAD_T, PCB_VIA_T }, LSET::AllLayersMask(), addToHoleTree );
|
||||
|
||||
for( TRACK* track : m_board->Tracks() )
|
||||
{
|
||||
if( track->Type() != PCB_VIA_T )
|
||||
continue;
|
||||
|
||||
VIA* via = static_cast<VIA*>( track );
|
||||
|
||||
if( !reportProgress( ii++, count, delta ) )
|
||||
break;
|
||||
|
||||
std::shared_ptr<SHAPE_CIRCLE> holeShape = getDrilledHoleShape( via );
|
||||
|
||||
m_holeTree.QueryColliding( via, F_Cu, F_Cu,
|
||||
[&]( BOARD_ITEM* other ) -> bool
|
||||
{
|
||||
if( other->HasFlag( SKIP_STRUCT ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
[&]( BOARD_ITEM* other, int ) -> bool
|
||||
{
|
||||
return testHoleAgainst( via, holeShape.get(), other );
|
||||
},
|
||||
m_largestClearance );
|
||||
|
||||
via->SetFlags( SKIP_STRUCT );
|
||||
}
|
||||
|
||||
for( MODULE* footprint : m_board->Modules() )
|
||||
{
|
||||
for( D_PAD* pad : footprint->Pads() )
|
||||
{
|
||||
if( !reportProgress( ii++, count, delta ) )
|
||||
break;
|
||||
|
||||
std::shared_ptr<SHAPE_CIRCLE> holeShape = getDrilledHoleShape( pad );
|
||||
|
||||
m_holeTree.QueryColliding( pad, F_Cu, F_Cu,
|
||||
[&]( BOARD_ITEM* other ) -> bool
|
||||
{
|
||||
if( other->HasFlag( SKIP_STRUCT ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
},
|
||||
[&]( BOARD_ITEM* other, int ) -> bool
|
||||
{
|
||||
return testHoleAgainst( pad, holeShape.get(), other );
|
||||
},
|
||||
m_largestClearance );
|
||||
|
||||
pad->SetFlags( SKIP_STRUCT );
|
||||
}
|
||||
}
|
||||
|
||||
reportRuleStatistics();
|
||||
|
||||
|
@ -139,269 +227,50 @@ bool DRC_TEST_PROVIDER_HOLE_CLEARANCE::Run()
|
|||
}
|
||||
|
||||
|
||||
void DRC_TEST_PROVIDER_HOLE_CLEARANCE::buildDrilledHoleList()
|
||||
bool DRC_TEST_PROVIDER_HOLE_CLEARANCE::testHoleAgainst( BOARD_ITEM* aItem, SHAPE_CIRCLE* aHole,
|
||||
BOARD_ITEM* aOther )
|
||||
{
|
||||
m_drilledHoles.clear();
|
||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_DRILLED_HOLES_TOO_CLOSE ) )
|
||||
return false;
|
||||
|
||||
for( MODULE* module : m_board->Modules() )
|
||||
std::shared_ptr<SHAPE_CIRCLE> otherHole = getDrilledHoleShape( aItem );
|
||||
|
||||
// Holes with identical locations are allowable
|
||||
if( aHole->GetCenter() == otherHole->GetCenter() )
|
||||
return true;
|
||||
|
||||
int actual = ( aHole->GetCenter() - otherHole->GetCenter() ).EuclideanNorm();
|
||||
actual = std::max( 0, actual - aHole->GetRadius() - otherHole->GetRadius() );
|
||||
|
||||
auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_HOLE_CLEARANCE,
|
||||
aItem, aOther );
|
||||
int minClearance = constraint.GetValue().Min();
|
||||
|
||||
accountCheck( constraint.GetParentRule() );
|
||||
|
||||
if( actual < minClearance )
|
||||
{
|
||||
for( D_PAD* pad : module->Pads() )
|
||||
{
|
||||
int holeSize = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
|
||||
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_DRILLED_HOLES_TOO_CLOSE );
|
||||
|
||||
if( holeSize == 0 )
|
||||
continue;
|
||||
m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
|
||||
constraint.GetName(),
|
||||
MessageTextFromValue( userUnits(), minClearance ),
|
||||
MessageTextFromValue( userUnits(), actual ) );
|
||||
|
||||
// Milled holes (slots) aren't required to meet the minimum hole-to-hole
|
||||
// distance, so we only have to collect the drilled holes.
|
||||
if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
|
||||
addHole( pad->GetPosition(), pad->GetDrillSize().x / 2, pad );
|
||||
}
|
||||
}
|
||||
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
|
||||
drce->SetItems( aItem, aOther );
|
||||
drce->SetViolatingRule( constraint.GetParentRule() );
|
||||
|
||||
for( TRACK* track : m_board->Tracks() )
|
||||
{
|
||||
if( track->Type() == PCB_VIA_T )
|
||||
{
|
||||
VIA* via = static_cast<VIA*>( track );
|
||||
addHole( via->GetPosition(), via->GetDrillValue() / 2, via );
|
||||
}
|
||||
}
|
||||
|
||||
reportAux( "Total drilled holes : %d", m_drilledHoles.size());
|
||||
}
|
||||
|
||||
|
||||
void DRC_TEST_PROVIDER_HOLE_CLEARANCE::testPads2Holes()
|
||||
{
|
||||
const int delta = 25; // This is the number of tests between 2 calls to the progress bar
|
||||
std::vector<D_PAD*> sortedPads;
|
||||
|
||||
m_board->GetSortedPadListByXthenYCoord( sortedPads );
|
||||
|
||||
if( sortedPads.empty() )
|
||||
return;
|
||||
|
||||
// find the max size of the pads (used to stop the pad-to-pad tests)
|
||||
int max_size = 0;
|
||||
|
||||
for( D_PAD* pad : sortedPads )
|
||||
{
|
||||
// GetBoundingRadius() is the radius of the minimum sized circle fully containing the pad
|
||||
int radius = pad->GetBoundingRadius();
|
||||
|
||||
if( radius > max_size )
|
||||
max_size = radius;
|
||||
}
|
||||
|
||||
// Better to be fast than accurate; this keeps us from having to look up / calculate the
|
||||
// actual clearances
|
||||
max_size += m_largestClearance;
|
||||
|
||||
// Test the pads
|
||||
for( int idx = 0; idx < (int) sortedPads.size(); idx++ )
|
||||
{
|
||||
D_PAD* pad = sortedPads[idx];
|
||||
int x_limit = pad->GetPosition().x + pad->GetBoundingRadius() + max_size;
|
||||
|
||||
drc_dbg( 10, "-> %p\n", pad );
|
||||
|
||||
if( !reportProgress( idx, sortedPads.size(), delta ) )
|
||||
break;
|
||||
|
||||
doPadToPadHoleDrc( idx, sortedPads, x_limit );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DRC_TEST_PROVIDER_HOLE_CLEARANCE::doPadToPadHoleDrc( int aRefPadIdx,
|
||||
std::vector<D_PAD*>& aSortedPadsList,
|
||||
int aX_limit )
|
||||
{
|
||||
const static LSET all_cu = LSET::AllCuMask();
|
||||
|
||||
D_PAD* refPad = aSortedPadsList[aRefPadIdx];
|
||||
|
||||
for( int idx = aRefPadIdx; idx < (int)aSortedPadsList.size(); ++idx )
|
||||
{
|
||||
D_PAD* pad = aSortedPadsList[idx];
|
||||
|
||||
if( pad == refPad || pad->SameLogicalPadAs( refPad ) )
|
||||
continue;
|
||||
|
||||
// drc_dbg(10," chk against -> %p\n", pad);
|
||||
|
||||
// We can stop the test when pad->GetPosition().x > aX_limit because the list is
|
||||
// sorted by X positions, and other pads are too far.
|
||||
if( pad->GetPosition().x > aX_limit )
|
||||
break;
|
||||
|
||||
drc_dbg( 10, " chk1 against -> %p x0 %d x2 %d\n",
|
||||
pad, pad->GetDrillSize().x, refPad->GetDrillSize().x );
|
||||
|
||||
if( pad->GetDrillSize().x && ( refPad->GetLayerSet() & all_cu ).any() )
|
||||
{
|
||||
// pad has a hole and refPad is on copper
|
||||
|
||||
auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_HOLE_CLEARANCE,
|
||||
refPad, pad );
|
||||
int minClearance = constraint.GetValue().Min();
|
||||
int actual;
|
||||
|
||||
drc_dbg( 10, "check pad %p rule '%s' cl %d\n",
|
||||
pad, constraint.GetParentRule()->m_Name, minClearance );
|
||||
|
||||
accountCheck( constraint.GetParentRule() );
|
||||
|
||||
const std::shared_ptr<SHAPE>& refPadShape = refPad->GetEffectiveShape();
|
||||
|
||||
// fixme: pad stacks...
|
||||
if( refPadShape->Collide( pad->GetEffectiveHoleShape(), minClearance, &actual ) )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
|
||||
|
||||
m_msg.Printf( drcItem->GetErrorText() + wxS( " " ) + _( "(%s clearance %s; actual %s)" ),
|
||||
constraint.GetName(),
|
||||
MessageTextFromValue( userUnits(), minClearance ),
|
||||
MessageTextFromValue( userUnits(), actual ) );
|
||||
|
||||
drcItem->SetErrorMessage( m_msg );
|
||||
drcItem->SetItems( pad, refPad );
|
||||
drcItem->SetViolatingRule( constraint.GetParentRule() );
|
||||
|
||||
reportViolation( drcItem, pad->GetPosition() );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( refPad->GetDrillSize().x && ( pad->GetLayerSet() & all_cu ).any() )
|
||||
{
|
||||
// refPad has a hole and pad is on copper
|
||||
|
||||
auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_HOLE_CLEARANCE,
|
||||
refPad, pad );
|
||||
int minClearance = constraint.GetValue().Min();
|
||||
int actual;
|
||||
|
||||
accountCheck( constraint.GetParentRule() );
|
||||
|
||||
drc_dbg( 10,"check pad %p rule '%s' cl %d\n", refPad,
|
||||
constraint.GetParentRule()->m_Name, minClearance );
|
||||
|
||||
const std::shared_ptr<SHAPE>& padShape = pad->GetEffectiveShape();
|
||||
|
||||
if( padShape->Collide( refPad->GetEffectiveHoleShape(), minClearance, &actual ) )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
|
||||
|
||||
m_msg.Printf( drcItem->GetErrorText() + wxS( " " ) + _( "(%s clearance %s; actual %s)" ),
|
||||
constraint.GetName(),
|
||||
MessageTextFromValue( userUnits(), minClearance ),
|
||||
MessageTextFromValue( userUnits(), actual ) );
|
||||
|
||||
drcItem->SetErrorMessage( m_msg );
|
||||
drcItem->SetItems( refPad, pad );
|
||||
drcItem->SetViolatingRule( constraint.GetParentRule() );
|
||||
|
||||
reportViolation( drcItem, pad->GetPosition() );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
reportViolation( drce, (wxPoint) aHole->GetCenter() );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void DRC_TEST_PROVIDER_HOLE_CLEARANCE::addHole( const VECTOR2I& aLocation, int aRadius,
|
||||
BOARD_ITEM* aOwner )
|
||||
{
|
||||
DRILLED_HOLE hole;
|
||||
|
||||
hole.m_location = aLocation;
|
||||
hole.m_drillRadius = aRadius;
|
||||
hole.m_owner = aOwner;
|
||||
|
||||
m_largestRadius = std::max( m_largestRadius, aRadius );
|
||||
|
||||
m_drilledHoles.push_back( hole );
|
||||
}
|
||||
|
||||
|
||||
void DRC_TEST_PROVIDER_HOLE_CLEARANCE::testHoles2Holes()
|
||||
{
|
||||
const int delta = 50; // This is the number of tests between 2 calls to the progress bar
|
||||
|
||||
// Sort holes by X for performance. In the nested iteration we then need to look at
|
||||
// following holes only while they are within the refHole's neighborhood as defined by
|
||||
// the refHole radius + the minimum hole-to-hole clearance + the largest radius any of
|
||||
// the following holes can have.
|
||||
std::sort( m_drilledHoles.begin(), m_drilledHoles.end(),
|
||||
[]( const DRILLED_HOLE& a, const DRILLED_HOLE& b )
|
||||
{
|
||||
if( a.m_location.x == b.m_location.x )
|
||||
return a.m_location.y < b.m_location.y;
|
||||
else
|
||||
return a.m_location.x < b.m_location.x;
|
||||
} );
|
||||
|
||||
for( size_t ii = 0; ii < m_drilledHoles.size(); ++ii )
|
||||
{
|
||||
if( !reportProgress( ii, m_drilledHoles.size(), delta ) )
|
||||
break;
|
||||
|
||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_DRILLED_HOLES_TOO_CLOSE ) )
|
||||
break;
|
||||
|
||||
DRILLED_HOLE& refHole = m_drilledHoles[ ii ];
|
||||
int neighborhood = refHole.m_drillRadius + m_largestClearance + m_largestRadius;
|
||||
|
||||
for( size_t jj = ii + 1; jj < m_drilledHoles.size(); ++jj )
|
||||
{
|
||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_DRILLED_HOLES_TOO_CLOSE ) )
|
||||
break;
|
||||
|
||||
DRILLED_HOLE& checkHole = m_drilledHoles[ jj ];
|
||||
|
||||
if( refHole.m_location.x + neighborhood < checkHole.m_location.x )
|
||||
break;
|
||||
|
||||
// Holes with identical locations are allowable
|
||||
if( checkHole.m_location == refHole.m_location )
|
||||
continue;
|
||||
|
||||
int actual = ( checkHole.m_location - refHole.m_location ).EuclideanNorm();
|
||||
actual = std::max( 0, actual - checkHole.m_drillRadius - refHole.m_drillRadius );
|
||||
|
||||
auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_HOLE_CLEARANCE,
|
||||
refHole.m_owner, checkHole.m_owner );
|
||||
int minClearance = constraint.GetValue().Min();
|
||||
|
||||
accountCheck( constraint.GetParentRule() );
|
||||
|
||||
if( actual < minClearance )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_DRILLED_HOLES_TOO_CLOSE );
|
||||
|
||||
m_msg.Printf( drcItem->GetErrorText() + wxS( " " ) + _( "(%s clearance %s; actual %s)" ),
|
||||
constraint.GetName(),
|
||||
MessageTextFromValue( userUnits(), minClearance ),
|
||||
MessageTextFromValue( userUnits(), actual ) );
|
||||
|
||||
drcItem->SetErrorMessage( m_msg );
|
||||
drcItem->SetItems( refHole.m_owner, checkHole.m_owner );
|
||||
drcItem->SetViolatingRule( constraint.GetParentRule() );
|
||||
|
||||
reportViolation( drcItem, (wxPoint) refHole.m_location );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int DRC_TEST_PROVIDER_HOLE_CLEARANCE::GetNumPhases() const
|
||||
{
|
||||
return 2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -83,6 +83,9 @@ private:
|
|||
|
||||
bool DRC_TEST_PROVIDER_SILK_CLEARANCE::Run()
|
||||
{
|
||||
// This is the number of tests between 2 calls to the progress bar
|
||||
const int delta = 250;
|
||||
|
||||
m_board = m_drcEngine->GetBoard();
|
||||
|
||||
DRC_CONSTRAINT worstClearanceConstraint;
|
||||
|
@ -105,18 +108,31 @@ bool DRC_TEST_PROVIDER_SILK_CLEARANCE::Run()
|
|||
if( !reportPhase( _( "Checking silkscreen for overlapping items..." ) ) )
|
||||
return false;
|
||||
|
||||
DRC_RTREE silkTree, targetTree;
|
||||
DRC_RTREE silkTree;
|
||||
DRC_RTREE targetTree;
|
||||
int ii = 0;
|
||||
int targets = 0;
|
||||
|
||||
auto addToSilkTree =
|
||||
[&silkTree]( BOARD_ITEM *item ) -> bool
|
||||
[&silkTree]( BOARD_ITEM* item ) -> bool
|
||||
{
|
||||
silkTree.insert( item );
|
||||
return true;
|
||||
};
|
||||
|
||||
auto addToTargetTree =
|
||||
[&targetTree]( BOARD_ITEM *item ) -> bool
|
||||
auto countTargets =
|
||||
[&targets]( BOARD_ITEM* item ) -> bool
|
||||
{
|
||||
++targets;
|
||||
return true;
|
||||
};
|
||||
|
||||
auto addToTargetTree =
|
||||
[&]( BOARD_ITEM* item ) -> bool
|
||||
{
|
||||
if( !reportProgress( ii++, targets, delta ) )
|
||||
return false;
|
||||
|
||||
targetTree.insert( item );
|
||||
return true;
|
||||
};
|
||||
|
@ -187,6 +203,10 @@ bool DRC_TEST_PROVIDER_SILK_CLEARANCE::Run()
|
|||
};
|
||||
|
||||
forEachGeometryItem( s_allBasicItems, LSET( 2, F_SilkS, B_SilkS ), addToSilkTree );
|
||||
forEachGeometryItem( s_allBasicItems, LSET::FrontMask() | LSET::BackMask(), countTargets );
|
||||
|
||||
targets *= 2; // One for adding to RTree; one for testing
|
||||
|
||||
forEachGeometryItem( s_allBasicItems, LSET::FrontMask() | LSET::BackMask(), addToTargetTree );
|
||||
|
||||
reportAux( _("Testing %d silkscreen features against %d board items."),
|
||||
|
@ -213,13 +233,10 @@ bool DRC_TEST_PROVIDER_SILK_CLEARANCE::Run()
|
|||
DRC_RTREE::LAYER_PAIR( B_SilkS, Edge_Cuts )
|
||||
};
|
||||
|
||||
// This is the number of tests between 2 calls to the progress bar
|
||||
const int delta = 250;
|
||||
|
||||
targetTree.QueryCollidingPairs( &silkTree, layerPairs, checkClearance, m_largestClearance,
|
||||
[&]( int aCount, int aSize ) -> bool
|
||||
{
|
||||
return reportProgress( aCount, aSize, delta );
|
||||
return reportProgress( ++ii, targets, delta );
|
||||
} );
|
||||
|
||||
reportRuleStatistics();
|
||||
|
|
|
@ -125,20 +125,20 @@ bool DRC_TEST_PROVIDER_TRACK_WIDTH::Run()
|
|||
|
||||
if( fail_min )
|
||||
{
|
||||
m_msg.Printf( drcItem->GetErrorText() + wxS( " " ) + _( "(%s min width %s; actual %s)" ),
|
||||
m_msg.Printf( _( "(%s min width %s; actual %s)" ),
|
||||
constraint.GetName(),
|
||||
MessageTextFromValue( userUnits(), constraintWidth ),
|
||||
MessageTextFromValue( userUnits(), actual ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_msg.Printf( drcItem->GetErrorText() + wxS( " " ) + _( "(%s max width %s; actual %s)" ),
|
||||
m_msg.Printf( _( "(%s max width %s; actual %s)" ),
|
||||
constraint.GetName(),
|
||||
MessageTextFromValue( userUnits(), constraintWidth ),
|
||||
MessageTextFromValue( userUnits(), actual ) );
|
||||
}
|
||||
|
||||
drcItem->SetErrorMessage( m_msg );
|
||||
drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg );
|
||||
drcItem->SetItems( item );
|
||||
drcItem->SetViolatingRule( constraint.GetParentRule() );
|
||||
|
||||
|
|
|
@ -220,6 +220,8 @@ void DRC_TOOL::RunTests( PROGRESS_REPORTER* aProgressReporter, bool aTestTracksA
|
|||
}
|
||||
|
||||
commit.Push( _( "DRC" ), false );
|
||||
aProgressReporter->AdvancePhase( _( "Formatting results..." ) );
|
||||
wxSafeYield();
|
||||
|
||||
m_drcRunning = false;
|
||||
|
||||
|
|
Loading…
Reference in New Issue