KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_zone_filler.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, see AUTHORS.txt for contributors.
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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25#include <boost/test/data/test_case.hpp>
26
28#include <board.h>
30#include <pad.h>
31#include <pcb_track.h>
32#include <footprint.h>
33#include <zone.h>
34#include <drc/drc_item.h>
36
37
39{
41 m_settingsManager( true /* headless */ )
42 { }
43
45 std::unique_ptr<BOARD> m_board;
46};
47
48
49constexpr int delta = KiROUND( 0.006 * pcbIUScale.IU_PER_MM );
50
51
53{
54 KI_TEST::LoadBoard( m_settingsManager, "zone_filler", m_board );
55
56 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
57
58 KI_TEST::FillZones( m_board.get() );
59
60 // Now that the zones are filled we're going to increase the size of -some- pads and
61 // tracks so that they generate DRC errors. The test then makes sure that those errors
62 // are generated, and that the other pads and tracks do -not- generate errors.
63
64 for( PAD* pad : m_board->Footprints()[0]->Pads() )
65 {
66 if( pad->GetNumber() == "2" || pad->GetNumber() == "4" || pad->GetNumber() == "6" )
67 {
68 pad->SetSize( PADSTACK::ALL_LAYERS,
69 pad->GetSize( PADSTACK::ALL_LAYERS ) + VECTOR2I( delta, delta ) );
70 }
71 }
72
73 int ii = 0;
74 KIID arc8;
75 KIID arc12;
76
77 for( PCB_TRACK* track : m_board->Tracks() )
78 {
79 if( track->Type() == PCB_ARC_T )
80 {
81 ii++;
82
83 if( ii == 8 )
84 {
85 arc8 = track->m_Uuid;
86 track->SetWidth( track->GetWidth() + delta + delta );
87 }
88 else if( ii == 12 )
89 {
90 arc12 = track->m_Uuid;
91 track->Move( VECTOR2I( -delta, -delta ) );
92 }
93 }
94 }
95
96 bool foundPad2Error = false;
97 bool foundPad4Error = false;
98 bool foundPad6Error = false;
99 bool foundArc8Error = false;
100 bool foundArc12Error = false;
101 bool foundOtherError = false;
102
103 bds.m_DRCEngine->InitEngine( wxFileName() ); // Just to be sure to be sure
104
105 bds.m_DRCEngine->SetViolationHandler(
106 [&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, int aLayer,
107 DRC_CUSTOM_MARKER_HANDLER* aCustomHandler )
108 {
109 if( aItem->GetErrorCode() == DRCE_CLEARANCE )
110 {
111 BOARD_ITEM* item_a = m_board->GetItem( aItem->GetMainItemID() );
112 PAD* pad_a = dynamic_cast<PAD*>( item_a );
113 PCB_TRACK* trk_a = dynamic_cast<PCB_TRACK*>( item_a );
114
115 BOARD_ITEM* item_b = m_board->GetItem( aItem->GetAuxItemID() );
116 PAD* pad_b = dynamic_cast<PAD*>( item_b );
117 PCB_TRACK* trk_b = dynamic_cast<PCB_TRACK*>( item_b );
118
119 if( pad_a && pad_a->GetNumber() == "2" ) foundPad2Error = true;
120 else if( pad_a && pad_a->GetNumber() == "4" ) foundPad4Error = true;
121 else if( pad_a && pad_a->GetNumber() == "6" ) foundPad6Error = true;
122 else if( pad_b && pad_b->GetNumber() == "2" ) foundPad2Error = true;
123 else if( pad_b && pad_b->GetNumber() == "4" ) foundPad4Error = true;
124 else if( pad_b && pad_b->GetNumber() == "6" ) foundPad6Error = true;
125 else if( trk_a && trk_a->m_Uuid == arc8 ) foundArc8Error = true;
126 else if( trk_a && trk_a->m_Uuid == arc12 ) foundArc12Error = true;
127 else if( trk_b && trk_b->m_Uuid == arc8 ) foundArc8Error = true;
128 else if( trk_b && trk_b->m_Uuid == arc12 ) foundArc12Error = true;
129 else foundOtherError = true;
130
131 }
132 } );
133
134 bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false );
135
136 BOOST_CHECK_EQUAL( foundPad2Error, true );
137 BOOST_CHECK_EQUAL( foundPad4Error, true );
138 BOOST_CHECK_EQUAL( foundPad6Error, true );
139 BOOST_CHECK_EQUAL( foundArc8Error, true );
140 BOOST_CHECK_EQUAL( foundArc12Error, true );
141 BOOST_CHECK_EQUAL( foundOtherError, false );
142}
143
144
146{
147 KI_TEST::LoadBoard( m_settingsManager, "notched_zones", m_board );
148
149 // Older algorithms had trouble where the filleted zones intersected and left notches.
150 // See:
151 // https://gitlab.com/kicad/code/kicad/-/issues/2737
152 // https://gitlab.com/kicad/code/kicad/-/issues/2752
153 SHAPE_POLY_SET frontCopper;
154
155 KI_TEST::FillZones( m_board.get() );
156
157 frontCopper = SHAPE_POLY_SET();
158
159 for( ZONE* zone : m_board->Zones() )
160 {
161 if( zone->GetLayerSet().Contains( F_Cu ) )
162 {
163 frontCopper.BooleanAdd( *zone->GetFilledPolysList( F_Cu ) );
164 }
165 }
166
167 BOOST_CHECK_EQUAL( frontCopper.OutlineCount(), 2 );
168}
169
170
171static const std::vector<wxString> RegressionZoneFillTests_tests = {
172 "issue18",
173 "issue2568",
174 "issue3812",
175 "issue5102",
176 "issue5313",
177 "issue5320",
178 "issue5567",
179 "issue5830",
180 "issue6039",
181 "issue6260",
182 "issue6284",
183 "issue7086",
184 "issue14294", // Bad Clipper2 fill
185 "fill_bad" // Missing zone clearance expansion
186};
187
188
190 boost::unit_test::data::make( RegressionZoneFillTests_tests ), relPath )
191{
192 KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
193
194 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
195
196 KI_TEST::FillZones( m_board.get() );
197
198 std::vector<DRC_ITEM> violations;
199
200 bds.m_DRCEngine->SetViolationHandler(
201 [&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, int aLayer,
202 DRC_CUSTOM_MARKER_HANDLER* aCustomHandler )
203 {
204 if( aItem->GetErrorCode() == DRCE_CLEARANCE )
205 violations.push_back( *aItem );
206 } );
207
208 bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false );
209
210 if( violations.empty() )
211 {
212 BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning
213 BOOST_TEST_MESSAGE( (const char*)(wxString::Format( "Zone fill regression: %s passed", relPath ).utf8_str()) );
214 }
215 else
216 {
218
219 std::map<KIID, EDA_ITEM*> itemMap;
220 m_board->FillItemMap( itemMap );
221
222 for( const DRC_ITEM& item : violations )
223 {
224 BOOST_TEST_MESSAGE( item.ShowReport( &unitsProvider, RPT_SEVERITY_ERROR,
225 itemMap ) );
226 }
227
228 BOOST_ERROR( (const char*)(wxString::Format( "Zone fill regression: %s failed", relPath ).utf8_str()) );
229 }
230}
231
232
233static const std::vector<wxString> RegressionSliverZoneFillTests_tests = {
234 "issue16182" // Slivers
235};
236
237
238BOOST_DATA_TEST_CASE_F( ZONE_FILL_TEST_FIXTURE, RegressionSliverZoneFillTests,
239 boost::unit_test::data::make( RegressionSliverZoneFillTests_tests ), relPath )
240{
241 KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
242
243 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
244
245 KI_TEST::FillZones( m_board.get() );
246
247 std::vector<DRC_ITEM> violations;
248
249 bds.m_DRCEngine->SetViolationHandler(
250 [&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, int aLayer,
251 DRC_CUSTOM_MARKER_HANDLER* aCustomHandler )
252 {
253 if( aItem->GetErrorCode() == DRCE_COPPER_SLIVER )
254 violations.push_back( *aItem );
255 } );
256
257 bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false );
258
259 if( violations.empty() )
260 {
261 BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning
262 BOOST_TEST_MESSAGE( (const char*)(wxString::Format( "Zone fill copper sliver regression: %s passed", relPath ).utf8_str()) );
263 }
264 else
265 {
267
268 std::map<KIID, EDA_ITEM*> itemMap;
269 m_board->FillItemMap( itemMap );
270
271 for( const DRC_ITEM& item : violations )
272 {
273 BOOST_TEST_MESSAGE( item.ShowReport( &unitsProvider, RPT_SEVERITY_ERROR,
274 itemMap ) );
275 }
276
277 BOOST_ERROR( (const char*)(wxString::Format( "Zone fill copper sliver regression: %s failed", relPath ).utf8_str()) );
278 }
279}
280
281
282static const std::vector<std::pair<wxString,int>> RegressionTeardropFill_tests = {
283 { "teardrop_issue_JPC2", 5 }, // Arcs with teardrops connecting to pads
284};
285
286
288 boost::unit_test::data::make( RegressionTeardropFill_tests ), test )
289{
290 const wxString& relPath = test.first;
291 const int count = test.second;
292
293 KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
294
295 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
296
297 KI_TEST::FillZones( m_board.get() );
298
299 int zoneCount = 0;
300
301 for( ZONE* zone : m_board->Zones() )
302 {
303 if( zone->IsTeardropArea() )
304 zoneCount++;
305 }
306
307 BOOST_CHECK_MESSAGE( zoneCount == count, "Expected " << count << " teardrop zones in "
308 << relPath << ", found "
309 << zoneCount );
310}
311
312
314{
315
316 std::vector<wxString> tests = { { "issue19956/issue19956" } // Arcs with teardrops connecting to pads
317 };
318
319 for( const wxString& relPath : tests )
320 {
321 KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
322 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
323 KI_TEST::FillZones( m_board.get() );
324
325 for( ZONE* zone : m_board->Zones() )
326 {
327 for( PCB_LAYER_ID layer : zone->GetLayerSet() )
328 {
329 std::shared_ptr<SHAPE> a_shape( zone->GetEffectiveShape( layer ) );
330
331 for( PAD* pad : m_board->GetPads() )
332 {
333 std::shared_ptr<SHAPE> pad_shape( pad->GetEffectiveShape( layer ) );
334 int clearance = pad_shape->GetClearance( a_shape.get() );
335 BOOST_CHECK_MESSAGE( pad->GetNetCode() == zone->GetNetCode() || clearance != 0,
336 "Pad " << pad->GetNumber() << " from Footprint "
337 << pad->GetParentFootprint()
338 ->GetReferenceAsString()
339 .ToStdString()
340 << " has net code "
341 << pad->GetNetname().ToStdString()
342 << " and is connected to zone with net code "
343 << zone->GetNetname().ToStdString() );
344 }
345 }
346 }
347 }
348}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
Container for design settings for a BOARD object.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
Definition: kiid.h:49
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:144
Definition: pad.h:54
Represent a set of closed polygons.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
int OutlineCount() const
Return the number of outlines in the set.
Handle a list of polygons defining a copper zone.
Definition: zone.h:73
std::function< void(PCB_MARKER *aMarker)> DRC_CUSTOM_MARKER_HANDLER
Definition: drc_engine.h:69
@ DRCE_CLEARANCE
Definition: drc_item.h:43
@ DRCE_COPPER_SLIVER
Definition: drc_item.h:92
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ F_Cu
Definition: layer_ids.h:64
void LoadBoard(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< BOARD > &aBoard)
void FillZones(BOARD *m_board)
@ RPT_SEVERITY_ERROR
const double IU_PER_MM
Definition: base_units.h:76
std::unique_ptr< BOARD > m_board
SETTINGS_MANAGER m_settingsManager
BOOST_CHECK_EQUAL(ret, c.m_exp_result)
int clearance
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
static const std::vector< wxString > RegressionZoneFillTests_tests
static const std::vector< std::pair< wxString, int > > RegressionTeardropFill_tests
constexpr int delta
BOOST_DATA_TEST_CASE_F(ZONE_FILL_TEST_FIXTURE, RegressionZoneFillTests, boost::unit_test::data::make(RegressionZoneFillTests_tests), relPath)
static const std::vector< wxString > RegressionSliverZoneFillTests_tests
BOOST_FIXTURE_TEST_CASE(BasicZoneFills, ZONE_FILL_TEST_FIXTURE)
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695