KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_kicad_sexpr.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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <algorithm>
21#include <chrono>
22#include <filesystem>
23#include <fstream>
24#include <memory>
25#include <sstream>
26#include <string>
27#include <thread>
28
29#include <qa_utils/file_utils.h>
33
36#include <progress_reporter.h>
37#include <richio.h>
38
39#include <board.h>
43#include <footprint.h>
44#include <netinfo.h>
45#include <pad.h>
46#include <pcb_shape.h>
47#include <pcb_track.h>
48#include <zone.h>
49
50
51#define CHECK_ENUM_CLASS_EQUAL( L, R ) \
52 BOOST_CHECK_EQUAL( static_cast<int>( L ), static_cast<int>( R ) )
53
54
61
62
66BOOST_FIXTURE_TEST_SUITE( KiCadSexprIO, KICAD_SEXPR_FIXTURE )
67
68
69
72BOOST_AUTO_TEST_CASE( Issue19775_ZoneLayerWildcards )
73{
74 std::string dataPath = KI_TEST::GetPcbnewTestDataDir() + "plugins/kicad_sexpr/Issue19775_ZoneLayers/";
75
76 BOOST_TEST_CONTEXT( "Zone layers with wildcards" )
77 {
78 std::unique_ptr<BOARD> testBoard = kicadPlugin.LoadBoard( dataPath + "LayerWildcard.kicad_pcb" );
79
80 // One zone in the file
81 BOOST_CHECK( testBoard->Zones().size() == 1 );
82
83 ZONE* z = testBoard->Zones()[0];
84
85 // On both front and back layers, with zone fill on both
86 BOOST_CHECK( z->GetLayerSet().Contains( F_Cu ) && z->GetLayerSet().Contains( B_Cu ) );
87 BOOST_CHECK( z->GetFilledPolysList( F_Cu )->TotalVertices() > 0 );
88 BOOST_CHECK( z->GetFilledPolysList( B_Cu )->TotalVertices() > 0 );
89 }
90
91 BOOST_TEST_CONTEXT( "Round trip layers" )
92 {
93 auto tmpBoard = std::filesystem::temp_directory_path() / "Issue19775_RoundTrip.kicad_pcb";
94
95 // Load and save the board from above to test how we write the zones into it
96 {
97 std::unique_ptr<BOARD> testBoard = kicadPlugin.LoadBoard( dataPath + "LayerEnumerate.kicad_pcb" );
98 kicadPlugin.SaveBoard( tmpBoard.string(), *testBoard );
99 }
100
101 // Read the new board
102 std::unique_ptr<BOARD> testBoard = kicadPlugin.LoadBoard( tmpBoard.string() );
103
104 // One zone in the file
105 BOOST_CHECK( testBoard->Zones().size() == 1 );
106
107 ZONE* z = testBoard->Zones()[0];
108
109 // On both front and back layers, with zone fill on both
110 BOOST_CHECK( z->GetLayerSet().Contains( F_Cu ) && z->GetLayerSet().Contains( B_Cu ) );
111 BOOST_CHECK( z->GetFilledPolysList( F_Cu )->TotalVertices() > 0 );
112 BOOST_CHECK( z->GetFilledPolysList( B_Cu )->TotalVertices() > 0 );
113 BOOST_CHECK( z->LayerProperties().contains( F_Cu ) );
114 }
115}
116
117
125BOOST_AUTO_TEST_CASE( Issue23125_EmptyZoneDiscarded )
126{
127 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
128 + "plugins/kicad_sexpr/Issue23125_EmptyZone/";
129
130 std::unique_ptr<BOARD> testBoard = kicadPlugin.LoadBoard( dataPath + "EmptyZone.kicad_pcb" );
131
132 // The file contains 3 zones: 1 valid (with polygon) and 2 empty (no polygon).
133 // The 2 empty zones should have been discarded during loading.
134 BOOST_CHECK_EQUAL( testBoard->Zones().size(), 1 );
135
136 // The surviving zone should have a valid position
137 ZONE* z = testBoard->Zones()[0];
138 BOOST_CHECK_NO_THROW( z->GetPosition() );
139 BOOST_CHECK( z->GetNumCorners() > 0 );
140}
141
142
148BOOST_AUTO_TEST_CASE( CoincidentDrawingsSurviveSave )
149{
150 // The writer sorts drawings into a std::set, so a cmp_drawings that reports equality for
151 // two coincident shapes silently drops one of them
152 BOARD board;
153
154 for( int ii = 0; ii < 2; ++ii )
155 {
156 PCB_SHAPE* line = new PCB_SHAPE( &board, SHAPE_T::SEGMENT );
157
158 line->SetLayer( F_SilkS );
159 line->SetStart( VECTOR2I( 0, 0 ) );
160 line->SetEnd( VECTOR2I( pcbIUScale.mmToIU( 5.0 ), 0 ) );
161 line->SetWidth( pcbIUScale.mmToIU( 0.1 ) );
162
163 board.Add( line, ADD_MODE::APPEND );
164 }
165
166 BOOST_REQUIRE_EQUAL( board.Drawings().size(), 2u );
167
168 const wxString path =
169 ( std::filesystem::temp_directory_path() / "qa_coincident_drawings.kicad_pcb" ).string();
170
171 kicadPlugin.SaveBoard( path, board );
172
173 std::unique_ptr<BOARD> reloaded = kicadPlugin.LoadBoard( path );
174
175 BOOST_REQUIRE( reloaded != nullptr );
176 BOOST_CHECK_EQUAL( reloaded->Drawings().size(), 2u );
177
178 std::filesystem::remove( path.ToStdString() );
179}
180
181
182BOOST_AUTO_TEST_CASE( ScientificNotationLoading )
183{
184 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
185 + "plugins/kicad_sexpr/";
186
187 std::unique_ptr<BOARD> testBoard = kicadPlugin.LoadBoard( dataPath + "ScientificNotation.kicad_pcb" );
188
189 // The file contains 1 arc with scientific notation in its coordinates
190 BOOST_CHECK_EQUAL( testBoard->Drawings().size(), 1 );
191
192 PCB_SHAPE* arc = dynamic_cast<PCB_SHAPE*>( testBoard->Drawings().front() );
193
194 // The arc's midpoint should be located at (4.17, -4.5e-05) in file units
195 BOOST_REQUIRE( arc );
196 BOOST_TEST( arc->GetArcMid().x == 4170000 );
197 BOOST_TEST( arc->GetArcMid().y == -45 );
198}
199
200
207BOOST_AUTO_TEST_CASE( Issue23625_CorruptedStackupCapped )
208{
209 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
210 + "plugins/kicad_sexpr/Issue23625_CorruptedStackup/";
211
212 std::unique_ptr<BOARD> testBoard;
213
214 BOOST_CHECK_NO_THROW( testBoard = kicadPlugin.LoadBoard( dataPath + "corrupted_stackup.kicad_pcb" ) );
215
216 const BOARD_STACKUP& stackup =
217 testBoard->GetDesignSettings().GetStackupDescriptor();
218
219 // The test file has 200 dielectric layers (plus copper, silk, mask), well
220 // beyond the parser's 128-item cap. After loading, the count must be
221 // clamped and the board must still be usable.
222 BOOST_CHECK_LE( stackup.GetCount(), 128 );
223 BOOST_CHECK_GT( stackup.GetCount(), 0 );
224}
225
226
234BOOST_AUTO_TEST_CASE( Issue24133_DuplicatedStackupNotInflated )
235{
236 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
237 + "plugins/kicad_sexpr/Issue24133_DuplicatedStackup/";
238
239 std::unique_ptr<BOARD> testBoard;
240
241 BOOST_CHECK_NO_THROW( testBoard = kicadPlugin.LoadBoard( dataPath + "duplicated_stackup.kicad_pcb" ) );
242
243 const BOARD_STACKUP& stackup = testBoard->GetDesignSettings().GetStackupDescriptor();
244
245 // The file repeats the 9-item layer sequence three times. Only the first copy must be kept.
246 BOOST_CHECK_EQUAL( stackup.GetCount(), 9 );
247
248 // A single copy is a standard 1.6 mm two-layer board. Without dedup the three copies would
249 // sum to 4.8 mm.
251}
252
253
260BOOST_AUTO_TEST_CASE( Issue23752_AppendBoardPreservesStackupAndGrowsToSixCopperLayers )
261{
262 std::string dataPath = KI_TEST::GetPcbnewTestDataDir();
263
264 // four layer board
265 std::string destinationPath = dataPath + "issue3812.kicad_pcb";
266 // six layer board
267 std::string sourcePath = dataPath + "issue18142.kicad_pcb";
268 std::map<std::string, UTF8> props;
269
270 // basically, make sure that we start with a four layer board, append a six layer
271 // board like this is a design block apply-layout with six layers, and make
272 // sure we end up with a six layer board and a six layer stackup and not a
273 // ten layer board
274 //
275 // we also need to test the stackup properties are preserved, because the increase
276 // in copper layers should cause a stackup growth from from four to six layers,
277 // but not replace the stackup with the source board's stackup, which has different properties (e.g. finish type)
279
280 auto countCopperLayers = []( const BOARD_STACKUP& aStackup )
281 {
282 int copperLayerCount = 0;
283
284 for( BOARD_STACKUP_ITEM* item : aStackup.GetList() )
285 {
286 if( item->GetType() == BS_ITEM_TYPE_COPPER )
287 ++copperLayerCount;
288 }
289
290 return copperLayerCount;
291 };
292
293 auto findFirstDielectric = []( const BOARD_STACKUP& aStackup ) -> const BOARD_STACKUP_ITEM*
294 {
295 for( BOARD_STACKUP_ITEM* item : aStackup.GetList() )
296 {
297 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
298 return item;
299 }
300
301 return nullptr;
302 };
303
304 std::unique_ptr<BOARD> testBoard = kicadPlugin.LoadBoard( destinationPath );
305
306 const BOARD_STACKUP& initialStackup = testBoard->GetDesignSettings().GetStackupDescriptor();
307 const BOARD_STACKUP_ITEM* initialFirstDielectric = findFirstDielectric( initialStackup );
308 const int initialCopperLayerCount = testBoard->GetCopperLayerCount();
309 const LSET initialEnabledLayers = testBoard->GetEnabledLayers();
310 const wxString initialFinishType = initialStackup.m_FinishType;
311
312 BOOST_REQUIRE_EQUAL( initialCopperLayerCount, 4 );
313 BOOST_REQUIRE_EQUAL( countCopperLayers( initialStackup ), 4 );
314 BOOST_REQUIRE( initialFirstDielectric );
315 BOOST_REQUIRE_EQUAL( initialFinishType, wxS( "ENIG" ) );
316 const wxString initialFirstDielectricMaterial = initialFirstDielectric->GetMaterial();
317 const int initialFirstDielectricThickness = initialFirstDielectric->GetThickness();
318
319 kicadPlugin.LoadAndAppendBoard( sourcePath, *testBoard, &props );
320
321 const int appendedCopperLayerCount = testBoard->GetCopperLayerCount();
322
323 if( appendedCopperLayerCount > initialCopperLayerCount )
324 testBoard->SetCopperLayerCount( appendedCopperLayerCount );
325
326 LSET enabledLayers = testBoard->GetEnabledLayers();
327 enabledLayers |= initialEnabledLayers;
328 testBoard->SetEnabledLayers( enabledLayers );
329 testBoard->GetDesignSettings().GetStackupDescriptor().SynchronizeWithBoard( &testBoard->GetDesignSettings() );
330
331 const BOARD_STACKUP& finalStackup = testBoard->GetDesignSettings().GetStackupDescriptor();
332 const BOARD_STACKUP_ITEM* finalFirstDielectric = findFirstDielectric( finalStackup );
333
334 BOOST_CHECK_EQUAL( testBoard->GetCopperLayerCount(), 6 );
335 BOOST_CHECK_EQUAL( countCopperLayers( finalStackup ), 6 );
336 BOOST_REQUIRE( finalFirstDielectric );
337 BOOST_CHECK_EQUAL( finalStackup.m_FinishType, initialFinishType );
338 BOOST_CHECK_EQUAL( finalFirstDielectric->GetMaterial(), initialFirstDielectricMaterial );
339 BOOST_CHECK_EQUAL( finalFirstDielectric->GetThickness(), initialFirstDielectricThickness );
340}
341
342
350BOOST_AUTO_TEST_CASE( Issue24642_AppendBoardPreservesDestinationLayerNames )
351{
352 std::string dataPath = KI_TEST::GetPcbnewTestDataDir();
353
354 // four layer board with custom copper layer names
355 std::string destinationPath = dataPath + "issue3812.kicad_pcb";
356 // six layer board using the standard copper layer names
357 std::string sourcePath = dataPath + "issue18142.kicad_pcb";
358
359 std::map<std::string, UTF8> props;
361
362 std::unique_ptr<BOARD> testBoard = kicadPlugin.LoadBoard( destinationPath );
363
364 BOOST_REQUIRE_EQUAL( testBoard->GetLayerName( F_Cu ), wxS( "Top_layer" ) );
365 BOOST_REQUIRE_EQUAL( testBoard->GetLayerName( In1_Cu ), wxS( "GND_layer" ) );
366 BOOST_REQUIRE_EQUAL( testBoard->GetLayerName( In2_Cu ), wxS( "VDD_layer" ) );
367 BOOST_REQUIRE_EQUAL( testBoard->GetLayerName( B_Cu ), wxS( "Bottom_layer" ) );
368
369 kicadPlugin.LoadAndAppendBoard( sourcePath, *testBoard, &props );
370
371 // The destination layer names must survive the append
372 BOOST_CHECK_EQUAL( testBoard->GetLayerName( F_Cu ), wxS( "Top_layer" ) );
373 BOOST_CHECK_EQUAL( testBoard->GetLayerName( In1_Cu ), wxS( "GND_layer" ) );
374 BOOST_CHECK_EQUAL( testBoard->GetLayerName( In2_Cu ), wxS( "VDD_layer" ) );
375 BOOST_CHECK_EQUAL( testBoard->GetLayerName( B_Cu ), wxS( "Bottom_layer" ) );
376
377 // The two extra copper layers from the source board are still added
378 BOOST_CHECK( testBoard->IsLayerEnabled( In3_Cu ) );
379 BOOST_CHECK( testBoard->IsLayerEnabled( In4_Cu ) );
380 BOOST_CHECK_EQUAL( testBoard->GetCopperLayerCount(), 6 );
381}
382
383
390BOOST_AUTO_TEST_CASE( Issue24642_AppendBoardRemapsLayersViaHandler )
391{
392 std::string dataPath = KI_TEST::GetPcbnewTestDataDir();
393 // four layer board with custom copper names and tracks on In2.Cu
394 std::string sourcePath = dataPath + "issue3812.kicad_pcb";
395
396 std::map<std::string, UTF8> props;
398
399 auto countTracksOn = []( BOARD* aBoard, PCB_LAYER_ID aLayer )
400 {
401 int n = 0;
402
403 for( PCB_TRACK* track : aBoard->Tracks() )
404 {
405 if( track->GetLayer() == aLayer )
406 n++;
407 }
408
409 return n;
410 };
411
412 // Source copper user names as the handler sees them
413 const wxString fName = wxS( "Top_layer" );
414 const wxString in1Name = wxS( "GND_layer" );
415 const wxString in2Name = wxS( "VDD_layer" );
416 const wxString bName = wxS( "Bottom_layer" );
417
418 // Identity mapping: appended layers stay where they are
419 std::vector<wxString> seenNames;
420
421 kicadPlugin.RegisterCallback(
422 [&]( const std::vector<INPUT_LAYER_DESC>& aDescs ) -> std::map<wxString, PCB_LAYER_ID>
423 {
424 for( const INPUT_LAYER_DESC& desc : aDescs )
425 seenNames.push_back( desc.Name );
426
427 return { { fName, F_Cu }, { in1Name, In1_Cu }, { in2Name, In2_Cu }, { bName, B_Cu } };
428 } );
429
430 std::unique_ptr<BOARD> identityBoard = std::make_unique<BOARD>();
431
432 kicadPlugin.LoadAndAppendBoard( sourcePath, *identityBoard, &props );
433
434 // The dialog must see the source board's own layer names, not the canonical defaults
435 BOOST_CHECK( std::find( seenNames.begin(), seenNames.end(), in1Name ) != seenNames.end() );
436 BOOST_CHECK( std::find( seenNames.begin(), seenNames.end(), in2Name ) != seenNames.end() );
437
438 const int idIn1 = countTracksOn( identityBoard.get(), In1_Cu );
439 const int idIn2 = countTracksOn( identityBoard.get(), In2_Cu );
440
441 BOOST_REQUIRE_GT( idIn2, 0 ); // the source has tracks on In2.Cu
442
443 // Swap mapping: appended In1 <-> In2
444 kicadPlugin.RegisterCallback(
445 [&]( const std::vector<INPUT_LAYER_DESC>& ) -> std::map<wxString, PCB_LAYER_ID>
446 {
447 return { { fName, F_Cu }, { in1Name, In2_Cu }, { in2Name, In1_Cu }, { bName, B_Cu } };
448 } );
449
450 std::unique_ptr<BOARD> swapBoard = std::make_unique<BOARD>();
451
452 kicadPlugin.LoadAndAppendBoard( sourcePath, *swapBoard, &props );
453
454 const int swIn1 = countTracksOn( swapBoard.get(), In1_Cu );
455 const int swIn2 = countTracksOn( swapBoard.get(), In2_Cu );
456
457 // The inner-copper track counts must have swapped
458 BOOST_CHECK_EQUAL( swIn1, idIn2 );
459 BOOST_CHECK_EQUAL( swIn2, idIn1 );
460}
461
462
468BOOST_AUTO_TEST_CASE( Issue24642_AppendBoardPromptsOnlyOnNameMismatch )
469{
470 std::string dataPath = KI_TEST::GetPcbnewTestDataDir();
471 std::string standardNames = dataPath + "issue18142.kicad_pcb"; // six layer, default names
472 std::string customNames = dataPath + "issue3812.kicad_pcb"; // four layer, custom copper names
473
474 std::map<std::string, UTF8> props;
476
477 bool handlerCalled = false;
478
479 kicadPlugin.RegisterCallback(
480 [&]( const std::vector<INPUT_LAYER_DESC>& ) -> std::map<wxString, PCB_LAYER_ID>
481 {
482 handlerCalled = true;
483 return {};
484 } );
485
486 // Appending onto a board with identical layer names must not prompt
487 {
488 std::unique_ptr<BOARD> matchBoard = kicadPlugin.LoadBoard( standardNames );
489 handlerCalled = false;
490 kicadPlugin.LoadAndAppendBoard( standardNames, *matchBoard, &props );
491 BOOST_CHECK( !handlerCalled );
492 }
493
494 // Appending a board with differently-named layers must prompt
495 {
496 std::unique_ptr<BOARD> mismatchBoard = kicadPlugin.LoadBoard( standardNames );
497 handlerCalled = false;
498 kicadPlugin.LoadAndAppendBoard( customNames, *mismatchBoard, &props );
499 BOOST_CHECK( handlerCalled );
500 }
501}
502
503
521BOOST_AUTO_TEST_CASE( FootprintSave_OmitsNetsOnAllBoardConnectedItems )
522{
523 auto tmpLib = std::filesystem::temp_directory_path() / "qa_fp_save_netinfo.pretty";
524 std::filesystem::remove_all( tmpLib );
525 std::filesystem::create_directories( tmpLib );
526
527 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
528
529 NETINFO_ITEM* net = new NETINFO_ITEM( board.get(), wxT( "TestNet" ), 1 );
530 board->Add( net );
531
532 FOOTPRINT* fp = new FOOTPRINT( board.get() );
533 board->Add( fp );
534 fp->SetFPID( LIB_ID( wxT( "scratch" ), wxT( "test_fp_save_netinfo" ) ) );
535
536 PAD* pad = new PAD( fp );
537 pad->SetPadstackMode( PADSTACK::MODE::NORMAL );
539 pad->SetSize( PADSTACK::ALL_LAYERS,
540 VECTOR2I( pcbIUScale.mmToIU( 1 ), pcbIUScale.mmToIU( 1 ) ) );
541 pad->SetNet( net );
542 fp->Add( pad );
543
544 PCB_SHAPE* shape = new PCB_SHAPE( fp, SHAPE_T::SEGMENT );
545 shape->SetLayer( F_Cu );
546 shape->SetStart( VECTOR2I( 0, 0 ) );
547 shape->SetEnd( VECTOR2I( pcbIUScale.mmToIU( 5 ), 0 ) );
548 shape->SetNet( net );
549 fp->Add( shape );
550
551 ZONE* zone = new ZONE( fp );
552 zone->SetLayer( F_Cu );
553 zone->SetNet( net );
554 fp->Add( zone );
555
556 BOOST_REQUIRE_EQUAL( pad->GetNet(), net );
557 BOOST_REQUIRE_EQUAL( shape->GetNet(), net );
558 BOOST_REQUIRE_EQUAL( zone->GetNet(), net );
559
560 // Save into the scratch library via the public library-save API. Must not throw.
561 BOOST_REQUIRE_NO_THROW( kicadPlugin.FootprintSave( tmpLib.string(), fp ) );
562
563 auto savedFile = tmpLib / "test_fp_save_netinfo.kicad_mod";
564 BOOST_REQUIRE( std::filesystem::exists( savedFile ) );
565
566 std::ifstream in( savedFile );
567 BOOST_REQUIRE_MESSAGE( in.is_open(),
568 "Failed to open serialized footprint: " << savedFile.string() );
569
570 std::stringstream ss;
571 ss << in.rdbuf();
572 BOOST_REQUIRE( !in.bad() );
573
574 // Windows refuses to unlink a file that still has an open handle, so release it before the
575 // remove_all() below.
576 in.close();
577
578 const std::string contents = ss.str();
579 BOOST_REQUIRE( !contents.empty() );
580
581 BOOST_CHECK_MESSAGE( contents.find( "(net " ) == std::string::npos,
582 "Saved footprint library file must not contain (net ...) tokens:\n"
583 << contents );
584
585 // Verify the second defense directly on the save-path's transient state: clone the
586 // footprint, detach it from its parent board exactly as FootprintSave does, invoke
587 // ClearAllNets(), and assert every BOARD_CONNECTED_ITEM descendant is orphaned. This
588 // catches a regression where FootprintSave stops calling ClearAllNets() even if the
589 // serializer guards keep the file contents looking correct.
590 std::unique_ptr<FOOTPRINT> detached( static_cast<FOOTPRINT*>( fp->Clone() ) );
591 detached->SetParent( nullptr );
592 detached->SetParentGroup( nullptr );
593 detached->ClearAllNets();
594
595 BOARD_CONNECTED_ITEM* detachedPad = nullptr;
596 BOARD_CONNECTED_ITEM* detachedShape = nullptr;
597 BOARD_CONNECTED_ITEM* detachedZone = nullptr;
598
599 detached->RunOnChildren(
600 [&]( BOARD_ITEM* aItem )
601 {
602 switch( aItem->Type() )
603 {
604 case PCB_PAD_T: detachedPad = static_cast<BOARD_CONNECTED_ITEM*>( aItem ); break;
605 case PCB_SHAPE_T: detachedShape = static_cast<BOARD_CONNECTED_ITEM*>( aItem ); break;
606 case PCB_ZONE_T: detachedZone = static_cast<BOARD_CONNECTED_ITEM*>( aItem ); break;
607 default: break;
608 }
609 },
611
612 BOOST_REQUIRE( detachedPad );
613 BOOST_REQUIRE( detachedShape );
614 BOOST_REQUIRE( detachedZone );
618
619 std::filesystem::remove_all( tmpLib );
620}
621
622
631BOOST_AUTO_TEST_CASE( CopperThievingZone_RoundTrip )
632{
633 std::unique_ptr<BOARD> writeBoard = std::make_unique<BOARD>();
634
635 ZONE* zone = new ZONE( writeBoard.get() );
636 zone->SetLayer( F_Cu );
637 zone->AppendCorner( VECTOR2I( 0, 0 ), -1 );
638 zone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( 10 ), 0 ), -1 );
639 zone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ), -1 );
640 zone->AppendCorner( VECTOR2I( 0, pcbIUScale.mmToIU( 10 ) ), -1 );
642
643 THIEVING_SETTINGS thieving;
645 thieving.element_size = pcbIUScale.mmToIU( 0.42 );
646 thieving.gap = pcbIUScale.mmToIU( 1.27 );
647 thieving.line_width = pcbIUScale.mmToIU( 0.35 );
648 thieving.stagger = true;
649 thieving.orientation = EDA_ANGLE( 30.0, DEGREES_T );
650 zone->SetThievingSettings( thieving );
651
652 writeBoard->Add( zone );
653
654 std::filesystem::path tmpPath = std::filesystem::temp_directory_path()
655 / "copper_thieving_roundtrip.kicad_pcb";
656
657 PCB_IO_KICAD_SEXPR writer;
658 writer.SaveBoard( tmpPath.string(), *writeBoard );
659
660 PCB_IO_KICAD_SEXPR reader;
661 std::unique_ptr<BOARD> readBoard = reader.LoadBoard( tmpPath.string() );
662
663 BOOST_REQUIRE_EQUAL( readBoard->Zones().size(), 1u );
664
665 ZONE* loaded = readBoard->Zones()[0];
666
667 BOOST_CHECK( loaded->GetFillMode() == ZONE_FILL_MODE::COPPER_THIEVING );
668 BOOST_CHECK( loaded->IsCopperThieving() );
669 BOOST_CHECK_EQUAL( loaded->GetNetCode(), 0 );
670
671 const THIEVING_SETTINGS& loadedSettings = loaded->GetThievingSettings();
672 BOOST_CHECK( loadedSettings.pattern == THIEVING_PATTERN::HATCH );
673 BOOST_CHECK_EQUAL( loadedSettings.element_size, thieving.element_size );
674 BOOST_CHECK_EQUAL( loadedSettings.gap, thieving.gap );
675 BOOST_CHECK_EQUAL( loadedSettings.line_width, thieving.line_width );
676 BOOST_CHECK_EQUAL( loadedSettings.stagger, true );
677 BOOST_CHECK( loadedSettings.orientation == EDA_ANGLE( 30.0, DEGREES_T ) );
678
679 std::filesystem::remove( tmpPath );
680}
681
682
687BOOST_AUTO_TEST_CASE( CopperThievingZone_AllPatternsRoundTrip )
688{
689 const std::array<THIEVING_PATTERN, 3> patterns = {
693 };
694
695 for( THIEVING_PATTERN pattern : patterns )
696 {
697 BOOST_TEST_CONTEXT( "pattern enum value " << static_cast<int>( pattern ) )
698 {
699 std::unique_ptr<BOARD> writeBoard = std::make_unique<BOARD>();
700
701 ZONE* zone = new ZONE( writeBoard.get() );
702 zone->SetLayer( F_Cu );
703 zone->AppendCorner( VECTOR2I( 0, 0 ), -1 );
704 zone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( 5 ), 0 ), -1 );
705 zone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ), -1 );
706 zone->AppendCorner( VECTOR2I( 0, pcbIUScale.mmToIU( 5 ) ), -1 );
708
709 THIEVING_SETTINGS thieving = zone->GetThievingSettings();
710 thieving.pattern = pattern;
711 zone->SetThievingSettings( thieving );
712
713 writeBoard->Add( zone );
714
715 std::filesystem::path tmpPath = std::filesystem::temp_directory_path()
716 / "copper_thieving_pattern.kicad_pcb";
717 PCB_IO_KICAD_SEXPR writer;
718 writer.SaveBoard( tmpPath.string(), *writeBoard );
719
720 PCB_IO_KICAD_SEXPR reader;
721 std::unique_ptr<BOARD> readBoard = reader.LoadBoard( tmpPath.string() );
722
723 BOOST_REQUIRE_EQUAL( readBoard->Zones().size(), 1u );
724 BOOST_CHECK( readBoard->Zones()[0]->GetThievingSettings().pattern == pattern );
725
726 std::filesystem::remove( tmpPath );
727 }
728 }
729}
730
731
738BOOST_AUTO_TEST_CASE( CopperThievingZone_RejectedInOldFileVersion )
739{
740 std::filesystem::path tmpPath = std::filesystem::temp_directory_path()
741 / "copper_thieving_old_version.kicad_pcb";
742 std::ofstream out( tmpPath );
743 // 20260512 is the Net Chains release; thieving needs >= 20260513. Using the
744 // immediately-prior version exercises the boundary check.
745 out << "(kicad_pcb (version 20260512) (generator \"test\") (generator_version \"test\")"
746 << " (general (thickness 1.6)) (paper \"A4\")"
747 << " (layers (0 \"F.Cu\" signal) (31 \"B.Cu\" signal))"
748 << " (zone (net 0) (net_name \"\") (layer \"F.Cu\") (uuid \"00000000-0000-0000-0000-000000000001\")"
749 << " (hatch edge 0.5)"
750 << " (connect_pads (clearance 0))"
751 << " (min_thickness 0.25) (filled_areas_thickness no)"
752 << " (fill yes (mode thieving) (thermal_gap 0.5) (thermal_bridge_width 0.5)"
753 << " (island_removal_mode 0))"
754 << " (polygon (pts (xy 0 0) (xy 5 0) (xy 5 5) (xy 0 5))))"
755 << ")";
756 out.close();
757
758 PCB_IO_KICAD_SEXPR reader;
759 BOOST_REQUIRE_THROW( reader.LoadBoard( tmpPath.string() ), IO_ERROR );
760
761 std::filesystem::remove( tmpPath );
762}
763
764
765BOOST_AUTO_TEST_CASE( MalformedDimensionTextThrowsCleanly )
766{
767 KI_TEST::SCOPED_TEMP_DIR tempDir( "kicad_qa_malformed_dimension_text" );
768 std::filesystem::path tmpPath = tempDir.Path() / "malformed_dimension_text.kicad_pcb";
769 std::ofstream out( tmpPath );
770 out << "(kicad_pcb (version 20240108) (generator \"test\")"
771 << " (general (thickness 1.6)) (paper \"A4\")"
772 << " (layers (0 \"F.Cu\" signal) (31 \"B.Cu\" signal) (36 \"B.SilkS\" user \"b.silkscreen\"))"
773 << " (dimension (type aligned) (layer \"B.SilkS\")"
774 << " (gr_text \"1 mm\" (at broken))))";
775 out.close();
776
777 PCB_IO_KICAD_SEXPR reader;
778
779 BOOST_CHECK_THROW( reader.LoadBoard( tmpPath.string() ), IO_ERROR );
780}
781
782
789BOOST_AUTO_TEST_CASE( CopperThievingZone_RejectsMalformedGeometry )
790{
791 std::filesystem::path tmpPath = std::filesystem::temp_directory_path()
792 / "copper_thieving_malformed.kicad_pcb";
793 std::ofstream out( tmpPath );
794 out << "(kicad_pcb (version 20260513) (generator \"test\") (generator_version \"test\")"
795 << " (general (thickness 1.6)) (paper \"A4\")"
796 << " (layers (0 \"F.Cu\" signal) (31 \"B.Cu\" signal))"
797 << " (zone (net 0) (net_name \"\") (layer \"F.Cu\") (uuid \"00000000-0000-0000-0000-000000000002\")"
798 << " (hatch edge 0.5)"
799 << " (connect_pads (clearance 0))"
800 << " (min_thickness 0.25) (filled_areas_thickness no)"
801 << " (fill yes (mode thieving)"
802 << " (thermal_gap 0.5) (thermal_bridge_width 0.5)"
803 << " (island_removal_mode 0)"
804 << " (thieving (type dots) (size -1) (gap 0)"
805 << " (width -5) (stagger no) (orientation 0)))"
806 << " (polygon (pts (xy 0 0) (xy 5 0) (xy 5 5) (xy 0 5))))"
807 << ")";
808 out.close();
809
810 PCB_IO_KICAD_SEXPR reader;
811 std::unique_ptr<BOARD> readBoard = reader.LoadBoard( tmpPath.string() );
812
813 BOOST_REQUIRE_EQUAL( readBoard->Zones().size(), 1u );
814
815 const THIEVING_SETTINGS& loaded = readBoard->Zones()[0]->GetThievingSettings();
816 BOOST_CHECK_GT( loaded.element_size, 0 );
817 BOOST_CHECK_GT( loaded.gap, 0 );
818 BOOST_CHECK_GT( loaded.line_width, 0 );
819
820 std::filesystem::remove( tmpPath );
821}
822
823
831BOOST_AUTO_TEST_CASE( Issue24955_AppendDoesNotInheritSessionZoneDefaults )
832{
833 KI_TEST::SCOPED_TEMP_DIR tempDir( "kicad_qa_zone_defaults_append" );
834 std::filesystem::path tmpPath = tempDir.Path() / "thermal_zone_block.kicad_pcb";
835 std::ofstream out( tmpPath );
836
837 // Two zones, first with every omitted-when-default token left out (thermal, polygon
838 // fill, no smoothing, unlocked), second with the explicit tokens
839 out << "(kicad_pcb (version 20260513) (generator \"test\") (generator_version \"test\")"
840 << " (general (thickness 1.6)) (paper \"A4\")"
841 << " (layers (0 \"F.Cu\" signal) (31 \"B.Cu\" signal))"
842 << " (zone (net 0) (net_name \"\") (layer \"F.Cu\") (uuid \"00000000-0000-0000-0000-000000000001\")"
843 << " (hatch edge 0.5)"
844 << " (connect_pads (clearance 0.5))"
845 << " (min_thickness 0.25) (filled_areas_thickness no)"
846 << " (fill yes (thermal_gap 0.5) (thermal_bridge_width 0.5) (island_removal_mode 0))"
847 << " (polygon (pts (xy 0 0) (xy 5 0) (xy 5 5) (xy 0 5))))"
848 << " (zone (net 0) (net_name \"\") (layer \"F.Cu\") (uuid \"00000000-0000-0000-0000-000000000002\")"
849 << " (locked yes)"
850 << " (hatch edge 0.5)"
851 << " (connect_pads yes (clearance 0.5))"
852 << " (min_thickness 0.25) (filled_areas_thickness no)"
853 << " (fill yes (thermal_gap 0.5) (thermal_bridge_width 0.5) (island_removal_mode 0)"
854 << " (smoothing fillet) (radius 1))"
855 << " (polygon (pts (xy 10 0) (xy 15 0) (xy 15 5) (xy 10 5))))"
856 << ")";
857 out.close();
858
859 // A destination board whose session zone defaults differ from the format defaults
860 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
861
862 ZONE_SETTINGS settings = board->GetDesignSettings().GetDefaultZoneSettings();
866 settings.SetCornerRadius( pcbIUScale.mmToIU( 1 ) );
867 settings.m_HatchSmoothingLevel = 2;
868 settings.m_Locked = true;
869 board->GetDesignSettings().SetDefaultZoneSettings( settings );
870
871 kicadPlugin.LoadAndAppendBoard( tmpPath.string(), *board );
872
873 BOOST_REQUIRE_EQUAL( board->Zones().size(), 2u );
874
875 // The all-defaults zone must not pick up the board's session defaults
876 ZONE* omitted = board->Zones()[0];
877 BOOST_CHECK( omitted->GetPadConnection() == ZONE_CONNECTION::THERMAL );
878 BOOST_CHECK( omitted->GetFillMode() == ZONE_FILL_MODE::POLYGONS );
880 BOOST_CHECK_EQUAL( omitted->GetCornerRadius(), 0 );
882 BOOST_CHECK( !omitted->IsLocked() );
883
884 // Explicit tokens still win
885 ZONE* explicitZone = board->Zones()[1];
886 BOOST_CHECK( explicitZone->GetPadConnection() == ZONE_CONNECTION::FULL );
888 BOOST_CHECK_EQUAL( explicitZone->GetCornerRadius(), pcbIUScale.mmToIU( 1 ) );
889 BOOST_CHECK( explicitZone->IsLocked() );
890}
891
892
897BOOST_AUTO_TEST_CASE( Issue24911_CancelledAppendLeavesBoardUntouched )
898{
899 // Requests cancellation once the parse reaches the given line, then stalls long
900 // enough to open the parser 250ms cancel checkpoint window
901 class CANCEL_AT_LINE_READER : public STRING_LINE_READER
902 {
903 public:
904 CANCEL_AT_LINE_READER( const std::string& aText, unsigned aCancelLine, bool* aCancelFlag ) :
905 STRING_LINE_READER( aText, wxT( "cancel-test" ) ),
906 m_cancelLine( aCancelLine ),
907 m_cancelFlag( aCancelFlag )
908 {
909 }
910
911 char* ReadLine() override
912 {
913 if( !*m_cancelFlag && LineNumber() >= m_cancelLine )
914 {
915 *m_cancelFlag = true;
916 std::this_thread::sleep_for( std::chrono::milliseconds( 300 ) );
917 }
918
920 }
921
922 private:
923 unsigned m_cancelLine;
924 bool* m_cancelFlag;
925 };
926
927 class CANCELLING_REPORTER : public PROGRESS_REPORTER
928 {
929 public:
930 CANCELLING_REPORTER( bool* aCancelFlag ) :
931 m_cancelFlag( aCancelFlag )
932 {
933 }
934
935 void SetNumPhases( int ) override {}
936 void AddPhases( int ) override {}
937 void BeginPhase( int ) override {}
938 void AdvancePhase() override {}
939 void AdvancePhase( const wxString& ) override {}
940 void Report( const wxString& ) override {}
941 void SetCurrentProgress( double ) override {}
942 void SetMaxProgress( int ) override {}
943 void AdvanceProgress() override {}
944 void SetTitle( const wxString& ) override {}
945 bool IsCancelled() const override { return *m_cancelFlag; }
946 bool KeepRefreshing( bool ) override { return !*m_cancelFlag; }
947
948 private:
949 bool* m_cancelFlag;
950 };
951
952 // Source board with one net and enough tracks that the cancel lands mid-parse
953 std::unique_ptr<BOARD> source = std::make_unique<BOARD>();
954
955 NETINFO_ITEM* net = new NETINFO_ITEM( source.get(), wxT( "GHOST_NET" ), 1 );
956 source->Add( net );
957
958 for( int i = 0; i < 400; i++ )
959 {
960 PCB_TRACK* track = new PCB_TRACK( source.get() );
961 track->SetStart( VECTOR2I( i * 10000, 0 ) );
962 track->SetEnd( VECTOR2I( i * 10000, 10000 ) );
963 track->SetWidth( 200000 );
964 track->SetLayer( F_Cu );
965 track->SetNet( net );
966 source->Add( track );
967 }
968
970 kicadPlugin.FormatBoardToFormatter( &fmt, source.get() );
971
972 // The formatter emits a single line, the parser can only be interrupted between lines
973 std::string text = fmt.GetString();
975
976 unsigned lineCount = std::count( text.begin(), text.end(), '\n' );
977
978 // Destination board with one pre-existing track
979 std::unique_ptr<BOARD> dest = std::make_unique<BOARD>();
980
981 PCB_TRACK* existing = new PCB_TRACK( dest.get() );
982 existing->SetStart( VECTOR2I( 0, 0 ) );
983 existing->SetEnd( VECTOR2I( 5000, 0 ) );
984 existing->SetWidth( 200000 );
985 existing->SetLayer( F_Cu );
986 dest->Add( existing );
987
988 size_t tracksBefore = dest->Tracks().size();
989 unsigned netsBefore = dest->GetNetInfo().GetNetCount();
990
991 bool cancelRequested = false;
992
993 CANCEL_AT_LINE_READER reader( text, lineCount / 2, &cancelRequested );
994 CANCELLING_REPORTER reporter( &cancelRequested );
995
996 BOOST_CHECK_THROW( kicadPlugin.DoLoad( reader, *dest, false, nullptr, &reporter, lineCount ), IO_ERROR );
997 BOOST_CHECK( cancelRequested );
998
999 BOOST_CHECK_EQUAL( dest->Tracks().size(), tracksBefore );
1000 BOOST_CHECK_EQUAL( dest->GetNetInfo().GetNetCount(), netsBefore );
1001}
1002
1003
1004// Enumerating a footprint library that is not on disk must fail every time. The failed
1005// load leaves a cache bound to the missing path carrying a zero timestamp, which is also
1006// what a missing directory reports, so the cache looks current and the next enumerate
1007// answers with an empty library instead of the error.
1008BOOST_AUTO_TEST_CASE( MissingLibraryThrowsOnEveryEnumerate )
1009{
1010 std::filesystem::path libPath = std::filesystem::temp_directory_path() / "kicad_qa_no_such_library.pretty";
1011
1012 BOOST_REQUIRE( !std::filesystem::exists( libPath ) );
1013
1014 wxArrayString names;
1015
1016 BOOST_CHECK_THROW( kicadPlugin.FootprintEnumerate( names, libPath.string(), false ), IO_ERROR );
1017
1018 BOOST_CHECK_THROW( kicadPlugin.FootprintEnumerate( names, libPath.string(), false ), IO_ERROR );
1019}
1020
1021
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
General utilities for PCB file IO for QA programs.
@ BS_ITEM_TYPE_COPPER
@ BS_ITEM_TYPE_DIELECTRIC
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
bool IsLocked() const override
Manage one layer needed to make a physical board.
int GetThickness(int aDielectricSubLayer=0) const
wxString GetMaterial(int aDielectricSubLayer=0) const
Manage layers needed to make a physical board.
int GetCount() const
int BuildBoardThicknessFromStackup() const
wxString m_FinishType
The name of external copper finish.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1497
const TRACKS & Tracks() const
Definition board.h:461
const DRAWINGS & Drawings() const
Definition board.h:465
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
VECTOR2I GetArcMid() const
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:474
EDA_ITEM * Clone() const override
Invoke a function on all children.
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
const std::filesystem::path & Path() const
Get the path to the temporary directory as a std::filesystem::path.
Definition file_utils.h:59
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
virtual unsigned LineNumber() const
Return the line number of the last line read from this LINE_READER.
Definition richio.h:119
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
Handle the data for a net.
Definition netinfo.h:50
static NETINFO_ITEM * OrphanedItem()
NETINFO_ITEM meaning that there was no net assigned for an item, as there was no board storing net li...
Definition netinfo.h:288
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:170
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
Definition padstack.h:179
Definition pad.h:61
A #PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
void SaveBoard(const wxString &aFileName, BOARD &aBoard, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aBoard to a storage file in a format that this PCB_IO implementation knows about or it can be u...
std::unique_ptr< BOARD > LoadBoard(const wxString &aFileName, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr)
Load information from some input file format that this PCB_IO implementation knows about into new BOA...
Definition pcb_io.cpp:72
void SetWidth(int aWidth) override
void SetEnd(const VECTOR2I &aEnd) override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetStart(const VECTOR2I &aStart) override
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:89
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:92
virtual void SetWidth(int aWidth)
Definition pcb_track.h:86
A progress reporter interface for use in multi-threaded environments.
virtual bool IsCancelled() const =0
virtual void AddPhases(int aNumPhases)=0
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void SetNumPhases(int aNumPhases)=0
Set the number of phases.
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
virtual void BeginPhase(int aPhase)=0
Initialize the aPhase virtual zone of the dialog progress bar.
virtual void AdvancePhase()=0
Use the next available virtual zone of the dialog progress bar.
virtual void SetTitle(const wxString &aTitle)=0
Change the title displayed on the window caption.
virtual void AdvanceProgress()=0
Increment the progress bar length (inside the current virtual zone).
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).
virtual void SetMaxProgress(int aMaxProgress)=0
Fix the value that gives the 100 percent progress bar length (inside the current virtual zone).
int TotalVertices() const
Return total number of vertices stored in the set.
Implement an OUTPUTFORMATTER to a memory buffer.
Definition richio.h:430
const std::string & GetString()
Definition richio.h:453
Is a LINE_READER that reads from a multiline 8 bit wide std::string.
Definition richio.h:225
STRING_LINE_READER(const std::string &aString, const wxString &aSource)
Construct a string line reader.
Definition richio.cpp:236
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition richio.cpp:259
ZONE_SETTINGS handles zones parameters.
void SetCornerSmoothingType(CORNER_SMOOTHING aType)
void SetPadConnection(ZONE_CONNECTION aPadConnection)
void SetCornerRadius(int aRadius)
ZONE_FILL_MODE m_FillMode
Handle a list of polygons defining a copper zone.
Definition zone.h:70
const THIEVING_SETTINGS & GetThievingSettings() const
Definition zone.h:351
bool AppendCorner(const VECTOR2I &aPosition, int aHoleIdx, bool aAllowDuplication=false)
Add a new corner to the zone outline (to the main outline or a hole)
Definition zone.cpp:1441
ZONE_LAYER_PROPERTIES & LayerProperties(PCB_LAYER_ID aLayer)
Definition zone.h:146
std::shared_ptr< SHAPE_POLY_SET > GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition zone.h:692
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:641
bool IsCopperThieving() const
Definition zone.h:349
ZONE_SETTINGS::CORNER_SMOOTHING GetCornerSmoothingType() const
Definition zone.h:746
void SetFillMode(ZONE_FILL_MODE aFillMode)
Definition zone.cpp:647
ZONE_CONNECTION GetPadConnection() const
Definition zone.h:312
void SetThievingSettings(const THIEVING_SETTINGS &aSettings)
Definition zone.h:352
VECTOR2I GetPosition() const override
Definition zone.cpp:565
void SetNet(NETINFO_ITEM *aNetInfo) override
Override that drops aNetInfo when this zone is in copper-thieving fill mode.
Definition zone.cpp:632
ZONE_FILL_MODE GetFillMode() const
Definition zone.h:238
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:133
int GetHatchSmoothingLevel() const
Definition zone.h:334
unsigned int GetCornerRadius() const
Definition zone.h:750
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition zone.h:610
@ DEGREES_T
Definition eda_angle.h:31
@ RECURSE
Definition eda_item.h:51
@ SEGMENT
Definition eda_shape.h:56
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Cu
Definition layer_ids.h:61
@ In2_Cu
Definition layer_ids.h:63
@ F_SilkS
Definition layer_ids.h:96
@ In4_Cu
Definition layer_ids.h:65
@ In1_Cu
Definition layer_ids.h:62
@ In3_Cu
Definition layer_ids.h:64
@ F_Cu
Definition layer_ids.h:60
void Prettify(std::string &aSource, FORMAT_MODE aMode)
Pretty-prints s-expression text according to KiCad format rules.
std::string GetPcbnewTestDataDir()
Utility which returns a path to the data directory where the test board files are stored.
constexpr char APPEND_PRESERVE_DESTINATION_STACKUP[]
Definition pcb_io.h:44
Describes an imported layer and how it could be mapped to KiCad Layers.
PCB_IO_KICAD_SEXPR kicadPlugin
Parameters that drive copper-thieving fill generation.
EDA_ANGLE orientation
THIEVING_PATTERN pattern
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_TEST(netlist.find("R_G1 ARM_OUT1 DIE_B R='0.001 / ((SW_STATE)") !=std::string::npos)
std::string path
IbisParser parser & reporter
#define CHECK_ENUM_CLASS_EQUAL(L, R)
BOOST_AUTO_TEST_CASE(Issue19775_ZoneLayerWildcards)
Declares the struct as the Boost test fixture.
BOOST_TEST_CONTEXT("Test Clearance")
BOOST_CHECK_EQUAL(result, "25.4")
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:100
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
THIEVING_PATTERN
Shape stamped onto the grid for a copper-thieving fill.
@ THERMAL
Use thermal relief for pads.
Definition zones.h:46
@ FULL
pads are covered by copper
Definition zones.h:47