39struct PREPARED_CANDIDATES_FIXTURE
41 void Load(
const wxString& aRelPath )
45 m_board->GetDesignSettings().m_DRCEngine->RunTests(
EDA_UNITS::MM,
true,
false );
46 BOOST_REQUIRE_GT( m_board->m_DRCMaxClearance, 0 );
49 SETTINGS_MANAGER m_settingsManager;
50 std::unique_ptr<BOARD> m_board;
56 BOARD_ITEM* aExceptItem =
nullptr,
int aExceptPadding = 0 )
61 if( !aItem->IsOnLayer( aLayer ) )
65 aItem == aExceptItem ? aExceptPadding : aPadding );
73 for(
PAD*
pad : footprint->Pads() )
82bool isSubsequence(
const std::vector<std::string>& aNeedle,
const std::vector<std::string>& aHaystack )
86 for(
const std::string& entry : aNeedle )
88 while( pos < aHaystack.size() && aHaystack[pos] != entry )
91 if( pos == aHaystack.size() )
101std::vector<BOARD_ITEM*> copperReferences(
BOARD* aBoard,
size_t aLimit )
103 std::vector<BOARD_ITEM*> refs;
107 if( refs.size() >= aLimit )
110 refs.push_back( track );
113 size_t padLimit = refs.size() + aLimit;
117 for(
PAD*
pad : footprint->Pads() )
119 if( refs.size() >= padLimit )
122 refs.push_back(
pad );
131 std::vector<std::string>* aFilterLog =
nullptr,
132 const std::string& aReject = std::string() )
134 std::vector<std::string> hits;
137 aRef, aLayer, aLayer,
143 aFilterLog->push_back( uuid );
145 return uuid != aReject;
159 const std::shared_ptr<SHAPE>& aRefShape,
PCB_LAYER_ID aLayer,
160 int aClearance,
bool aCredit,
161 std::vector<std::string>* aFilterLog =
nullptr,
162 const std::string& aReject = std::string(),
163 bool aCheckParentShapes =
false )
165 std::vector<std::string> hits;
168 aRef, aRefShape, aLayer,
174 aFilterLog->push_back( uuid );
176 return uuid != aReject;
178 [&](
BOARD_ITEM* aItem,
const std::shared_ptr<SHAPE>& aParentShape )
182 if( aCheckParentShapes )
194 aClearance, aCredit );
204 const wxString boards[] = { wxT(
"issue12609" ), wxT(
"issue6443" ), wxT(
"issue22102" ) };
206 size_t totalHits = 0;
207 size_t widestQuery = 0;
208 size_t negativeClearanceQueries = 0;
210 for(
const wxString& boardName : boards )
214 const int maxClearance = m_board->m_DRCMaxClearance;
215 const int clearances[] = { -maxClearance, 0, maxClearance / 2, maxClearance, maxClearance + 1 };
219 for(
BOARD_ITEM* ref : copperReferences( m_board.get(), 40 ) )
223 std::shared_ptr<SHAPE> refShape = ref->GetEffectiveShape( layer );
228 std::vector<std::string> legacy =
229 legacyCandidates( *m_board->m_CopperItemRTreeCache, ref, layer,
clearance );
230 std::vector<std::string> prepared =
231 preparedCandidates( *m_board->m_CopperItemRTreeCache, ref, refShape, layer,
clearance,
232 true,
nullptr, std::string(),
true );
236 totalHits += legacy.size();
237 widestQuery = std::max( widestQuery, legacy.size() );
240 ++negativeClearanceQueries;
248 BOOST_CHECK_GT( totalHits, 0u );
249 BOOST_CHECK_GT( widestQuery, 1u );
250 BOOST_CHECK_GT( negativeClearanceQueries, 0u );
256 Load( wxT(
"issue6443" ) );
258 const int maxClearance = m_board->m_DRCMaxClearance;
259 DRC_RTREE& tree = *m_board->m_CopperItemRTreeCache;
261 size_t plainInvocations = 0;
262 size_t creditInvocations = 0;
263 size_t rejections = 0;
265 for(
BOARD_ITEM* ref : copperReferences( m_board.get(), 40 ) )
269 std::shared_ptr<SHAPE> refShape = ref->GetEffectiveShape( layer );
273 std::vector<std::string> probe;
274 legacyCandidates( tree, ref, layer, maxClearance, &probe );
279 const std::string reject = probe.front();
282 std::vector<std::string> legacyLog;
283 std::vector<std::string> plainLog;
284 std::vector<std::string> creditLog;
286 std::vector<std::string> legacy =
287 legacyCandidates( tree, ref, layer, maxClearance, &legacyLog, reject );
288 std::vector<std::string> plain = preparedCandidates( tree, ref, refShape, layer, maxClearance,
false,
290 std::vector<std::string> credit = preparedCandidates( tree, ref, refShape, layer, maxClearance,
true,
291 &creditLog, reject );
300 BOOST_CHECK( isSubsequence( creditLog, plainLog ) );
304 std::vector<std::string> sortedCredit = creditLog;
305 std::sort( sortedCredit.begin(), sortedCredit.end() );
306 BOOST_CHECK( std::adjacent_find( sortedCredit.begin(), sortedCredit.end() ) == sortedCredit.end() );
308 plainInvocations += plainLog.size();
309 creditInvocations += creditLog.size();
313 BOOST_CHECK_GT( rejections, 0u );
317 BOOST_CHECK_LT( creditInvocations, plainInvocations );
325 const wxString
candidates[] = { wxT(
"issue12609" ), wxT(
"issue6443" ), wxT(
"issue22102" ),
326 wxT(
"issue5978" ), wxT(
"issue14008" ) };
332 int maxClearance = 0;
338 maxClearance = m_board->m_DRCMaxClearance;
341 insertCopperItems( uniform, m_board.get(), layer, maxClearance );
343 std::vector<BOARD_ITEM*> items = copperReferences( m_board.get(), 100000 );
347 if( !ref->IsOnLayer( layer ) )
350 std::vector<std::string> reachable = legacyCandidates( uniform, ref, layer, maxClearance );
354 if( candidate == ref || !candidate->IsOnLayer( layer ) )
357 if( ref->GetBoundingBox().Intersects( candidate->GetBoundingBox() ) )
360 if( std::find( reachable.begin(), reachable.end(),
361 candidate->m_Uuid.AsString().ToStdString() ) == reachable.end() )
367 farCandidate = candidate;
375 BOOST_TEST_MESSAGE( wxString::Format(
"MINPAD %s maxClearance=%d qualifyingPair=%s", boardName,
376 maxClearance, reference ?
"yes" :
"no" ) );
388 insertCopperItems( mixed, m_board.get(), layer, maxClearance, farCandidate, 0 );
391 std::vector<std::string>
expected = legacyCandidates( mixed, reference, layer, maxClearance );
392 std::vector<std::string> credit =
393 preparedCandidates( mixed, reference, refShape, layer, maxClearance,
true );
396 BOOST_CHECK( std::find( credit.begin(), credit.end(), farCandidate->
m_Uuid.
AsString().ToStdString() )
402 BOOST_CHECK_EQUAL( preparedCandidates( mixed, reference, refShape, layer, maxClearance,
true ).size(), 0u );
404 insertCopperItems( mixed, m_board.get(),
B_Cu, maxClearance / 2 );
406 for(
BOARD_ITEM* ref : copperReferences( m_board.get(), 40 ) )
408 if( !ref->IsOnLayer(
B_Cu ) )
411 std::shared_ptr<SHAPE> backShape = ref->GetEffectiveShape(
B_Cu );
412 std::vector<std::string> legacy = legacyCandidates( mixed, ref,
B_Cu, maxClearance );
413 std::vector<std::string> rebuilt =
414 preparedCandidates( mixed, ref, backShape,
B_Cu, maxClearance,
true );
428 const wxString boards[] = { wxT(
"issue12609" ), wxT(
"issue6443" ), wxT(
"issue22102" ),
429 wxT(
"issue14008" ), wxT(
"reverse_via" ) };
433 for(
const wxString& boardName : boards )
452 + wxT(
" does not contain its own shape" ) );
456 for(
PCB_TRACK* track : m_board->Tracks() )
459 for(
FOOTPRINT* footprint : m_board->Footprints() )
461 for(
PAD*
pad : footprint->Pads() )
467 BOOST_CHECK_GT( examined, 0u );
477 Load( wxT(
"issue22102" ) );
479 size_t copperGraphics = 0;
480 size_t uncontained = 0;
489 std::shared_ptr<SHAPE> shape = item->GetEffectiveShape( layer );
496 if( !item->GetBoundingBox().Contains( shape->BBox() ) )
500 BOOST_CHECK_GT( copperGraphics, 0u );
501 BOOST_CHECK_GT( uncontained, 0u );
507 const wxString boards[] = { wxT(
"fill_bad" ), wxT(
"intersectingzones" ), wxT(
"issue5750" ) };
509 size_t exercised = 0;
510 size_t multiCandidateQueries = 0;
512 for(
const wxString& boardName : boards )
519 index.Build( m_board->m_DRCCopperZonesByLayer );
521 for(
const auto& [layer, zones] : m_board->m_DRCCopperZonesByLayer )
523 for(
ZONE* zone : zones )
525 BOX2I query = zone->GetBoundingBox();
526 query.
Inflate( m_board->m_DRCMaxClearance );
530 std::vector<ZONE*>
actual;
532 for(
ZONE* candidate : zones )
534 if( query.
Intersects( candidate->GetBoundingBox() ) )
544 ++multiCandidateQueries;
549 std::vector<ZONE*> absent;
551 BOOST_CHECK( absent.empty() );
555 BOOST_CHECK( absent.empty() );
558 BOOST_CHECK_GT( exercised, 0u );
559 BOOST_CHECK_GT( multiCandidateQueries, 0u );
565 const wxString boards[] = { wxT(
"fill_bad" ), wxT(
"intersectingzones" ), wxT(
"issue5750" ) };
567 size_t totalPairs = 0;
568 size_t emptyLayers = 0;
569 size_t defaultBoxes = 0;
570 size_t noFillEntries = 0;
572 for(
const wxString& boardName : boards )
577 std::map<PCB_LAYER_ID, std::vector<size_t>> zoneIdxByLayer;
579 for(
size_t ii = 0; ii < m_board->m_DRCCopperZones.size(); ++ii )
581 ZONE* zone = m_board->m_DRCCopperZones[ii];
589 zoneIdxByLayer[layer].push_back( ii );
593 for(
const auto& [layer, indices] : zoneIdxByLayer )
596 [&](
size_t aIndex ) -> std::optional<BOX2I>
598 if(
SHAPE_POLY_SET* poly = m_board->m_DRCCopperZones[aIndex]->GetFill( layer ) )
599 return poly->BBoxFromCaches();
606 for(
size_t ia = 0; ia < indices.size(); ++ia )
608 SHAPE_POLY_SET* polyA = m_board->m_DRCCopperZones[indices[ia]]->GetFill( layer );
619 for(
size_t ib = ia + 1; ib < indices.size(); ++ib )
621 SHAPE_POLY_SET* polyB = m_board->m_DRCCopperZones[indices[ib]]->GetFill( layer );
626 expected.push_back( std::to_string( indices[ia] ) +
"-" + std::to_string( indices[ib] ) );
631 std::vector<std::string>
actual;
633 for(
const auto& [first, second] : pairs )
634 actual.push_back( std::to_string( first ) +
"-" + std::to_string( second ) );
638 totalPairs +=
actual.size();
643 std::set<size_t> participating;
645 for(
const auto& [first, second] : pairs )
647 participating.insert( first );
648 participating.insert( second );
653 for(
size_t idx : participating )
655 bool known = std::find( indices.begin(), indices.end(), idx ) != indices.end();
657 BOOST_CHECK( known );
661 BOOST_CHECK( m_board->m_DRCCopperZones[idx]->GetFill( layer ) !=
nullptr );
666 BOOST_TEST_MESSAGE( wxString::Format(
"ZONEPAIRS totalPairs=%zu emptyLayers=%zu defaultBoxes=%zu "
668 totalPairs, emptyLayers, defaultBoxes, noFillEntries ) );
670 BOOST_CHECK_GT( totalPairs, 0u );
671 BOOST_CHECK_GT( emptyLayers, 0u );
676 "defaultBoxes=%zu noFillEntries=%zu",
677 defaultBoxes, noFillEntries ) );
KICAD_PLUGIN_EXPORT SCENEGRAPH * Load(char const *aFileName)
Read a model file and creates a generic display structure.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT, DRC_CONSTRAINT_T aUsage=NULL_CONSTRAINT) const
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Information pertinent to a Pcbnew printed circuit board.
const FOOTPRINTS & Footprints() const
const TRACKS & Tracks() const
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
constexpr bool Contains(const Vec &aPoint) const
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Implement an R-tree for fast spatial and layer indexing of connectable items.
void Insert(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer, DRC_CONSTRAINT_T aConstraintType, int aWorstClearance=0, bool aAtomicTables=false)
Insert an item into the tree on a particular layer with an optional worst clearance.
int QueryColliding(BOARD_ITEM *aRefItem, PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer, std::function< bool(BOARD_ITEM *)> aFilter=nullptr, std::function< bool(BOARD_ITEM *)> aVisitor=nullptr, int aClearance=0) const
This is a fast test which essentially does bounding-box overlap given a worst-case clearance.
int QueryCollidingPreparedCopper(BOARD_ITEM *aRefItem, const std::shared_ptr< SHAPE > &aRefShape, PCB_LAYER_ID aTargetLayer, std::function< bool(BOARD_ITEM *)> aFilter, std::function< bool(BOARD_ITEM *, const std::shared_ptr< SHAPE > &)> aVisitor, int aClearance=0, bool aUsePaddingCredit=false) const
Same broad phase as QueryColliding(), but the caller supplies the reference shape and the visitor als...
void clear()
Remove all items from the RTree.
void Build()
Finalize all pending inserts by bulk-building packed R-trees from the staged items.
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
wxString AsString() const
LSET is a set of PCB_LAYER_IDs.
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Represent a set of closed polygons.
const BOX2I BBoxFromCaches() const
Handle a list of polygons defining a copper zone.
bool IsTeardropArea() const
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
std::vector< std::pair< size_t, size_t > > CollectOverlappingPairs(const std::vector< size_t > &aOrderedIndices, BOX_FN aBox)
Enumerate the pairs of aOrderedIndices whose boxes overlap, in the order a nested loop over the vecto...
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
PCB_LAYER_ID
A quick note on layer IDs:
void LoadBoard(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< BOARD > &aBoard)
BOOST_CHECK_EQUAL_COLLECTIONS(mixed.begin(), mixed.end(), expMixed.begin(), expMixed.end())
BOOST_FIXTURE_TEST_CASE(DRCPreparedCopperQueryMatchesLegacy, PREPARED_CANDIDATES_FIXTURE)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
VECTOR3I expected(15, 30, 45)
static std::vector< int > candidates(const SEGMENT_INDEX &aIndex, const SEG &aQuery, int aPadding)
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
BOOST_CHECK_EQUAL(result, "25.4")