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 <iterator>
25#include <memory>
26#include <sstream>
27#include <string>
28#include <thread>
29
30#include <qa_utils/file_utils.h>
34
37#include <progress_reporter.h>
38#include <richio.h>
39
40#include <board.h>
44#include <footprint.h>
45#include <netinfo.h>
46#include <pad.h>
47#include <pcb_shape.h>
48#include <pcb_track.h>
49#include <zone.h>
50
51
52#define CHECK_ENUM_CLASS_EQUAL( L, R ) \
53 BOOST_CHECK_EQUAL( static_cast<int>( L ), static_cast<int>( R ) )
54
55
62
63
67BOOST_FIXTURE_TEST_SUITE( KiCadSexprIO, KICAD_SEXPR_FIXTURE )
68
69
70
73BOOST_AUTO_TEST_CASE( Issue19775_ZoneLayerWildcards )
74{
75 std::string dataPath = KI_TEST::GetPcbnewTestDataDir() + "plugins/kicad_sexpr/Issue19775_ZoneLayers/";
76
77 BOOST_TEST_CONTEXT( "Zone layers with wildcards" )
78 {
79 std::unique_ptr<BOARD> testBoard = kicadPlugin.LoadBoard( dataPath + "LayerWildcard.kicad_pcb" );
80
81 // One zone in the file
82 BOOST_CHECK( testBoard->Zones().size() == 1 );
83
84 ZONE* z = testBoard->Zones()[0];
85
86 // On both front and back layers, with zone fill on both
87 BOOST_CHECK( z->GetLayerSet().Contains( F_Cu ) && z->GetLayerSet().Contains( B_Cu ) );
88 BOOST_CHECK( z->GetFilledPolysList( F_Cu )->TotalVertices() > 0 );
89 BOOST_CHECK( z->GetFilledPolysList( B_Cu )->TotalVertices() > 0 );
90 }
91
92 BOOST_TEST_CONTEXT( "Round trip layers" )
93 {
94 auto tmpBoard = std::filesystem::temp_directory_path() / "Issue19775_RoundTrip.kicad_pcb";
95
96 // Load and save the board from above to test how we write the zones into it
97 {
98 std::unique_ptr<BOARD> testBoard = kicadPlugin.LoadBoard( dataPath + "LayerEnumerate.kicad_pcb" );
99 kicadPlugin.SaveBoard( tmpBoard.string(), *testBoard );
100 }
101
102 // Read the new board
103 std::unique_ptr<BOARD> testBoard = kicadPlugin.LoadBoard( tmpBoard.string() );
104
105 // One zone in the file
106 BOOST_CHECK( testBoard->Zones().size() == 1 );
107
108 ZONE* z = testBoard->Zones()[0];
109
110 // On both front and back layers, with zone fill on both
111 BOOST_CHECK( z->GetLayerSet().Contains( F_Cu ) && z->GetLayerSet().Contains( B_Cu ) );
112 BOOST_CHECK( z->GetFilledPolysList( F_Cu )->TotalVertices() > 0 );
113 BOOST_CHECK( z->GetFilledPolysList( B_Cu )->TotalVertices() > 0 );
114 BOOST_CHECK( z->LayerProperties().contains( F_Cu ) );
115 }
116}
117
118
126BOOST_AUTO_TEST_CASE( Issue23125_EmptyZoneDiscarded )
127{
128 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
129 + "plugins/kicad_sexpr/Issue23125_EmptyZone/";
130
131 std::unique_ptr<BOARD> testBoard = kicadPlugin.LoadBoard( dataPath + "EmptyZone.kicad_pcb" );
132
133 // The file contains 3 zones: 1 valid (with polygon) and 2 empty (no polygon).
134 // The 2 empty zones should have been discarded during loading.
135 BOOST_CHECK_EQUAL( testBoard->Zones().size(), 1 );
136
137 // The surviving zone should have a valid position
138 ZONE* z = testBoard->Zones()[0];
139 BOOST_CHECK_NO_THROW( z->GetPosition() );
140 BOOST_CHECK( z->GetNumCorners() > 0 );
141}
142
143
149BOOST_AUTO_TEST_CASE( CoincidentDrawingsSurviveSave )
150{
151 // The writer sorts drawings into a std::set, so a cmp_drawings that reports equality for
152 // two coincident shapes silently drops one of them
153 BOARD board;
154
155 for( int ii = 0; ii < 2; ++ii )
156 {
157 PCB_SHAPE* line = new PCB_SHAPE( &board, SHAPE_T::SEGMENT );
158
159 line->SetLayer( F_SilkS );
160 line->SetStart( VECTOR2I( 0, 0 ) );
161 line->SetEnd( VECTOR2I( pcbIUScale.mmToIU( 5.0 ), 0 ) );
162 line->SetWidth( pcbIUScale.mmToIU( 0.1 ) );
163
164 board.Add( line, ADD_MODE::APPEND );
165 }
166
167 BOOST_REQUIRE_EQUAL( board.Drawings().size(), 2u );
168
169 const wxString path =
170 ( std::filesystem::temp_directory_path() / "qa_coincident_drawings.kicad_pcb" ).string();
171
172 kicadPlugin.SaveBoard( path, board );
173
174 std::unique_ptr<BOARD> reloaded = kicadPlugin.LoadBoard( path );
175
176 BOOST_REQUIRE( reloaded != nullptr );
177 BOOST_CHECK_EQUAL( reloaded->Drawings().size(), 2u );
178
179 std::filesystem::remove( path.ToStdString() );
180}
181
182
183BOOST_AUTO_TEST_CASE( ScientificNotationLoading )
184{
185 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
186 + "plugins/kicad_sexpr/";
187
188 std::unique_ptr<BOARD> testBoard = kicadPlugin.LoadBoard( dataPath + "ScientificNotation.kicad_pcb" );
189
190 // The file contains 1 arc with scientific notation in its coordinates
191 BOOST_CHECK_EQUAL( testBoard->Drawings().size(), 1 );
192
193 PCB_SHAPE* arc = dynamic_cast<PCB_SHAPE*>( testBoard->Drawings().front() );
194
195 // The arc's midpoint should be located at (4.17, -4.5e-05) in file units
196 BOOST_REQUIRE( arc );
197 BOOST_TEST( arc->GetArcMid().x == 4170000 );
198 BOOST_TEST( arc->GetArcMid().y == -45 );
199}
200
201
208BOOST_AUTO_TEST_CASE( Issue23625_CorruptedStackupCapped )
209{
210 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
211 + "plugins/kicad_sexpr/Issue23625_CorruptedStackup/";
212
213 std::unique_ptr<BOARD> testBoard;
214
215 BOOST_CHECK_NO_THROW( testBoard = kicadPlugin.LoadBoard( dataPath + "corrupted_stackup.kicad_pcb" ) );
216
217 const BOARD_STACKUP& stackup =
218 testBoard->GetDesignSettings().GetStackupDescriptor();
219
220 // The test file has 200 dielectric layers (plus copper, silk, mask), well
221 // beyond the parser's 128-item cap. After loading, the count must be
222 // clamped and the board must still be usable.
223 BOOST_CHECK_LE( stackup.GetCount(), 128 );
224 BOOST_CHECK_GT( stackup.GetCount(), 0 );
225}
226
227
235BOOST_AUTO_TEST_CASE( Issue24133_DuplicatedStackupNotInflated )
236{
237 std::string dataPath = KI_TEST::GetPcbnewTestDataDir()
238 + "plugins/kicad_sexpr/Issue24133_DuplicatedStackup/";
239
240 std::unique_ptr<BOARD> testBoard;
241
242 BOOST_CHECK_NO_THROW( testBoard = kicadPlugin.LoadBoard( dataPath + "duplicated_stackup.kicad_pcb" ) );
243
244 const BOARD_STACKUP& stackup = testBoard->GetDesignSettings().GetStackupDescriptor();
245
246 // The file repeats the 9-item layer sequence three times. Only the first copy must be kept.
247 BOOST_CHECK_EQUAL( stackup.GetCount(), 9 );
248
249 // A single copy is a standard 1.6 mm two-layer board. Without dedup the three copies would
250 // sum to 4.8 mm.
252}
253
254
261BOOST_AUTO_TEST_CASE( Issue23752_AppendBoardPreservesStackupAndGrowsToSixCopperLayers )
262{
263 std::string dataPath = KI_TEST::GetPcbnewTestDataDir();
264
265 // four layer board
266 std::string destinationPath = dataPath + "issue3812.kicad_pcb";
267 // six layer board
268 std::string sourcePath = dataPath + "issue18142.kicad_pcb";
269 std::map<std::string, UTF8> props;
270
271 // basically, make sure that we start with a four layer board, append a six layer
272 // board like this is a design block apply-layout with six layers, and make
273 // sure we end up with a six layer board and a six layer stackup and not a
274 // ten layer board
275 //
276 // we also need to test the stackup properties are preserved, because the increase
277 // in copper layers should cause a stackup growth from from four to six layers,
278 // but not replace the stackup with the source board's stackup, which has different properties (e.g. finish type)
280
281 auto countCopperLayers = []( const BOARD_STACKUP& aStackup )
282 {
283 int copperLayerCount = 0;
284
285 for( BOARD_STACKUP_ITEM* item : aStackup.GetList() )
286 {
287 if( item->GetType() == BS_ITEM_TYPE_COPPER )
288 ++copperLayerCount;
289 }
290
291 return copperLayerCount;
292 };
293
294 auto findFirstDielectric = []( const BOARD_STACKUP& aStackup ) -> const BOARD_STACKUP_ITEM*
295 {
296 for( BOARD_STACKUP_ITEM* item : aStackup.GetList() )
297 {
298 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
299 return item;
300 }
301
302 return nullptr;
303 };
304
305 std::unique_ptr<BOARD> testBoard = kicadPlugin.LoadBoard( destinationPath );
306
307 const BOARD_STACKUP& initialStackup = testBoard->GetDesignSettings().GetStackupDescriptor();
308 const BOARD_STACKUP_ITEM* initialFirstDielectric = findFirstDielectric( initialStackup );
309 const int initialCopperLayerCount = testBoard->GetCopperLayerCount();
310 const LSET initialEnabledLayers = testBoard->GetEnabledLayers();
311 const wxString initialFinishType = initialStackup.m_FinishType;
312
313 BOOST_REQUIRE_EQUAL( initialCopperLayerCount, 4 );
314 BOOST_REQUIRE_EQUAL( countCopperLayers( initialStackup ), 4 );
315 BOOST_REQUIRE( initialFirstDielectric );
316 BOOST_REQUIRE_EQUAL( initialFinishType, wxS( "ENIG" ) );
317 const wxString initialFirstDielectricMaterial = initialFirstDielectric->GetMaterial();
318 const int initialFirstDielectricThickness = initialFirstDielectric->GetThickness();
319
320 kicadPlugin.LoadAndAppendBoard( sourcePath, *testBoard, &props );
321
322 const int appendedCopperLayerCount = testBoard->GetCopperLayerCount();
323
324 if( appendedCopperLayerCount > initialCopperLayerCount )
325 testBoard->SetCopperLayerCount( appendedCopperLayerCount );
326
327 LSET enabledLayers = testBoard->GetEnabledLayers();
328 enabledLayers |= initialEnabledLayers;
329 testBoard->SetEnabledLayers( enabledLayers );
330 testBoard->GetDesignSettings().GetStackupDescriptor().SynchronizeWithBoard( &testBoard->GetDesignSettings() );
331
332 const BOARD_STACKUP& finalStackup = testBoard->GetDesignSettings().GetStackupDescriptor();
333 const BOARD_STACKUP_ITEM* finalFirstDielectric = findFirstDielectric( finalStackup );
334
335 BOOST_CHECK_EQUAL( testBoard->GetCopperLayerCount(), 6 );
336 BOOST_CHECK_EQUAL( countCopperLayers( finalStackup ), 6 );
337 BOOST_REQUIRE( finalFirstDielectric );
338 BOOST_CHECK_EQUAL( finalStackup.m_FinishType, initialFinishType );
339 BOOST_CHECK_EQUAL( finalFirstDielectric->GetMaterial(), initialFirstDielectricMaterial );
340 BOOST_CHECK_EQUAL( finalFirstDielectric->GetThickness(), initialFirstDielectricThickness );
341}
342
343
351BOOST_AUTO_TEST_CASE( Issue24642_AppendBoardPreservesDestinationLayerNames )
352{
353 std::string dataPath = KI_TEST::GetPcbnewTestDataDir();
354
355 // four layer board with custom copper layer names
356 std::string destinationPath = dataPath + "issue3812.kicad_pcb";
357 // six layer board using the standard copper layer names
358 std::string sourcePath = dataPath + "issue18142.kicad_pcb";
359
360 std::map<std::string, UTF8> props;
362
363 std::unique_ptr<BOARD> testBoard = kicadPlugin.LoadBoard( destinationPath );
364
365 BOOST_REQUIRE_EQUAL( testBoard->GetLayerName( F_Cu ), wxS( "Top_layer" ) );
366 BOOST_REQUIRE_EQUAL( testBoard->GetLayerName( In1_Cu ), wxS( "GND_layer" ) );
367 BOOST_REQUIRE_EQUAL( testBoard->GetLayerName( In2_Cu ), wxS( "VDD_layer" ) );
368 BOOST_REQUIRE_EQUAL( testBoard->GetLayerName( B_Cu ), wxS( "Bottom_layer" ) );
369
370 kicadPlugin.LoadAndAppendBoard( sourcePath, *testBoard, &props );
371
372 // The destination layer names must survive the append
373 BOOST_CHECK_EQUAL( testBoard->GetLayerName( F_Cu ), wxS( "Top_layer" ) );
374 BOOST_CHECK_EQUAL( testBoard->GetLayerName( In1_Cu ), wxS( "GND_layer" ) );
375 BOOST_CHECK_EQUAL( testBoard->GetLayerName( In2_Cu ), wxS( "VDD_layer" ) );
376 BOOST_CHECK_EQUAL( testBoard->GetLayerName( B_Cu ), wxS( "Bottom_layer" ) );
377
378 // The two extra copper layers from the source board are still added
379 BOOST_CHECK( testBoard->IsLayerEnabled( In3_Cu ) );
380 BOOST_CHECK( testBoard->IsLayerEnabled( In4_Cu ) );
381 BOOST_CHECK_EQUAL( testBoard->GetCopperLayerCount(), 6 );
382}
383
384
391BOOST_AUTO_TEST_CASE( Issue24642_AppendBoardRemapsLayersViaHandler )
392{
393 std::string dataPath = KI_TEST::GetPcbnewTestDataDir();
394 // four layer board with custom copper names and tracks on In2.Cu
395 std::string sourcePath = dataPath + "issue3812.kicad_pcb";
396
397 std::map<std::string, UTF8> props;
399
400 auto countTracksOn = []( BOARD* aBoard, PCB_LAYER_ID aLayer )
401 {
402 int n = 0;
403
404 for( PCB_TRACK* track : aBoard->Tracks() )
405 {
406 if( track->GetLayer() == aLayer )
407 n++;
408 }
409
410 return n;
411 };
412
413 // Source copper user names as the handler sees them
414 const wxString fName = wxS( "Top_layer" );
415 const wxString in1Name = wxS( "GND_layer" );
416 const wxString in2Name = wxS( "VDD_layer" );
417 const wxString bName = wxS( "Bottom_layer" );
418
419 // Identity mapping: appended layers stay where they are
420 std::vector<wxString> seenNames;
421
422 kicadPlugin.RegisterCallback(
423 [&]( const std::vector<INPUT_LAYER_DESC>& aDescs ) -> std::map<wxString, PCB_LAYER_ID>
424 {
425 for( const INPUT_LAYER_DESC& desc : aDescs )
426 seenNames.push_back( desc.Name );
427
428 return { { fName, F_Cu }, { in1Name, In1_Cu }, { in2Name, In2_Cu }, { bName, B_Cu } };
429 } );
430
431 std::unique_ptr<BOARD> identityBoard = std::make_unique<BOARD>();
432
433 kicadPlugin.LoadAndAppendBoard( sourcePath, *identityBoard, &props );
434
435 // The dialog must see the source board's own layer names, not the canonical defaults
436 BOOST_CHECK( std::find( seenNames.begin(), seenNames.end(), in1Name ) != seenNames.end() );
437 BOOST_CHECK( std::find( seenNames.begin(), seenNames.end(), in2Name ) != seenNames.end() );
438
439 const int idIn1 = countTracksOn( identityBoard.get(), In1_Cu );
440 const int idIn2 = countTracksOn( identityBoard.get(), In2_Cu );
441
442 BOOST_REQUIRE_GT( idIn2, 0 ); // the source has tracks on In2.Cu
443
444 // Swap mapping: appended In1 <-> In2
445 kicadPlugin.RegisterCallback(
446 [&]( const std::vector<INPUT_LAYER_DESC>& ) -> std::map<wxString, PCB_LAYER_ID>
447 {
448 return { { fName, F_Cu }, { in1Name, In2_Cu }, { in2Name, In1_Cu }, { bName, B_Cu } };
449 } );
450
451 std::unique_ptr<BOARD> swapBoard = std::make_unique<BOARD>();
452
453 kicadPlugin.LoadAndAppendBoard( sourcePath, *swapBoard, &props );
454
455 const int swIn1 = countTracksOn( swapBoard.get(), In1_Cu );
456 const int swIn2 = countTracksOn( swapBoard.get(), In2_Cu );
457
458 // The inner-copper track counts must have swapped
459 BOOST_CHECK_EQUAL( swIn1, idIn2 );
460 BOOST_CHECK_EQUAL( swIn2, idIn1 );
461}
462
463
469BOOST_AUTO_TEST_CASE( Issue24642_AppendBoardPromptsOnlyOnNameMismatch )
470{
471 std::string dataPath = KI_TEST::GetPcbnewTestDataDir();
472 std::string standardNames = dataPath + "issue18142.kicad_pcb"; // six layer, default names
473 std::string customNames = dataPath + "issue3812.kicad_pcb"; // four layer, custom copper names
474
475 std::map<std::string, UTF8> props;
477
478 bool handlerCalled = false;
479
480 kicadPlugin.RegisterCallback(
481 [&]( const std::vector<INPUT_LAYER_DESC>& ) -> std::map<wxString, PCB_LAYER_ID>
482 {
483 handlerCalled = true;
484 return {};
485 } );
486
487 // Appending onto a board with identical layer names must not prompt
488 {
489 std::unique_ptr<BOARD> matchBoard = kicadPlugin.LoadBoard( standardNames );
490 handlerCalled = false;
491 kicadPlugin.LoadAndAppendBoard( standardNames, *matchBoard, &props );
492 BOOST_CHECK( !handlerCalled );
493 }
494
495 // Appending a board with differently-named layers must prompt
496 {
497 std::unique_ptr<BOARD> mismatchBoard = kicadPlugin.LoadBoard( standardNames );
498 handlerCalled = false;
499 kicadPlugin.LoadAndAppendBoard( customNames, *mismatchBoard, &props );
500 BOOST_CHECK( handlerCalled );
501 }
502}
503
504
522BOOST_AUTO_TEST_CASE( FootprintSave_OmitsNetsOnAllBoardConnectedItems )
523{
524 KI_TEST::SCOPED_TEMP_DIR tmpLib( wxS( "qa_fp_save_netinfo" ) );
525
526 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
527
528 NETINFO_ITEM* net = new NETINFO_ITEM( board.get(), wxT( "TestNet" ), 1 );
529 board->Add( net );
530
531 FOOTPRINT* fp = new FOOTPRINT( board.get() );
532 board->Add( fp );
533 fp->SetFPID( LIB_ID( wxT( "scratch" ), wxT( "test_fp_save_netinfo" ) ) );
534
535 PAD* pad = new PAD( fp );
536 pad->SetPadstackMode( PADSTACK::MODE::NORMAL );
538 pad->SetSize( PADSTACK::ALL_LAYERS,
539 VECTOR2I( pcbIUScale.mmToIU( 1 ), pcbIUScale.mmToIU( 1 ) ) );
540 pad->SetNet( net );
541 fp->Add( pad );
542
543 PCB_SHAPE* shape = new PCB_SHAPE( fp, SHAPE_T::SEGMENT );
544 shape->SetLayer( F_Cu );
545 shape->SetStart( VECTOR2I( 0, 0 ) );
546 shape->SetEnd( VECTOR2I( pcbIUScale.mmToIU( 5 ), 0 ) );
547 shape->SetNet( net );
548 fp->Add( shape );
549
550 ZONE* zone = new ZONE( fp );
551 zone->SetLayer( F_Cu );
552 zone->SetNet( net );
553 fp->Add( zone );
554
555 BOOST_REQUIRE_EQUAL( pad->GetNet(), net );
556 BOOST_REQUIRE_EQUAL( shape->GetNet(), net );
557 BOOST_REQUIRE_EQUAL( zone->GetNet(), net );
558
559 // Save into the scratch library via the public library-save API. Must not throw.
560 BOOST_REQUIRE_NO_THROW( kicadPlugin.FootprintSave( tmpLib.Path().string(), fp ) );
561
562 auto savedFile = tmpLib.Path() / "test_fp_save_netinfo.kicad_mod";
563 BOOST_REQUIRE( std::filesystem::exists( savedFile ) );
564
565 std::ifstream in( savedFile );
566 BOOST_REQUIRE_MESSAGE( in.is_open(),
567 "Failed to open serialized footprint: " << savedFile.string() );
568
569 std::stringstream ss;
570 ss << in.rdbuf();
571 BOOST_REQUIRE( !in.bad() );
572
573 // Windows refuses to unlink a file that still has an open handle, so release it before the
574 // scoped directory teardown.
575 in.close();
576
577 const std::string contents = ss.str();
578 BOOST_REQUIRE( !contents.empty() );
579
580 BOOST_CHECK_MESSAGE( contents.find( "(net " ) == std::string::npos,
581 "Saved footprint library file must not contain (net ...) tokens:\n"
582 << contents );
583
584 // Verify the second defense directly on the save-path's transient state: clone the
585 // footprint, detach it from its parent board exactly as FootprintSave does, invoke
586 // ClearAllNets(), and assert every BOARD_CONNECTED_ITEM descendant is orphaned. This
587 // catches a regression where FootprintSave stops calling ClearAllNets() even if the
588 // serializer guards keep the file contents looking correct.
589 std::unique_ptr<FOOTPRINT> detached( static_cast<FOOTPRINT*>( fp->Clone() ) );
590 detached->SetParent( nullptr );
591 detached->SetParentGroup( nullptr );
592 detached->ClearAllNets();
593
594 BOARD_CONNECTED_ITEM* detachedPad = nullptr;
595 BOARD_CONNECTED_ITEM* detachedShape = nullptr;
596 BOARD_CONNECTED_ITEM* detachedZone = nullptr;
597
598 detached->RunOnChildren(
599 [&]( BOARD_ITEM* aItem )
600 {
601 switch( aItem->Type() )
602 {
603 case PCB_PAD_T: detachedPad = static_cast<BOARD_CONNECTED_ITEM*>( aItem ); break;
604 case PCB_SHAPE_T: detachedShape = static_cast<BOARD_CONNECTED_ITEM*>( aItem ); break;
605 case PCB_ZONE_T: detachedZone = static_cast<BOARD_CONNECTED_ITEM*>( aItem ); break;
606 default: break;
607 }
608 },
610
611 BOOST_REQUIRE( detachedPad );
612 BOOST_REQUIRE( detachedShape );
613 BOOST_REQUIRE( detachedZone );
617}
618
619
628BOOST_AUTO_TEST_CASE( CopperThievingZone_RoundTrip )
629{
630 std::unique_ptr<BOARD> writeBoard = std::make_unique<BOARD>();
631
632 ZONE* zone = new ZONE( writeBoard.get() );
633 zone->SetLayer( F_Cu );
634 zone->AppendCorner( VECTOR2I( 0, 0 ), -1 );
635 zone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( 10 ), 0 ), -1 );
636 zone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ), -1 );
637 zone->AppendCorner( VECTOR2I( 0, pcbIUScale.mmToIU( 10 ) ), -1 );
639
640 THIEVING_SETTINGS thieving;
642 thieving.element_size = pcbIUScale.mmToIU( 0.42 );
643 thieving.gap = pcbIUScale.mmToIU( 1.27 );
644 thieving.line_width = pcbIUScale.mmToIU( 0.35 );
645 thieving.stagger = true;
646 thieving.orientation.SetAngle( EDA_ANGLE( 30.0, DEGREES_T ) );
647 zone->SetThievingSettings( thieving );
648
649 writeBoard->Add( zone );
650
651 std::filesystem::path tmpPath = std::filesystem::temp_directory_path()
652 / "copper_thieving_roundtrip.kicad_pcb";
653
654 PCB_IO_KICAD_SEXPR writer;
655 writer.SaveBoard( tmpPath.string(), *writeBoard );
656
657 PCB_IO_KICAD_SEXPR reader;
658 std::unique_ptr<BOARD> readBoard = reader.LoadBoard( tmpPath.string() );
659
660 BOOST_REQUIRE_EQUAL( readBoard->Zones().size(), 1u );
661
662 ZONE* loaded = readBoard->Zones()[0];
663
664 BOOST_CHECK( loaded->GetFillMode() == ZONE_FILL_MODE::COPPER_THIEVING );
665 BOOST_CHECK( loaded->IsCopperThieving() );
666 BOOST_CHECK_EQUAL( loaded->GetNetCode(), 0 );
667
668 const THIEVING_SETTINGS& loadedSettings = loaded->GetThievingSettings();
669 BOOST_CHECK( loadedSettings.pattern == THIEVING_PATTERN::HATCH );
670 BOOST_CHECK_EQUAL( loadedSettings.element_size, thieving.element_size );
671 BOOST_CHECK_EQUAL( loadedSettings.gap, thieving.gap );
672 BOOST_CHECK_EQUAL( loadedSettings.line_width, thieving.line_width );
673 BOOST_CHECK_EQUAL( loadedSettings.stagger, true );
674 BOOST_CHECK( loadedSettings.orientation.GetAngle() == EDA_ANGLE( 30.0, DEGREES_T ) );
675
676 std::filesystem::remove( tmpPath );
677}
678
679
684BOOST_AUTO_TEST_CASE( CopperThievingZone_AllPatternsRoundTrip )
685{
686 const std::array<THIEVING_PATTERN, 3> patterns = {
690 };
691
692 for( THIEVING_PATTERN pattern : patterns )
693 {
694 BOOST_TEST_CONTEXT( "pattern enum value " << static_cast<int>( pattern ) )
695 {
696 std::unique_ptr<BOARD> writeBoard = std::make_unique<BOARD>();
697
698 ZONE* zone = new ZONE( writeBoard.get() );
699 zone->SetLayer( F_Cu );
700 zone->AppendCorner( VECTOR2I( 0, 0 ), -1 );
701 zone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( 5 ), 0 ), -1 );
702 zone->AppendCorner( VECTOR2I( pcbIUScale.mmToIU( 5 ), pcbIUScale.mmToIU( 5 ) ), -1 );
703 zone->AppendCorner( VECTOR2I( 0, pcbIUScale.mmToIU( 5 ) ), -1 );
705
706 THIEVING_SETTINGS thieving = zone->GetThievingSettings();
707 thieving.pattern = pattern;
708 zone->SetThievingSettings( thieving );
709
710 writeBoard->Add( zone );
711
712 std::filesystem::path tmpPath = std::filesystem::temp_directory_path()
713 / "copper_thieving_pattern.kicad_pcb";
714 PCB_IO_KICAD_SEXPR writer;
715 writer.SaveBoard( tmpPath.string(), *writeBoard );
716
717 PCB_IO_KICAD_SEXPR reader;
718 std::unique_ptr<BOARD> readBoard = reader.LoadBoard( tmpPath.string() );
719
720 BOOST_REQUIRE_EQUAL( readBoard->Zones().size(), 1u );
721 BOOST_CHECK( readBoard->Zones()[0]->GetThievingSettings().pattern == pattern );
722
723 std::filesystem::remove( tmpPath );
724 }
725 }
726}
727
728
735BOOST_AUTO_TEST_CASE( CopperThievingZone_RejectedInOldFileVersion )
736{
737 std::filesystem::path tmpPath = std::filesystem::temp_directory_path()
738 / "copper_thieving_old_version.kicad_pcb";
739 std::ofstream out( tmpPath );
740 // 20260512 is the Net Chains release; thieving needs >= 20260513. Using the
741 // immediately-prior version exercises the boundary check.
742 out << "(kicad_pcb (version 20260512) (generator \"test\") (generator_version \"test\")"
743 << " (general (thickness 1.6)) (paper \"A4\")"
744 << " (layers (0 \"F.Cu\" signal) (31 \"B.Cu\" signal))"
745 << " (zone (net 0) (net_name \"\") (layer \"F.Cu\") (uuid \"00000000-0000-0000-0000-000000000001\")"
746 << " (hatch edge 0.5)"
747 << " (connect_pads (clearance 0))"
748 << " (min_thickness 0.25) (filled_areas_thickness no)"
749 << " (fill yes (mode thieving) (thermal_gap 0.5) (thermal_bridge_width 0.5)"
750 << " (island_removal_mode 0))"
751 << " (polygon (pts (xy 0 0) (xy 5 0) (xy 5 5) (xy 0 5))))"
752 << ")";
753 out.close();
754
755 PCB_IO_KICAD_SEXPR reader;
756 BOOST_REQUIRE_THROW( reader.LoadBoard( tmpPath.string() ), IO_ERROR );
757
758 std::filesystem::remove( tmpPath );
759}
760
761
762BOOST_AUTO_TEST_CASE( MalformedDimensionTextThrowsCleanly )
763{
764 KI_TEST::SCOPED_TEMP_DIR tempDir( "kicad_qa_malformed_dimension_text" );
765 std::filesystem::path tmpPath = tempDir.Path() / "malformed_dimension_text.kicad_pcb";
766 std::ofstream out( tmpPath );
767 out << "(kicad_pcb (version 20240108) (generator \"test\")"
768 << " (general (thickness 1.6)) (paper \"A4\")"
769 << " (layers (0 \"F.Cu\" signal) (31 \"B.Cu\" signal) (36 \"B.SilkS\" user \"b.silkscreen\"))"
770 << " (dimension (type aligned) (layer \"B.SilkS\")"
771 << " (gr_text \"1 mm\" (at broken))))";
772 out.close();
773
774 PCB_IO_KICAD_SEXPR reader;
775
776 BOOST_CHECK_THROW( reader.LoadBoard( tmpPath.string() ), IO_ERROR );
777}
778
779
786BOOST_AUTO_TEST_CASE( CopperThievingZone_RejectsMalformedGeometry )
787{
788 std::filesystem::path tmpPath = std::filesystem::temp_directory_path()
789 / "copper_thieving_malformed.kicad_pcb";
790 std::ofstream out( tmpPath );
791 out << "(kicad_pcb (version 20260513) (generator \"test\") (generator_version \"test\")"
792 << " (general (thickness 1.6)) (paper \"A4\")"
793 << " (layers (0 \"F.Cu\" signal) (31 \"B.Cu\" signal))"
794 << " (zone (net 0) (net_name \"\") (layer \"F.Cu\") (uuid \"00000000-0000-0000-0000-000000000002\")"
795 << " (hatch edge 0.5)"
796 << " (connect_pads (clearance 0))"
797 << " (min_thickness 0.25) (filled_areas_thickness no)"
798 << " (fill yes (mode thieving)"
799 << " (thermal_gap 0.5) (thermal_bridge_width 0.5)"
800 << " (island_removal_mode 0)"
801 << " (thieving (type dots) (size -1) (gap 0)"
802 << " (width -5) (stagger no) (orientation 0)))"
803 << " (polygon (pts (xy 0 0) (xy 5 0) (xy 5 5) (xy 0 5))))"
804 << ")";
805 out.close();
806
807 PCB_IO_KICAD_SEXPR reader;
808 std::unique_ptr<BOARD> readBoard = reader.LoadBoard( tmpPath.string() );
809
810 BOOST_REQUIRE_EQUAL( readBoard->Zones().size(), 1u );
811
812 const THIEVING_SETTINGS& loaded = readBoard->Zones()[0]->GetThievingSettings();
813 BOOST_CHECK_GT( loaded.element_size, 0 );
814 BOOST_CHECK_GT( loaded.gap, 0 );
815 BOOST_CHECK_GT( loaded.line_width, 0 );
816
817 std::filesystem::remove( tmpPath );
818}
819
820
828BOOST_AUTO_TEST_CASE( Issue24955_AppendDoesNotInheritSessionZoneDefaults )
829{
830 KI_TEST::SCOPED_TEMP_DIR tempDir( "kicad_qa_zone_defaults_append" );
831 std::filesystem::path tmpPath = tempDir.Path() / "thermal_zone_block.kicad_pcb";
832 std::ofstream out( tmpPath );
833
834 // Two zones, first with every omitted-when-default token left out (thermal, polygon
835 // fill, no smoothing, unlocked), second with the explicit tokens
836 out << "(kicad_pcb (version 20260513) (generator \"test\") (generator_version \"test\")"
837 << " (general (thickness 1.6)) (paper \"A4\")"
838 << " (layers (0 \"F.Cu\" signal) (31 \"B.Cu\" signal))"
839 << " (zone (net 0) (net_name \"\") (layer \"F.Cu\") (uuid \"00000000-0000-0000-0000-000000000001\")"
840 << " (hatch edge 0.5)"
841 << " (connect_pads (clearance 0.5))"
842 << " (min_thickness 0.25) (filled_areas_thickness no)"
843 << " (fill yes (thermal_gap 0.5) (thermal_bridge_width 0.5) (island_removal_mode 0))"
844 << " (polygon (pts (xy 0 0) (xy 5 0) (xy 5 5) (xy 0 5))))"
845 << " (zone (net 0) (net_name \"\") (layer \"F.Cu\") (uuid \"00000000-0000-0000-0000-000000000002\")"
846 << " (locked yes)"
847 << " (hatch edge 0.5)"
848 << " (connect_pads yes (clearance 0.5))"
849 << " (min_thickness 0.25) (filled_areas_thickness no)"
850 << " (fill yes (thermal_gap 0.5) (thermal_bridge_width 0.5) (island_removal_mode 0)"
851 << " (smoothing fillet) (radius 1))"
852 << " (polygon (pts (xy 10 0) (xy 15 0) (xy 15 5) (xy 10 5))))"
853 << ")";
854 out.close();
855
856 // A destination board whose session zone defaults differ from the format defaults
857 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
858
859 ZONE_SETTINGS settings = board->GetDesignSettings().GetDefaultZoneSettings();
863 settings.SetCornerRadius( pcbIUScale.mmToIU( 1 ) );
864 settings.m_HatchSmoothingLevel = 2;
865 settings.m_Locked = true;
866 board->GetDesignSettings().SetDefaultZoneSettings( settings );
867
868 kicadPlugin.LoadAndAppendBoard( tmpPath.string(), *board );
869
870 BOOST_REQUIRE_EQUAL( board->Zones().size(), 2u );
871
872 // The all-defaults zone must not pick up the board's session defaults
873 ZONE* omitted = board->Zones()[0];
874 BOOST_CHECK( omitted->GetPadConnection() == ZONE_CONNECTION::THERMAL );
875 BOOST_CHECK( omitted->GetFillMode() == ZONE_FILL_MODE::POLYGONS );
877 BOOST_CHECK_EQUAL( omitted->GetCornerRadius(), 0 );
879 BOOST_CHECK( !omitted->IsLocked() );
880
881 // Explicit tokens still win
882 ZONE* explicitZone = board->Zones()[1];
883 BOOST_CHECK( explicitZone->GetPadConnection() == ZONE_CONNECTION::FULL );
885 BOOST_CHECK_EQUAL( explicitZone->GetCornerRadius(), pcbIUScale.mmToIU( 1 ) );
886 BOOST_CHECK( explicitZone->IsLocked() );
887}
888
889
894BOOST_AUTO_TEST_CASE( Issue24911_CancelledAppendLeavesBoardUntouched )
895{
896 // Requests cancellation once the parse reaches the given line, then stalls long
897 // enough to open the parser 250ms cancel checkpoint window
898 class CANCEL_AT_LINE_READER : public STRING_LINE_READER
899 {
900 public:
901 CANCEL_AT_LINE_READER( const std::string& aText, unsigned aCancelLine, bool* aCancelFlag ) :
902 STRING_LINE_READER( aText, wxT( "cancel-test" ) ),
903 m_cancelLine( aCancelLine ),
904 m_cancelFlag( aCancelFlag )
905 {
906 }
907
908 char* ReadLine() override
909 {
910 if( !*m_cancelFlag && LineNumber() >= m_cancelLine )
911 {
912 *m_cancelFlag = true;
913 std::this_thread::sleep_for( std::chrono::milliseconds( 300 ) );
914 }
915
917 }
918
919 private:
920 unsigned m_cancelLine;
921 bool* m_cancelFlag;
922 };
923
924 class CANCELLING_REPORTER : public PROGRESS_REPORTER
925 {
926 public:
927 CANCELLING_REPORTER( bool* aCancelFlag ) :
928 m_cancelFlag( aCancelFlag )
929 {
930 }
931
932 void SetNumPhases( int ) override {}
933 void AddPhases( int ) override {}
934 void BeginPhase( int ) override {}
935 void AdvancePhase() override {}
936 void AdvancePhase( const wxString& ) override {}
937 void Report( const wxString& ) override {}
938 void SetCurrentProgress( double ) override {}
939 void SetMaxProgress( int ) override {}
940 void AdvanceProgress() override {}
941 void SetTitle( const wxString& ) override {}
942 bool IsCancelled() const override { return *m_cancelFlag; }
943 bool KeepRefreshing( bool ) override { return !*m_cancelFlag; }
944
945 private:
946 bool* m_cancelFlag;
947 };
948
949 // Source board with one net and enough tracks that the cancel lands mid-parse
950 std::unique_ptr<BOARD> source = std::make_unique<BOARD>();
951
952 NETINFO_ITEM* net = new NETINFO_ITEM( source.get(), wxT( "GHOST_NET" ), 1 );
953 source->Add( net );
954
955 for( int i = 0; i < 400; i++ )
956 {
957 PCB_TRACK* track = new PCB_TRACK( source.get() );
958 track->SetStart( VECTOR2I( i * 10000, 0 ) );
959 track->SetEnd( VECTOR2I( i * 10000, 10000 ) );
960 track->SetWidth( 200000 );
961 track->SetLayer( F_Cu );
962 track->SetNet( net );
963 source->Add( track );
964 }
965
967 kicadPlugin.FormatBoardToFormatter( &fmt, source.get() );
968
969 // The formatter emits a single line, the parser can only be interrupted between lines
970 std::string text = fmt.GetString();
972
973 unsigned lineCount = std::count( text.begin(), text.end(), '\n' );
974
975 // Destination board with one pre-existing track
976 std::unique_ptr<BOARD> dest = std::make_unique<BOARD>();
977
978 PCB_TRACK* existing = new PCB_TRACK( dest.get() );
979 existing->SetStart( VECTOR2I( 0, 0 ) );
980 existing->SetEnd( VECTOR2I( 5000, 0 ) );
981 existing->SetWidth( 200000 );
982 existing->SetLayer( F_Cu );
983 dest->Add( existing );
984
985 size_t tracksBefore = dest->Tracks().size();
986 unsigned netsBefore = dest->GetNetInfo().GetNetCount();
987
988 bool cancelRequested = false;
989
990 CANCEL_AT_LINE_READER reader( text, lineCount / 2, &cancelRequested );
991 CANCELLING_REPORTER reporter( &cancelRequested );
992
993 BOOST_CHECK_THROW( kicadPlugin.DoLoad( reader, *dest, false, nullptr, &reporter, lineCount ), IO_ERROR );
994 BOOST_CHECK( cancelRequested );
995
996 BOOST_CHECK_EQUAL( dest->Tracks().size(), tracksBefore );
997 BOOST_CHECK_EQUAL( dest->GetNetInfo().GetNetCount(), netsBefore );
998}
999
1000
1001// Enumerating a footprint library that is not on disk must fail every time. The failed
1002// load leaves a cache bound to the missing path carrying a zero timestamp, which is also
1003// what a missing directory reports, so the cache looks current and the next enumerate
1004// answers with an empty library instead of the error.
1005BOOST_AUTO_TEST_CASE( MissingLibraryThrowsOnEveryEnumerate )
1006{
1007 std::filesystem::path libPath = std::filesystem::temp_directory_path() / "kicad_qa_no_such_library.pretty";
1008
1009 BOOST_REQUIRE( !std::filesystem::exists( libPath ) );
1010
1011 wxArrayString names;
1012
1013 BOOST_CHECK_THROW( kicadPlugin.FootprintEnumerate( names, libPath.string(), false ), IO_ERROR );
1014
1015 BOOST_CHECK_THROW( kicadPlugin.FootprintEnumerate( names, libPath.string(), false ), IO_ERROR );
1016}
1017
1018
1019// A parse warning leaves the footprint usable, so it must not stop the rest of the library loading
1020// fails on revert, since the warning then makes the whole library fail
1021BOOST_AUTO_TEST_CASE( ParseWarningDoesNotFailTheLibrary )
1022{
1023 KI_TEST::SCOPED_TEMP_DIR temp( wxS( "fp_warning" ) );
1024
1025 std::filesystem::path libPath = temp.Path() / "warning.pretty";
1026 std::filesystem::create_directories( libPath );
1027
1028 std::filesystem::path source =
1029 std::filesystem::path( KI_TEST::GetTestDataRootDir() ) / "libraries" / "Resistor_SMD.pretty";
1030
1031 for( const char* name : { "R_0402_1005Metric", "R_0603_1608Metric" } )
1032 std::filesystem::copy_file( source / ( std::string( name ) + ".kicad_mod" ),
1033 libPath / ( std::string( name ) + ".kicad_mod" ) );
1034
1035 // A zero size pad parses with a warning
1036 std::filesystem::path zeroPad = libPath / "R_0402_1005Metric.kicad_mod";
1037 std::ifstream in( zeroPad );
1038 std::string text( ( std::istreambuf_iterator<char>( in ) ), std::istreambuf_iterator<char>() );
1039 in.close();
1040
1041 size_t at = text.find( "(size 0.54 0.64)" );
1042 BOOST_REQUIRE( at != std::string::npos );
1043 text.replace( at, std::string( "(size 0.54 0.64)" ).size(), "(size 0 0)" );
1044 std::ofstream( zeroPad ) << text;
1045
1046 wxArrayString names;
1047
1048 BOOST_CHECK_NO_THROW( kicadPlugin.FootprintEnumerate( names, libPath.string(), false ) );
1049 BOOST_CHECK_EQUAL( names.size(), 2u );
1050 BOOST_CHECK( names.Index( wxS( "R_0402_1005Metric" ) ) != wxNOT_FOUND );
1051
1052 // A file that does not parse still fails the library, as it always has
1053 std::ofstream( libPath / "broken.kicad_mod" ) << "(footprint";
1054
1055 kicadPlugin.ClearCachedFootprints( libPath.string() );
1056 names.clear();
1057
1058 BOOST_CHECK_THROW( kicadPlugin.FootprintEnumerate( names, libPath.string(), false ), IO_ERROR );
1059}
1060
1061
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
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 NETINFO_ITEM 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:85
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:411
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1520
const TRACKS & Tracks() const
Definition board.h:463
const DRAWINGS & Drawings() const
Definition board.h:467
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
EDA_ANGLE GetAngle() const
Definition eda_angle.h:533
void SetAngle(const EDA_ANGLE &aAngle)
Definition eda_angle.h:535
VECTOR2I GetArcMid() const
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:418
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Adds an item to 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:61
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:289
@ 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 PCB_IO 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:94
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:97
virtual void SetWidth(int aWidth)
Definition pcb_track.h:91
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
Handle 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:1496
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:699
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:675
bool IsCopperThieving() const
Definition zone.h:349
ZONE_SETTINGS::CORNER_SMOOTHING GetCornerSmoothingType() const
Definition zone.h:771
void SetFillMode(ZONE_FILL_MODE aFillMode)
Definition zone.cpp:681
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:599
void SetNet(NETINFO_ITEM *aNetInfo) override
Override that drops aNetInfo when this zone is in copper-thieving fill mode.
Definition zone.cpp:666
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:775
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition zone.h:617
@ DEGREES_T
Definition eda_angle.h:30
@ RECURSE
Definition eda_item.h:51
@ SEGMENT
Definition eda_shape.h:56
std::string source
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 GetTestDataRootDir()
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:46
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.
THIEVING_PATTERN pattern
EDA_ORIENTATION orientation
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:708
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