KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_footprint_courtyard_index.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <set>
21
24#include <board.h>
25#include <footprint.h>
27
28/*
29 * The courtyard spatial index replaces the full-board scan that intersectsCourtyard()-style
30 * predicates used to run. It is only correct if it returns every footprint whose courtyard
31 * bounding box overlaps the query box, which is precisely the broad-phase test the downstream
32 * collision check applies. These tests pin that invariant against a brute-force reference so a
33 * future change to the index (e.g. indexing by footprint body instead of courtyard) cannot
34 * silently drop courtyard collisions.
35 */
36
37namespace
38{
39
40int mm( double aMillimetres )
41{
42 return pcbIUScale.mmToIU( aMillimetres );
43}
44
45
47FOOTPRINT* addFootprint( BOARD& aBoard, const wxString& aRef, const VECTOR2I& aPos,
48 const VECTOR2I& aCourtyardSize )
49{
50 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( &aBoard );
51
52 KI_TEST::DrawRect( *footprint, VECTOR2I( 0, 0 ), aCourtyardSize, 0, mm( 0.1 ), F_CrtYd );
53
54 footprint->SetReference( aRef );
55
56 // SetPosition moves the already-drawn graphics, so it must run after DrawRect for the
57 // courtyard to land at aPos rather than the origin.
58 footprint->SetPosition( aPos );
59
60 FOOTPRINT* raw = footprint.get();
61 aBoard.Add( footprint.release() );
62
63 return raw;
64}
65
66
68std::set<FOOTPRINT*> bruteForceOverlap( BOARD& aBoard, const BOX2I& aBox )
69{
70 std::set<FOOTPRINT*> result;
71
72 for( FOOTPRINT* footprint : aBoard.Footprints() )
73 {
74 BOX2I bbox;
75 bool hasCourtyard = false;
76
77 for( PCB_LAYER_ID side : { F_Cu, B_Cu } )
78 {
79 const SHAPE_POLY_SET& courtyard = footprint->GetCourtyard( side );
80
81 if( courtyard.OutlineCount() == 0 )
82 continue;
83
84 if( hasCourtyard )
85 bbox.Merge( courtyard.BBox() );
86 else
87 bbox = courtyard.BBox();
88
89 hasCourtyard = true;
90 }
91
92 if( hasCourtyard && bbox.Intersects( aBox ) )
93 result.insert( footprint );
94 }
95
96 return result;
97}
98
99
100std::set<FOOTPRINT*> indexOverlap( const FOOTPRINT_COURTYARD_INDEX& aIndex, const BOX2I& aBox )
101{
102 std::set<FOOTPRINT*> result;
103
104 aIndex.QueryOverlapping( aBox,
105 [&]( FOOTPRINT* aFootprint ) -> bool
106 {
107 result.insert( aFootprint );
108 return true;
109 } );
110
111 return result;
112}
113
114} // namespace
115
116
117BOOST_AUTO_TEST_SUITE( FootprintCourtyardIndex )
118
119
120BOOST_AUTO_TEST_CASE( MatchesBruteForceScan )
121{
122 BOARD board;
123
124 addFootprint( board, "U1", { 0, 0 }, { mm( 2 ), mm( 2 ) } );
125 addFootprint( board, "U2", { mm( 50 ), 0 }, { mm( 2 ), mm( 2 ) } );
126 addFootprint( board, "U3", { mm( 100 ), mm( 100 ) }, { mm( 2 ), mm( 2 ) } );
127
128 // A footprint whose courtyard is far larger than its body: indexing the body bounds instead
129 // of the courtyard bounds would wrongly exclude it from far-reaching queries.
130 addFootprint( board, "U4", { 0, mm( 50 ) }, { mm( 60 ), mm( 60 ) } );
131
132 // A footprint with no courtyard graphics must never be returned.
133 std::unique_ptr<FOOTPRINT> noCourtyard = std::make_unique<FOOTPRINT>( &board );
134 noCourtyard->SetReference( "U5" );
135 noCourtyard->SetPosition( { mm( 50 ), mm( 50 ) } );
136 board.Add( noCourtyard.release() );
137
139
140 const std::vector<BOX2I> queries = {
141 BOX2I( VECTOR2I( mm( -5 ), mm( -5 ) ), VECTOR2I( mm( 10 ), mm( 10 ) ) ), // around U1 only
142 BOX2I( VECTOR2I( mm( 200 ), mm( 200 ) ), VECTOR2I( mm( 10 ), mm( 10 ) ) ), // empty region
143 BOX2I( VECTOR2I( mm( 20 ), mm( 40 ) ), VECTOR2I( mm( 5 ), mm( 5 ) ) ), // only U4's large courtyard
144 BOX2I( VECTOR2I( mm( -50 ), mm( -50 ) ), VECTOR2I( mm( 250 ), mm( 250 ) ) ) // everything
145 };
146
147 for( const BOX2I& query : queries )
148 BOOST_CHECK( indexOverlap( index, query ) == bruteForceOverlap( board, query ) );
149}
150
151
int index
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
Construction utilities for PCB tests.
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:372
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1295
const FOOTPRINTS & Footprints() const
Definition board.h:420
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:654
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:307
Spatial index over footprint courtyard bounding boxes.
void QueryOverlapping(const BOX2I &aBox, const std::function< bool(FOOTPRINT *)> &aVisitor) const
Visit every footprint whose courtyard bounding box overlaps aBox.
Represent a set of closed polygons.
int OutlineCount() const
Return the number of outlines in the set.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ B_Cu
Definition layer_ids.h:61
@ F_Cu
Definition layer_ids.h:60
void DrawRect(FOOTPRINT &aFootprint, const VECTOR2I &aPos, const VECTOR2I &aSize, int aRadius, int aWidth, PCB_LAYER_ID aLayer)
Draw a rectangle on a footprint.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
wxString result
Test unit parsing edge cases and error handling.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683