KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_io_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 (C) 2012 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <wx/dir.h>
26#include <wx/ffile.h>
27#include <wx/log.h>
28#include <wx/msgdlg.h>
29#include <wx/mstream.h>
30
31#include <board.h>
33#include <callback_gal.h>
35#include <confirm.h>
36#include <convert_basic_shapes_to_polygon.h> // for enum RECT_CHAMFER_POSITIONS definition
37#include <fmt/core.h>
38#include <font/fontconfig.h>
39#include <footprint.h>
41#include <kiface_base.h>
42#include <kiplatform/io.h>
43#include <layer_range.h>
44#include <macros.h>
45#include <pad.h>
46#include <pcb_dimension.h>
47#include <pcb_generator.h>
48#include <pcb_group.h>
51#include <pcb_point.h>
52#include <pcb_reference_image.h>
53#include <pcb_barcode.h>
54#include <pcb_shape.h>
55#include <pcb_table.h>
56#include <pcb_tablecell.h>
57#include <pcb_target.h>
58#include <pcb_text.h>
59#include <pcb_textbox.h>
60#include <pcb_track.h>
61#include <pcbnew_settings.h>
62#include <pgm_base.h>
63#include <progress_reporter.h>
64#include <reporter.h>
65#include <string_utils.h>
66#include <trace_helpers.h>
68#include <zone.h>
69
70#include <build_version.h>
71#include <filter_reader.h>
72#include <ctl_flags.h>
73
74
75using namespace PCB_KEYS_T;
76
77
78FP_CACHE_ENTRY::FP_CACHE_ENTRY( FOOTPRINT* aFootprint, const WX_FILENAME& aFileName ) :
79 m_filename( aFileName ),
80 m_footprint( aFootprint )
81{ }
82
83
84FP_CACHE::FP_CACHE( PCB_IO_KICAD_SEXPR* aOwner, const wxString& aLibraryPath )
85{
86 m_owner = aOwner;
87 m_lib_raw_path = aLibraryPath;
88 m_lib_path.SetPath( aLibraryPath );
90 m_cache_dirty = true;
91}
92
93
94void FP_CACHE::Save( FOOTPRINT* aFootprintFilter )
95{
97
98 if( !m_lib_path.DirExists() && !m_lib_path.Mkdir() )
99 {
100 THROW_IO_ERROR( wxString::Format( _( "Cannot create footprint library '%s'." ),
101 m_lib_raw_path ) );
102 }
103
104 if( !m_lib_path.IsDirWritable() )
105 {
106 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' is read only." ),
107 m_lib_raw_path ) );
108 }
109
110 for( auto it = m_footprints.begin(); it != m_footprints.end(); ++it )
111 {
112 FP_CACHE_ENTRY* fpCacheEntry = it->second;
113 std::unique_ptr<FOOTPRINT>& footprint = fpCacheEntry->GetFootprint();
114
115 if( aFootprintFilter && footprint.get() != aFootprintFilter )
116 continue;
117
118 // If we've requested to embed the fonts in the footprint, do so. Otherwise, clear the
119 // embedded fonts from the footprint. Embedded fonts will be used if available.
120 if( footprint->GetAreFontsEmbedded() )
121 footprint->EmbedFonts();
122 else
123 footprint->GetEmbeddedFiles()->ClearEmbeddedFonts();
124
125 WX_FILENAME fn = fpCacheEntry->GetFileName();
126 wxString fileName = fn.GetFullPath();
127
128 // Allow file output stream to go out of scope to close the file stream before
129 // renaming the file.
130 {
131 wxLogTrace( traceKicadPcbPlugin, wxT( "Writing library file '%s'." ),
132 fileName );
133
134 PRETTIFIED_FILE_OUTPUTFORMATTER formatter( fileName );
135
136 m_owner->SetOutputFormatter( &formatter );
137 m_owner->Format( footprint.get() );
138 formatter.Finish();
139 }
140
142 }
143
144 if( m_lib_path.IsFileReadable() && m_lib_path.GetModificationTime().IsValid() )
145 m_cache_timestamp += m_lib_path.GetModificationTime().GetValue().GetValue();
146
147 // If we've saved the full cache, we clear the dirty flag.
148 if( !aFootprintFilter )
149 m_cache_dirty = false;
150}
151
152
154{
155 m_cache_dirty = false;
157
158 wxDir dir( m_lib_raw_path );
159
160 if( !dir.IsOpened() )
161 {
162 wxString msg = wxString::Format( _( "Footprint library '%s' not found." ),
164 THROW_IO_ERROR( msg );
165 }
166
167 wxString fullName;
168 wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::KiCadFootprintFileExtension );
169
170 // wxFileName construction is egregiously slow. Construct it once and just swap out
171 // the filename thereafter.
172 WX_FILENAME fn( m_lib_raw_path, wxT( "dummyName" ) );
173
174 if( dir.GetFirst( &fullName, fileSpec ) )
175 {
176 wxString cacheError;
177
178 do
179 {
180 fn.SetFullName( fullName );
181
182 // Queue I/O errors so only files that fail to parse don't get loaded.
183 try
184 {
185 FILE_LINE_READER reader( fn.GetFullPath() );
186 PCB_IO_KICAD_SEXPR_PARSER parser( &reader, nullptr, nullptr );
187
188 FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( parser.Parse() );
189 wxString fpName = fn.GetName();
190
191 if( !footprint )
192 THROW_IO_ERROR( wxEmptyString ); // caught locally, just below...
193
194 footprint->SetFPID( LIB_ID( wxEmptyString, fpName ) );
195 m_footprints.insert( fpName, new FP_CACHE_ENTRY( footprint, fn ) );
196
197 // Collect any non-fatal parse warnings
198 for( const wxString& warning : parser.GetParseWarnings() )
199 {
200 if( !cacheError.IsEmpty() )
201 cacheError += wxT( "\n\n" );
202
203 cacheError += wxString::Format( _( "Warning in file '%s'" ) + '\n',
204 fn.GetFullPath() );
205 cacheError += warning;
206 }
207 }
208 catch( const IO_ERROR& ioe )
209 {
210 if( !cacheError.IsEmpty() )
211 cacheError += wxT( "\n\n" );
212
213 cacheError += wxString::Format( _( "Unable to read file '%s'" ) + '\n',
214 fn.GetFullPath() );
215 cacheError += ioe.What();
216 }
217 } while( dir.GetNext( &fullName ) );
218
220
221 if( !cacheError.IsEmpty() )
222 THROW_IO_ERROR( cacheError );
223 }
224}
225
226
227void FP_CACHE::Remove( const wxString& aFootprintName )
228{
229 auto it = m_footprints.find( aFootprintName );
230
231 if( it == m_footprints.end() )
232 {
233 wxString msg = wxString::Format( _( "Library '%s' has no footprint '%s'." ),
235 aFootprintName );
236 THROW_IO_ERROR( msg );
237 }
238
239 // Remove the footprint from the cache and delete the footprint file from the library.
240 wxString fullPath = it->second->GetFileName().GetFullPath();
241 m_footprints.erase( aFootprintName );
242 wxRemoveFile( fullPath );
243}
244
245
246bool FP_CACHE::IsPath( const wxString& aPath ) const
247{
248 return aPath == m_lib_raw_path;
249}
250
251
252void FP_CACHE::SetPath( const wxString& aPath )
253{
254 m_lib_raw_path = aPath;
255 m_lib_path.SetPath( aPath );
256
257
258 for( const auto& footprint : GetFootprints() )
259 footprint.second->SetFilePath( aPath );
260}
261
262
264{
266
267 return m_cache_dirty;
268}
269
270
271long long FP_CACHE::GetTimestamp( const wxString& aLibPath )
272{
273 wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::KiCadFootprintFileExtension );
274
275 return KIPLATFORM::IO::TimestampDir( aLibPath, fileSpec );
276}
277
278
279bool PCB_IO_KICAD_SEXPR::CanReadBoard( const wxString& aFileName ) const
280{
281 if( !PCB_IO::CanReadBoard( aFileName ) )
282 return false;
283
284 try
285 {
286 FILE_LINE_READER reader( aFileName );
287 PCB_IO_KICAD_SEXPR_PARSER parser( &reader, nullptr, m_queryUserCallback );
288
289 return parser.IsValidBoardHeader();
290 }
291 catch( const IO_ERROR& )
292 {
293 }
294
295 return false;
296}
297
298
299void PCB_IO_KICAD_SEXPR::SaveBoard( const wxString& aFileName, BOARD* aBoard,
300 const std::map<std::string, UTF8>* aProperties )
301{
302 wxString sanityResult = aBoard->GroupsSanityCheck();
303
304 if( sanityResult != wxEmptyString && m_queryUserCallback )
305 {
307 _( "Internal Group Data Error" ), wxICON_ERROR,
308 wxString::Format( _( "Please report this bug. Error validating group "
309 "structure: %s\n\nSave anyway?" ), sanityResult ),
310 _( "Save Anyway" ) ) )
311 {
312 return;
313 }
314 }
315
316 PRETTIFIED_FILE_OUTPUTFORMATTER formatter( aFileName );
317 FormatBoardToFormatter( &formatter, aBoard, aProperties );
318 formatter.Finish();
319}
320
321
323 const std::map<std::string, UTF8>* aProperties )
324{
325 init( aProperties );
326
327 m_board = aBoard; // after init()
328
329 // If the user wants fonts embedded, make sure that they are added to the board. Otherwise,
330 // remove any fonts that were previously embedded.
331 if( m_board->GetAreFontsEmbedded() )
332 m_board->EmbedFonts();
333 else
334 m_board->GetEmbeddedFiles()->ClearEmbeddedFonts();
335
336 m_out = aOut;
337
338 m_out->Print( "(kicad_pcb (version %d) (generator \"pcbnew\") (generator_version %s)",
340 m_out->Quotew( GetMajorMinorVersion() ).c_str() );
341
342 Format( aBoard );
343
344 m_out->Print( ")" );
345
346 m_out = nullptr;
347}
348
349
350BOARD_ITEM* PCB_IO_KICAD_SEXPR::Parse( const wxString& aClipboardSourceInput )
351{
352 std::string input = TO_UTF8( aClipboardSourceInput );
353
354 STRING_LINE_READER reader( input, wxT( "clipboard" ) );
355 PCB_IO_KICAD_SEXPR_PARSER parser( &reader, nullptr, m_queryUserCallback );
356
357 try
358 {
359 return parser.Parse();
360 }
361 catch( const PARSE_ERROR& parse_error )
362 {
363 if( parser.IsTooRecent() )
364 throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
365 else
366 throw;
367 }
368}
369
370
371void PCB_IO_KICAD_SEXPR::Format( const BOARD_ITEM* aItem ) const
372{
373 switch( aItem->Type() )
374 {
375 case PCB_T:
376 format( static_cast<const BOARD*>( aItem ) );
377 break;
378
380 case PCB_DIM_CENTER_T:
381 case PCB_DIM_RADIAL_T:
383 case PCB_DIM_LEADER_T:
384 format( static_cast<const PCB_DIMENSION_BASE*>( aItem ) );
385 break;
386
387 case PCB_SHAPE_T:
388 format( static_cast<const PCB_SHAPE*>( aItem ) );
389 break;
390
392 format( static_cast<const PCB_REFERENCE_IMAGE*>( aItem ) );
393 break;
394
395 case PCB_POINT_T:
396 format( static_cast<const PCB_POINT*>( aItem ) );
397 break;
398
399 case PCB_TARGET_T:
400 format( static_cast<const PCB_TARGET*>( aItem ) );
401 break;
402
403 case PCB_FOOTPRINT_T:
404 format( static_cast<const FOOTPRINT*>( aItem ) );
405 break;
406
407 case PCB_PAD_T:
408 format( static_cast<const PAD*>( aItem ) );
409 break;
410
411 case PCB_FIELD_T:
412 // Handled in the footprint formatter when properties are formatted
413 break;
414
415 case PCB_TEXT_T:
416 format( static_cast<const PCB_TEXT*>( aItem ) );
417 break;
418
419 case PCB_TEXTBOX_T:
420 format( static_cast<const PCB_TEXTBOX*>( aItem ) );
421 break;
422
423 case PCB_BARCODE_T:
424 format( static_cast<const PCB_BARCODE*>( aItem ) );
425 break;
426
427 case PCB_TABLE_T:
428 format( static_cast<const PCB_TABLE*>( aItem ) );
429 break;
430
431 case PCB_GROUP_T:
432 format( static_cast<const PCB_GROUP*>( aItem ) );
433 break;
434
435 case PCB_GENERATOR_T:
436 format( static_cast<const PCB_GENERATOR*>( aItem ) );
437 break;
438
439 case PCB_TRACE_T:
440 case PCB_ARC_T:
441 case PCB_VIA_T:
442 format( static_cast<const PCB_TRACK*>( aItem ) );
443 break;
444
445 case PCB_ZONE_T:
446 format( static_cast<const ZONE*>( aItem ) );
447 break;
448
449 default:
450 wxFAIL_MSG( wxT( "Cannot format item " ) + aItem->GetClass() );
451 }
452}
453
454
455std::string formatInternalUnits( const int aValue, const EDA_DATA_TYPE aDataType = EDA_DATA_TYPE::DISTANCE )
456{
457 return EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aValue, aDataType );
458}
459
460
461std::string formatInternalUnits( const VECTOR2I& aCoord )
462{
464}
465
466
467std::string formatInternalUnits( const VECTOR2I& aCoord, const FOOTPRINT* aParentFP )
468{
469 if( aParentFP )
470 {
471 VECTOR2I coord = aCoord - aParentFP->GetPosition();
472 RotatePoint( coord, -aParentFP->GetOrientation() );
473 return formatInternalUnits( coord );
474 }
475
476 return formatInternalUnits( aCoord );
477}
478
479
480void PCB_IO_KICAD_SEXPR::formatLayer( PCB_LAYER_ID aLayer, bool aIsKnockout ) const
481{
482 m_out->Print( "(layer %s %s)",
483 m_out->Quotew( LSET::Name( aLayer ) ).c_str(),
484 aIsKnockout ? "knockout" : "" );
485}
486
487
489 const FOOTPRINT* aParentFP ) const
490{
491 m_out->Print( "(pts" );
492
493 for( int ii = 0; ii < outline.PointCount(); ++ii )
494 {
495 int ind = outline.ArcIndex( ii );
496
497 if( ind < 0 )
498 {
499 m_out->Print( "(xy %s)",
500 formatInternalUnits( outline.CPoint( ii ), aParentFP ).c_str() );
501 }
502 else
503 {
504 const SHAPE_ARC& arc = outline.Arc( ind );
505 m_out->Print( "(arc (start %s) (mid %s) (end %s))",
506 formatInternalUnits( arc.GetP0(), aParentFP ).c_str(),
507 formatInternalUnits( arc.GetArcMid(), aParentFP ).c_str(),
508 formatInternalUnits( arc.GetP1(), aParentFP ).c_str() );
509
510 do
511 {
512 ++ii;
513 } while( ii < outline.PointCount() && outline.ArcIndex( ii ) == ind );
514
515 --ii;
516 }
517 }
518
519 m_out->Print( ")" );
520}
521
522
524{
525 wxString resolvedText( aText->GetShownText( true ) );
526 std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = aText->GetRenderCache( aText->GetFont(),
527 resolvedText );
528
529 m_out->Print( "(render_cache %s %s",
530 m_out->Quotew( resolvedText ).c_str(),
531 EDA_UNIT_UTILS::FormatAngle( aText->GetDrawRotation() ).c_str() );
532
534
535 CALLBACK_GAL callback_gal( empty_opts,
536 // Polygon callback
537 [&]( const SHAPE_LINE_CHAIN& aPoly )
538 {
539 m_out->Print( "(polygon" );
540 formatPolyPts( aPoly );
541 m_out->Print( ")" );
542 } );
543
544 callback_gal.SetLineWidth( aText->GetTextThickness() );
545 callback_gal.DrawGlyphs( *cache );
546
547 m_out->Print( ")" );
548}
549
550
551void PCB_IO_KICAD_SEXPR::formatSetup( const BOARD* aBoard ) const
552{
553 // Setup
554 m_out->Print( "(setup" );
555
556 // Save the board physical stackup structure
557 const BOARD_STACKUP& stackup = aBoard->GetDesignSettings().GetStackupDescriptor();
558
559 if( aBoard->GetDesignSettings().m_HasStackup )
560 stackup.FormatBoardStackup( m_out, aBoard );
561
562 BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings();
563
564 m_out->Print( "(pad_to_mask_clearance %s)",
565 formatInternalUnits( dsnSettings.m_SolderMaskExpansion ).c_str() );
566
567 if( dsnSettings.m_SolderMaskMinWidth )
568 {
569 m_out->Print( "(solder_mask_min_width %s)",
570 formatInternalUnits( dsnSettings.m_SolderMaskMinWidth ).c_str() );
571 }
572
573 if( dsnSettings.m_SolderPasteMargin != 0 )
574 {
575 m_out->Print( "(pad_to_paste_clearance %s)",
576 formatInternalUnits( dsnSettings.m_SolderPasteMargin ).c_str() );
577 }
578
579 if( dsnSettings.m_SolderPasteMarginRatio != 0 )
580 {
581 m_out->Print( "(pad_to_paste_clearance_ratio %s)",
582 FormatDouble2Str( dsnSettings.m_SolderPasteMarginRatio ).c_str() );
583 }
584
585 KICAD_FORMAT::FormatBool( m_out, "allow_soldermask_bridges_in_footprints",
586 dsnSettings.m_AllowSoldermaskBridgesInFPs );
587
588 m_out->Print( 0, " (tenting " );
589 KICAD_FORMAT::FormatBool( m_out, "front", dsnSettings.m_TentViasFront );
590 KICAD_FORMAT::FormatBool( m_out, "back", dsnSettings.m_TentViasBack );
591 m_out->Print( 0, ")" );
592
593 m_out->Print( 0, " (covering " );
594 KICAD_FORMAT::FormatBool( m_out, "front", dsnSettings.m_CoverViasFront );
595 KICAD_FORMAT::FormatBool( m_out, "back", dsnSettings.m_CoverViasBack );
596 m_out->Print( 0, ")" );
597
598 m_out->Print( 0, " (plugging " );
599 KICAD_FORMAT::FormatBool( m_out, "front", dsnSettings.m_PlugViasFront );
600 KICAD_FORMAT::FormatBool( m_out, "back", dsnSettings.m_PlugViasBack );
601 m_out->Print( 0, ")" );
602
603 KICAD_FORMAT::FormatBool( m_out, "capping", dsnSettings.m_CapVias );
604
605 KICAD_FORMAT::FormatBool( m_out, "filling", dsnSettings.m_FillVias );
606
607 if( !dsnSettings.m_ZoneLayerProperties.empty() )
608 {
609 m_out->Print( 0, " (zone_defaults" );
610
611 for( const auto& [layer, properties] : dsnSettings.m_ZoneLayerProperties )
612 format( properties, 0, layer );
613
614 m_out->Print( 0, ")\n" );
615 }
616
617 VECTOR2I origin = dsnSettings.GetAuxOrigin();
618
619 if( origin != VECTOR2I( 0, 0 ) )
620 {
621 m_out->Print( "(aux_axis_origin %s %s)",
622 formatInternalUnits( origin.x ).c_str(),
623 formatInternalUnits( origin.y ).c_str() );
624 }
625
626 origin = dsnSettings.GetGridOrigin();
627
628 if( origin != VECTOR2I( 0, 0 ) )
629 {
630 m_out->Print( "(grid_origin %s %s)",
631 formatInternalUnits( origin.x ).c_str(),
632 formatInternalUnits( origin.y ).c_str() );
633 }
634
635 aBoard->GetPlotOptions().Format( m_out );
636
637 m_out->Print( ")" );
638}
639
640
641void PCB_IO_KICAD_SEXPR::formatGeneral( const BOARD* aBoard ) const
642{
643 const BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings();
644
645 m_out->Print( "(general" );
646
647 m_out->Print( "(thickness %s)",
648 formatInternalUnits( dsnSettings.GetBoardThickness() ).c_str() );
649
650 KICAD_FORMAT::FormatBool( m_out, "legacy_teardrops", aBoard->LegacyTeardrops() );
651
652 m_out->Print( ")" );
653
654 aBoard->GetPageSettings().Format( m_out );
655 aBoard->GetTitleBlock().Format( m_out );
656}
657
658
660{
661 m_out->Print( "(layers" );
662
663 // Save only the used copper layers from front to back.
664
665 for( PCB_LAYER_ID layer : aBoard->GetEnabledLayers().CuStack() )
666 {
667 m_out->Print( "(%d %s %s %s)",
668 layer,
669 m_out->Quotew( LSET::Name( layer ) ).c_str(),
670 LAYER::ShowType( aBoard->GetLayerType( layer ) ),
671 LSET::Name( layer ) == m_board->GetLayerName( layer )
672 ? ""
673 : m_out->Quotew( m_board->GetLayerName( layer ) ).c_str() );
674
675 }
676
677 // Save used non-copper layers in the order they are defined.
678 LSEQ seq = aBoard->GetEnabledLayers().TechAndUserUIOrder();
679
680 for( PCB_LAYER_ID layer : seq )
681 {
682 bool print_type = false;
683
684 // User layers (layer id >= User_1) have a qualifier
685 // default is "user", but other qualifiers exist
686 if( layer >= User_1 )
687 {
688 if( IsCopperLayer( layer ) )
689 print_type = true;
690
691 if( aBoard->GetLayerType( layer ) == LT_FRONT
692 || aBoard->GetLayerType( layer ) == LT_BACK )
693 print_type = true;
694 }
695
696 m_out->Print( "(%d %s %s %s)",
697 layer,
698 m_out->Quotew( LSET::Name( layer ) ).c_str(),
699 print_type
700 ? LAYER::ShowType( aBoard->GetLayerType( layer ) )
701 : "user",
702 m_board->GetLayerName( layer ) == LSET::Name( layer )
703 ? ""
704 : m_out->Quotew( m_board->GetLayerName( layer ) ).c_str() );
705 }
706
707 m_out->Print( ")" );
708}
709
710
712{
713 for( const std::pair<const wxString, wxString>& prop : aBoard->GetProperties() )
714 {
715 m_out->Print( "(property %s %s)",
716 m_out->Quotew( prop.first ).c_str(),
717 m_out->Quotew( prop.second ).c_str() );
718 }
719}
720
721
722void PCB_IO_KICAD_SEXPR::formatVariants( const BOARD* aBoard ) const
723{
724 const std::vector<wxString>& variantNames = aBoard->GetVariantNames();
725
726 if( variantNames.empty() )
727 return;
728
729 m_out->Print( "(variants" );
730
731 for( const wxString& variantName : variantNames )
732 {
733 m_out->Print( "(variant (name %s)", m_out->Quotew( variantName ).c_str() );
734
735 wxString description = aBoard->GetVariantDescription( variantName );
736
737 if( !description.IsEmpty() )
738 m_out->Print( "(description %s)", m_out->Quotew( description ).c_str() );
739
740 m_out->Print( ")" );
741 }
742
743 m_out->Print( ")" );
744}
745
746
747void PCB_IO_KICAD_SEXPR::formatHeader( const BOARD* aBoard ) const
748{
749 formatGeneral( aBoard );
750
751 // Layers list.
752 formatBoardLayers( aBoard );
753
754 // Setup
755 formatSetup( aBoard );
756
757 // Properties
758 formatProperties( aBoard );
759
760 // Variants
761 formatVariants( aBoard );
762}
763
764
766{
767 static const TEARDROP_PARAMETERS defaults;
768
769 return tdParams.m_Enabled == defaults.m_Enabled
770 && tdParams.m_BestLengthRatio == defaults.m_BestLengthRatio
771 && tdParams.m_TdMaxLen == defaults.m_TdMaxLen
772 && tdParams.m_BestWidthRatio == defaults.m_BestWidthRatio
773 && tdParams.m_TdMaxWidth == defaults.m_TdMaxWidth
774 && tdParams.m_CurvedEdges == defaults.m_CurvedEdges
776 && tdParams.m_AllowUseTwoTracks == defaults.m_AllowUseTwoTracks
777 && tdParams.m_TdOnPadsInZones == defaults.m_TdOnPadsInZones;
778}
779
780
782{
783 m_out->Print( "(teardrops (best_length_ratio %s) (max_length %s) (best_width_ratio %s) "
784 "(max_width %s)",
785 FormatDouble2Str( tdParams.m_BestLengthRatio ).c_str(),
786 formatInternalUnits( tdParams.m_TdMaxLen ).c_str(),
787 FormatDouble2Str( tdParams.m_BestWidthRatio ).c_str(),
788 formatInternalUnits( tdParams.m_TdMaxWidth ).c_str() );
789
790 KICAD_FORMAT::FormatBool( m_out, "curved_edges", tdParams.m_CurvedEdges );
791
792 m_out->Print( "(filter_ratio %s)",
793 FormatDouble2Str( tdParams.m_WidthtoSizeFilterRatio ).c_str() );
794
795 KICAD_FORMAT::FormatBool( m_out, "enabled", tdParams.m_Enabled );
796 KICAD_FORMAT::FormatBool( m_out, "allow_two_segments", tdParams.m_AllowUseTwoTracks );
797 KICAD_FORMAT::FormatBool( m_out, "prefer_zone_connections", !tdParams.m_TdOnPadsInZones );
798 m_out->Print( ")" );
799}
800
801
802void PCB_IO_KICAD_SEXPR::format( const BOARD* aBoard ) const
803{
804 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_footprints( aBoard->Footprints().begin(),
805 aBoard->Footprints().end() );
806 std::set<BOARD_ITEM*, BOARD::cmp_drawings> sorted_drawings( aBoard->Drawings().begin(),
807 aBoard->Drawings().end() );
808 std::set<PCB_TRACK*, PCB_TRACK::cmp_tracks> sorted_tracks( aBoard->Tracks().begin(),
809 aBoard->Tracks().end() );
810 std::set<PCB_POINT*, PCB_POINT::cmp_points> sorted_points( aBoard->Points().begin(),
811 aBoard->Points().end() );
812 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_zones( aBoard->Zones().begin(),
813 aBoard->Zones().end() );
814 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_groups( aBoard->Groups().begin(),
815 aBoard->Groups().end() );
816 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_generators( aBoard->Generators().begin(),
817 aBoard->Generators().end() );
818 formatHeader( aBoard );
819
820 // Save the footprints.
821 for( BOARD_ITEM* footprint : sorted_footprints )
822 Format( footprint );
823
824 // Save the graphical items on the board (not owned by a footprint)
825 for( BOARD_ITEM* item : sorted_drawings )
826 Format( item );
827
828 // Save the points
829 for( PCB_POINT* point : sorted_points )
830 Format( point );
831
832 // Do not save PCB_MARKERs, they can be regenerated easily.
833
834 // Save the tracks and vias.
835 for( PCB_TRACK* track : sorted_tracks )
836 Format( track );
837
838 // Save the polygon (which are the newer technology) zones.
839 for( auto zone : sorted_zones )
840 Format( zone );
841
842 // Save the groups
843 for( BOARD_ITEM* group : sorted_groups )
844 Format( group );
845
846 // Save the generators
847 for( BOARD_ITEM* gen : sorted_generators )
848 Format( gen );
849
850 // After writing all items, write the aggregated net chains section (if any)
851 struct CHAIN_INFO
852 {
853 std::vector<NETINFO_ITEM*> nets;
854 PAD* pads[2] = { nullptr, nullptr };
855 };
856
857 // Simple lexicographic ordering using ValueStringCompare comparator logic
858 auto cmp = []( const wxString& a, const wxString& b )
859 {
860 return ValueStringCompare( a, b ) < 0;
861 };
862
863 std::map<wxString, CHAIN_INFO, decltype( cmp )> chains( cmp );
864
865 for( NETINFO_ITEM* net : aBoard->GetNetInfo() )
866 {
867 if( !net )
868 continue;
869
870 if( net->GetNetChain().IsEmpty() && !net->GetTerminalPad( 0 ) && !net->GetTerminalPad( 1 ) )
871 continue; // nothing to aggregate
872
873 wxString chainName = net->GetNetChain();
874
875 if( chainName.IsEmpty() && ( net->GetTerminalPad( 0 ) || net->GetTerminalPad( 1 ) ) )
876 chainName = net->GetNetname(); // synthetic name for unnamed terminal association
877
878 CHAIN_INFO& info = chains[chainName];
879 info.nets.push_back( net );
880 for( int i = 0; i < 2; ++i )
881 {
882 if( net->GetTerminalPad( i ) && !info.pads[i] )
883 info.pads[i] = net->GetTerminalPad( i );
884 }
885 }
886
887 size_t count = 0;
888 for( const auto& kv : chains )
889 {
890 const CHAIN_INFO& si = kv.second;
891 const wxString& chainName = kv.first;
892 // Persist if: multi-net OR terminal pads OR explicit (non-empty) chain name
893 if( si.nets.size() > 1 || si.pads[0] || si.pads[1] || !chainName.IsEmpty() )
894 ++count;
895 }
896
897 if( count )
898 {
899 m_out->Print( "(net_chains" );
900 for( const auto& kv : chains )
901 {
902 const wxString& name = kv.first;
903 const CHAIN_INFO& si = kv.second;
904
905 if( si.nets.size() == 1 && !si.pads[0] && !si.pads[1] && name.IsEmpty() )
906 continue;
907
908 m_out->Print( " (net_chain (name %s)", m_out->Quotew( name ).c_str() );
909 m_out->Print( " (members" );
910 for( NETINFO_ITEM* n : si.nets )
911 {
912 m_out->Print( " (net %s)", m_out->Quotew( n->GetNetname() ).c_str() );
913 }
914 m_out->Print( ")" );
915
916 for( int i = 0; i < 2; ++i )
917 {
918 if( si.pads[i] )
919 m_out->Print( " (terminal_pad %s)",
920 m_out->Quotew( si.pads[i]->m_Uuid.AsString() ).c_str() );
921 }
922
923 m_out->Print( ")" );
924 }
925 m_out->Print( ")" );
926 }
927
928 // Save any embedded files
929 // Consolidate the embedded models in footprints into a single map
930 // to avoid duplicating the same model in the board file.
931 EMBEDDED_FILES files_to_write;
932
933 for( auto& file : aBoard->GetEmbeddedFiles()->EmbeddedFileMap() )
934 files_to_write.AddFile( file.second );
935
936 for( BOARD_ITEM* item : sorted_footprints )
937 {
938 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
939
940 for( auto& file : fp->GetEmbeddedFiles()->EmbeddedFileMap() )
941 files_to_write.AddFile( file.second );
942 }
943
944 m_out->Print( "(embedded_fonts %s)",
945 aBoard->GetEmbeddedFiles()->GetAreFontsEmbedded() ? "yes" : "no" );
946
947 if( !files_to_write.IsEmpty() )
948 files_to_write.WriteEmbeddedFiles( *m_out, ( m_ctl & CTL_FOR_BOARD ) );
949
950 // Remove the files so that they are not freed in the DTOR
951 files_to_write.ClearEmbeddedFiles( false );
952}
953
954
955void PCB_IO_KICAD_SEXPR::format( const PCB_DIMENSION_BASE* aDimension ) const
956{
957 const PCB_DIM_ALIGNED* aligned = dynamic_cast<const PCB_DIM_ALIGNED*>( aDimension );
958 const PCB_DIM_ORTHOGONAL* ortho = dynamic_cast<const PCB_DIM_ORTHOGONAL*>( aDimension );
959 const PCB_DIM_CENTER* center = dynamic_cast<const PCB_DIM_CENTER*>( aDimension );
960 const PCB_DIM_RADIAL* radial = dynamic_cast<const PCB_DIM_RADIAL*>( aDimension );
961 const PCB_DIM_LEADER* leader = dynamic_cast<const PCB_DIM_LEADER*>( aDimension );
962
963 m_out->Print( "(dimension" );
964
965 if( ortho ) // must be tested before aligned, because ortho is derived from aligned
966 // and aligned is not null
967 m_out->Print( "(type orthogonal)" );
968 else if( aligned )
969 m_out->Print( "(type aligned)" );
970 else if( leader )
971 m_out->Print( "(type leader)" );
972 else if( center )
973 m_out->Print( "(type center)" );
974 else if( radial )
975 m_out->Print( "(type radial)" );
976 else
977 wxFAIL_MSG( wxT( "Cannot format unknown dimension type!" ) );
978
979 if( aDimension->IsLocked() )
980 KICAD_FORMAT::FormatBool( m_out, "locked", aDimension->IsLocked() );
981
982 formatLayer( aDimension->GetLayer() );
983
984 KICAD_FORMAT::FormatUuid( m_out, aDimension->m_Uuid );
985
986 m_out->Print( "(pts (xy %s %s) (xy %s %s))",
987 formatInternalUnits( aDimension->GetStart().x ).c_str(),
988 formatInternalUnits( aDimension->GetStart().y ).c_str(),
989 formatInternalUnits( aDimension->GetEnd().x ).c_str(),
990 formatInternalUnits( aDimension->GetEnd().y ).c_str() );
991
992 if( aligned )
993 m_out->Print( "(height %s)", formatInternalUnits( aligned->GetHeight() ).c_str() );
994
995 if( radial )
996 {
997 m_out->Print( "(leader_length %s)",
998 formatInternalUnits( radial->GetLeaderLength() ).c_str() );
999 }
1000
1001 if( ortho )
1002 m_out->Print( "(orientation %d)", static_cast<int>( ortho->GetOrientation() ) );
1003
1004 if( !center )
1005 {
1006 m_out->Print( "(format (prefix %s) (suffix %s) (units %d) (units_format %d) (precision %d)",
1007 m_out->Quotew( aDimension->GetPrefix() ).c_str(),
1008 m_out->Quotew( aDimension->GetSuffix() ).c_str(),
1009 static_cast<int>( aDimension->GetUnitsMode() ),
1010 static_cast<int>( aDimension->GetUnitsFormat() ),
1011 static_cast<int>( aDimension->GetPrecision() ) );
1012
1013 if( aDimension->GetOverrideTextEnabled() )
1014 {
1015 m_out->Print( "(override_value %s)",
1016 m_out->Quotew( aDimension->GetOverrideText() ).c_str() );
1017 }
1018
1019 if( aDimension->GetSuppressZeroes() )
1020 KICAD_FORMAT::FormatBool( m_out, "suppress_zeroes", true );
1021
1022 m_out->Print( ")" );
1023 }
1024
1025 m_out->Print( "(style (thickness %s) (arrow_length %s) (text_position_mode %d)",
1026 formatInternalUnits( aDimension->GetLineThickness() ).c_str(),
1027 formatInternalUnits( aDimension->GetArrowLength() ).c_str(),
1028 static_cast<int>( aDimension->GetTextPositionMode() ) );
1029
1030 if( ortho || aligned )
1031 {
1032 switch( aDimension->GetArrowDirection() )
1033 {
1035 m_out->Print( "(arrow_direction outward)" );
1036 break;
1038 m_out->Print( "(arrow_direction inward)" );
1039 break;
1040 // No default, handle all cases
1041 }
1042 }
1043
1044 if( aligned )
1045 {
1046 m_out->Print( "(extension_height %s)",
1047 formatInternalUnits( aligned->GetExtensionHeight() ).c_str() );
1048 }
1049
1050 if( leader )
1051 m_out->Print( "(text_frame %d)", static_cast<int>( leader->GetTextBorder() ) );
1052
1053 m_out->Print( "(extension_offset %s)",
1054 formatInternalUnits( aDimension->GetExtensionOffset() ).c_str() );
1055
1056 if( aDimension->GetKeepTextAligned() )
1057 KICAD_FORMAT::FormatBool( m_out, "keep_text_aligned", true );
1058
1059 m_out->Print( ")" );
1060
1061 // Write dimension text after all other options to be sure the
1062 // text options are known when reading the file
1063 if( !center )
1064 format( static_cast<const PCB_TEXT*>( aDimension ) );
1065
1066 m_out->Print( ")" );
1067}
1068
1069
1070void PCB_IO_KICAD_SEXPR::format( const PCB_SHAPE* aShape ) const
1071{
1072 FOOTPRINT* parentFP = aShape->GetParentFootprint();
1073 std::string prefix = parentFP ? "fp" : "gr";
1074
1075 switch( aShape->GetShape() )
1076 {
1077 case SHAPE_T::SEGMENT:
1078 m_out->Print( "(%s_line (start %s) (end %s)",
1079 prefix.c_str(),
1080 formatInternalUnits( aShape->GetStart(), parentFP ).c_str(),
1081 formatInternalUnits( aShape->GetEnd(), parentFP ).c_str() );
1082 break;
1083
1084 case SHAPE_T::RECTANGLE:
1085 m_out->Print( "(%s_rect (start %s) (end %s)",
1086 prefix.c_str(),
1087 formatInternalUnits( aShape->GetStart(), parentFP ).c_str(),
1088 formatInternalUnits( aShape->GetEnd(), parentFP ).c_str() );
1089
1090 if( aShape->GetCornerRadius() > 0 )
1091 m_out->Print( " (radius %s)", formatInternalUnits( aShape->GetCornerRadius() ).c_str() );
1092 break;
1093
1094 case SHAPE_T::CIRCLE:
1095 m_out->Print( "(%s_circle (center %s) (end %s)",
1096 prefix.c_str(),
1097 formatInternalUnits( aShape->GetStart(), parentFP ).c_str(),
1098 formatInternalUnits( aShape->GetEnd(), parentFP ).c_str() );
1099 break;
1100
1101 case SHAPE_T::ARC:
1102 m_out->Print( "(%s_arc (start %s) (mid %s) (end %s)",
1103 prefix.c_str(),
1104 formatInternalUnits( aShape->GetStart(), parentFP ).c_str(),
1105 formatInternalUnits( aShape->GetArcMid(), parentFP ).c_str(),
1106 formatInternalUnits( aShape->GetEnd(), parentFP ).c_str() );
1107 break;
1108
1109 case SHAPE_T::POLY:
1110 if( aShape->IsPolyShapeValid() )
1111 {
1112 const SHAPE_POLY_SET& poly = aShape->GetPolyShape();
1113 const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
1114
1115 m_out->Print( "(%s_poly", prefix.c_str() );
1116 formatPolyPts( outline, parentFP );
1117 }
1118 else
1119 {
1120 return;
1121 }
1122
1123 break;
1124
1125 case SHAPE_T::BEZIER:
1126 m_out->Print( "(%s_curve (pts (xy %s) (xy %s) (xy %s) (xy %s))",
1127 prefix.c_str(),
1128 formatInternalUnits( aShape->GetStart(), parentFP ).c_str(),
1129 formatInternalUnits( aShape->GetBezierC1(), parentFP ).c_str(),
1130 formatInternalUnits( aShape->GetBezierC2(), parentFP ).c_str(),
1131 formatInternalUnits( aShape->GetEnd(), parentFP ).c_str() );
1132 break;
1133
1134 case SHAPE_T::ELLIPSE:
1135 m_out->Print( "(%s_ellipse (center %s) (major_radius %s) (minor_radius %s) "
1136 "(rotation_angle %s)",
1137 prefix.c_str(), formatInternalUnits( aShape->GetEllipseCenter(), parentFP ).c_str(),
1138 formatInternalUnits( aShape->GetEllipseMajorRadius() ).c_str(),
1139 formatInternalUnits( aShape->GetEllipseMinorRadius() ).c_str(),
1140 EDA_UNIT_UTILS::FormatAngle( aShape->GetEllipseRotation() ).c_str() );
1141 break;
1142
1144 m_out->Print( "(%s_ellipse_arc (center %s) (major_radius %s) (minor_radius %s) "
1145 "(rotation_angle %s) (start_angle %s) (end_angle %s)",
1146 prefix.c_str(), formatInternalUnits( aShape->GetEllipseCenter(), parentFP ).c_str(),
1147 formatInternalUnits( aShape->GetEllipseMajorRadius() ).c_str(),
1148 formatInternalUnits( aShape->GetEllipseMinorRadius() ).c_str(),
1151 EDA_UNIT_UTILS::FormatAngle( aShape->GetEllipseEndAngle() ).c_str() );
1152 break;
1153
1154 default:
1156 return;
1157 };
1158
1159 aShape->GetStroke().Format( m_out, pcbIUScale );
1160
1161 // The filled flag represents if a solid fill is present on circles, rectangles and polygons
1162 if( ( aShape->GetShape() == SHAPE_T::POLY ) || ( aShape->GetShape() == SHAPE_T::RECTANGLE )
1163 || ( aShape->GetShape() == SHAPE_T::CIRCLE ) || ( aShape->GetShape() == SHAPE_T::ELLIPSE ) )
1164 {
1165 switch( aShape->GetFillMode() )
1166 {
1167 case FILL_T::HATCH:
1168 m_out->Print( "(fill hatch)" );
1169 break;
1170
1172 m_out->Print( "(fill reverse_hatch)" );
1173 break;
1174
1176 m_out->Print( "(fill cross_hatch)" );
1177 break;
1178
1180 KICAD_FORMAT::FormatBool( m_out, "fill", true );
1181 break;
1182
1183 default:
1184 KICAD_FORMAT::FormatBool( m_out, "fill", false );
1185 break;
1186 }
1187 }
1188
1189 if( aShape->IsLocked() )
1190 KICAD_FORMAT::FormatBool( m_out, "locked", true );
1191
1192 if( aShape->GetLayerSet().count() > 1 )
1193 formatLayers( aShape->GetLayerSet(), false /* enumerate layers */ );
1194 else
1195 formatLayer( aShape->GetLayer() );
1196
1197 if( aShape->HasSolderMask()
1198 && aShape->GetLocalSolderMaskMargin().has_value()
1199 && IsExternalCopperLayer( aShape->GetLayer() ) )
1200 {
1201 m_out->Print( "(solder_mask_margin %s)",
1202 formatInternalUnits( aShape->GetLocalSolderMaskMargin().value() ).c_str() );
1203 }
1204
1205 if( !( m_ctl & CTL_OMIT_PAD_NETS ) && aShape->GetNetCode() > 0 )
1206 m_out->Print( "(net %s)", m_out->Quotew( aShape->GetNetname() ).c_str() );
1207
1209 m_out->Print( ")" );
1210}
1211
1212
1214{
1215 wxCHECK_RET( aBitmap != nullptr && m_out != nullptr, "" );
1216
1217 const REFERENCE_IMAGE& refImage = aBitmap->GetReferenceImage();
1218
1219 const wxImage* image = refImage.GetImage().GetImageData();
1220
1221 wxCHECK_RET( image != nullptr, "wxImage* is NULL" );
1222
1223 m_out->Print( "(image (at %s %s)",
1224 formatInternalUnits( aBitmap->GetPosition().x ).c_str(),
1225 formatInternalUnits( aBitmap->GetPosition().y ).c_str() );
1226
1227 formatLayer( aBitmap->GetLayer() );
1228
1229 if( refImage.GetImageScale() != 1.0 )
1230 m_out->Print( "%s", fmt::format("(scale {:g})", refImage.GetImageScale()).c_str() );
1231
1232 if( aBitmap->IsLocked() )
1233 KICAD_FORMAT::FormatBool( m_out, "locked", true );
1234
1235 wxMemoryOutputStream ostream;
1236 refImage.GetImage().SaveImageData( ostream );
1237
1238 KICAD_FORMAT::FormatStreamData( *m_out, *ostream.GetOutputStreamBuffer() );
1239
1241 m_out->Print( ")" ); // Closes image token.
1242}
1243
1244
1245void PCB_IO_KICAD_SEXPR::format( const PCB_POINT* aPoint ) const
1246{
1247 m_out->Print( "(point (at %s) (size %s)",
1248 formatInternalUnits( aPoint->GetPosition() ).c_str(),
1249 formatInternalUnits( aPoint->GetSize() ).c_str()
1250 );
1251
1252 formatLayer( aPoint->GetLayer() );
1253
1255 m_out->Print( ")" );
1256}
1257
1258
1259void PCB_IO_KICAD_SEXPR::format( const PCB_TARGET* aTarget ) const
1260{
1261 m_out->Print( "(target %s (at %s) (size %s)",
1262 ( aTarget->GetShape() ) ? "x" : "plus",
1263 formatInternalUnits( aTarget->GetPosition() ).c_str(),
1264 formatInternalUnits( aTarget->GetSize() ).c_str() );
1265
1266 if( aTarget->GetWidth() != 0 )
1267 m_out->Print( "(width %s)", formatInternalUnits( aTarget->GetWidth() ).c_str() );
1268
1269 formatLayer( aTarget->GetLayer() );
1271 m_out->Print( ")" );
1272}
1273
1274
1275void PCB_IO_KICAD_SEXPR::format( const FOOTPRINT* aFootprint ) const
1276{
1277 if( !( m_ctl & CTL_OMIT_INITIAL_COMMENTS ) )
1278 {
1279 const wxArrayString* initial_comments = aFootprint->GetInitialComments();
1280
1281 if( initial_comments )
1282 {
1283 for( unsigned i = 0; i < initial_comments->GetCount(); ++i )
1284 m_out->Print( "%s\n", TO_UTF8( (*initial_comments)[i] ) );
1285 }
1286 }
1287
1288 if( m_ctl & CTL_OMIT_LIBNAME )
1289 {
1290 m_out->Print( "(footprint %s",
1291 m_out->Quotes( aFootprint->GetFPID().GetLibItemName() ).c_str() );
1292 }
1293 else
1294 {
1295 m_out->Print( "(footprint %s",
1296 m_out->Quotes( aFootprint->GetFPID().Format() ).c_str() );
1297 }
1298
1300 {
1301 m_out->Print( "(version %d) (generator \"pcbnew\") (generator_version %s)",
1303 m_out->Quotew( GetMajorMinorVersion() ).c_str() );
1304 }
1305
1306 if( aFootprint->IsLocked() )
1307 KICAD_FORMAT::FormatBool( m_out, "locked", true );
1308
1309 if( aFootprint->IsPlaced() )
1310 KICAD_FORMAT::FormatBool( m_out, "placed", true );
1311
1312 formatLayer( aFootprint->GetLayer() );
1313
1314 if( !( m_ctl & CTL_OMIT_UUIDS ) )
1315 KICAD_FORMAT::FormatUuid( m_out, aFootprint->m_Uuid );
1316
1317 if( !( m_ctl & CTL_OMIT_AT ) )
1318 {
1319 m_out->Print( "(at %s %s)",
1320 formatInternalUnits( aFootprint->GetPosition() ).c_str(),
1321 aFootprint->GetOrientation().IsZero()
1322 ? ""
1323 : EDA_UNIT_UTILS::FormatAngle( aFootprint->GetOrientation() ).c_str() );
1324 }
1325
1326 if( !aFootprint->GetLibDescription().IsEmpty() )
1327 m_out->Print( "(descr %s)", m_out->Quotew( aFootprint->GetLibDescription() ).c_str() );
1328
1329 if( !aFootprint->GetKeywords().IsEmpty() )
1330 m_out->Print( "(tags %s)", m_out->Quotew( aFootprint->GetKeywords() ).c_str() );
1331
1332 for( const PCB_FIELD* field : aFootprint->GetFields() )
1333 {
1334 if( !field )
1335 continue;
1336
1337 m_out->Print( "(property %s %s",
1338 m_out->Quotew( field->GetCanonicalName() ).c_str(),
1339 m_out->Quotew( field->GetText() ).c_str() );
1340
1341 format( field );
1342
1343 m_out->Print( ")" );
1344 }
1345
1346 if( const COMPONENT_CLASS* compClass = aFootprint->GetStaticComponentClass() )
1347 {
1348 if( !compClass->IsEmpty() )
1349 {
1350 m_out->Print( "(component_classes" );
1351
1352 for( const COMPONENT_CLASS* constituent : compClass->GetConstituentClasses() )
1353 m_out->Print( "(class %s)", m_out->Quotew( constituent->GetName() ).c_str() );
1354
1355 m_out->Print( ")" );
1356 }
1357 }
1358
1359 if( !aFootprint->GetFilters().empty() )
1360 {
1361 m_out->Print( "(property ki_fp_filters %s)",
1362 m_out->Quotew( aFootprint->GetFilters() ).c_str() );
1363 }
1364
1365 if( !( m_ctl & CTL_OMIT_PATH ) && !aFootprint->GetPath().empty() )
1366 m_out->Print( "(path %s)", m_out->Quotew( aFootprint->GetPath().AsString() ).c_str() );
1367
1368 if( !aFootprint->GetSheetname().empty() )
1369 m_out->Print( "(sheetname %s)", m_out->Quotew( aFootprint->GetSheetname() ).c_str() );
1370
1371 if( !aFootprint->GetSheetfile().empty() )
1372 m_out->Print( "(sheetfile %s)", m_out->Quotew( aFootprint->GetSheetfile() ).c_str() );
1373
1374 // Emit unit info for gate swapping metadata (flat pin list form)
1375 if( !aFootprint->GetUnitInfo().empty() )
1376 {
1377 m_out->Print( "(units" );
1378
1379 for( const FOOTPRINT::FP_UNIT_INFO& u : aFootprint->GetUnitInfo() )
1380 {
1381 m_out->Print( "(unit (name %s)", m_out->Quotew( u.m_unitName ).c_str() );
1382 m_out->Print( "(pins" );
1383
1384 for( const wxString& n : u.m_pins )
1385 m_out->Print( " %s", m_out->Quotew( n ).c_str() );
1386
1387 m_out->Print( ")" ); // </pins>
1388 m_out->Print( ")" ); // </unit>
1389 }
1390
1391 m_out->Print( ")" ); // </units>
1392 }
1393
1394 if( aFootprint->GetLocalSolderMaskMargin().has_value() )
1395 {
1396 m_out->Print( "(solder_mask_margin %s)",
1397 formatInternalUnits( aFootprint->GetLocalSolderMaskMargin().value() ).c_str() );
1398 }
1399
1400 if( aFootprint->GetLocalSolderPasteMargin().has_value() )
1401 {
1402 m_out->Print( "(solder_paste_margin %s)",
1403 formatInternalUnits( aFootprint->GetLocalSolderPasteMargin().value() ).c_str() );
1404 }
1405
1406 if( aFootprint->GetLocalSolderPasteMarginRatio().has_value() )
1407 {
1408 m_out->Print( "(solder_paste_margin_ratio %s)",
1409 FormatDouble2Str( aFootprint->GetLocalSolderPasteMarginRatio().value() ).c_str() );
1410 }
1411
1412 if( aFootprint->GetLocalClearance().has_value() )
1413 {
1414 m_out->Print( "(clearance %s)",
1415 formatInternalUnits( aFootprint->GetLocalClearance().value() ).c_str() );
1416 }
1417
1419 {
1420 m_out->Print( "(zone_connect %d)",
1421 static_cast<int>( aFootprint->GetLocalZoneConnection() ) );
1422 }
1423
1424 // Attributes
1425 if( aFootprint->GetAttributes()
1426 || aFootprint->AllowMissingCourtyard()
1427 || aFootprint->AllowSolderMaskBridges() )
1428 {
1429 m_out->Print( "(attr" );
1430
1431 if( aFootprint->GetAttributes() & FP_SMD )
1432 m_out->Print( " smd" );
1433
1434 if( aFootprint->GetAttributes() & FP_THROUGH_HOLE )
1435 m_out->Print( " through_hole" );
1436
1437 if( aFootprint->GetAttributes() & FP_BOARD_ONLY )
1438 m_out->Print( " board_only" );
1439
1440 if( aFootprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES )
1441 m_out->Print( " exclude_from_pos_files" );
1442
1443 if( aFootprint->GetAttributes() & FP_EXCLUDE_FROM_BOM )
1444 m_out->Print( " exclude_from_bom" );
1445
1446 if( aFootprint->AllowMissingCourtyard() )
1447 m_out->Print( " allow_missing_courtyard" );
1448
1449 if( aFootprint->GetAttributes() & FP_DNP )
1450 m_out->Print( " dnp" );
1451
1452 if( aFootprint->AllowSolderMaskBridges() )
1453 m_out->Print( " allow_soldermask_bridges" );
1454
1455 m_out->Print( ")" );
1456 }
1457
1458 // Expand inner layers is the default stackup mode
1460 {
1461 m_out->Print( "(stackup" );
1462
1463 const LSET& fpLset = aFootprint->GetStackupLayers();
1464 for( PCB_LAYER_ID layer : fpLset.Seq() )
1465 {
1466 wxString canonicalName( LSET::Name( layer ) );
1467 m_out->Print( "(layer %s)", m_out->Quotew( canonicalName ).c_str() );
1468 }
1469
1470 m_out->Print( ")" );
1471 }
1472
1473 if( aFootprint->GetPrivateLayers().any() )
1474 {
1475 m_out->Print( "(private_layers" );
1476
1477 for( PCB_LAYER_ID layer : aFootprint->GetPrivateLayers().Seq() )
1478 {
1479 wxString canonicalName( LSET::Name( layer ) );
1480 m_out->Print( " %s", m_out->Quotew( canonicalName ).c_str() );
1481 }
1482
1483 m_out->Print( ")" );
1484 }
1485
1486 if( aFootprint->IsNetTie() )
1487 {
1488 m_out->Print( "(net_tie_pad_groups" );
1489
1490 for( const wxString& group : aFootprint->GetNetTiePadGroups() )
1491 m_out->Print( " %s", m_out->Quotew( group ).c_str() );
1492
1493 m_out->Print( ")" );
1494 }
1495
1496 KICAD_FORMAT::FormatBool( m_out, "duplicate_pad_numbers_are_jumpers",
1497 aFootprint->GetDuplicatePadNumbersAreJumpers() );
1498
1499 const std::vector<std::set<wxString>>& jumperGroups = aFootprint->JumperPadGroups();
1500
1501 if( !jumperGroups.empty() )
1502 {
1503 m_out->Print( "(jumper_pad_groups" );
1504
1505 for( const std::set<wxString>& group : jumperGroups )
1506 {
1507 m_out->Print( "(" );
1508
1509 for( const wxString& padName : group )
1510 m_out->Print( "%s ", m_out->Quotew( padName ).c_str() );
1511
1512 m_out->Print( ")" );
1513 }
1514
1515 m_out->Print( ")" );
1516 }
1517
1518 Format( &aFootprint->Reference() );
1519 Format( &aFootprint->Value() );
1520
1521 std::set<PAD*, FOOTPRINT::cmp_pads> sorted_pads( aFootprint->Pads().begin(),
1522 aFootprint->Pads().end() );
1523 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> sorted_drawings(
1524 aFootprint->GraphicalItems().begin(),
1525 aFootprint->GraphicalItems().end() );
1526 std::set<PCB_POINT*, PCB_POINT::cmp_points> sorted_points(
1527 aFootprint->Points().begin(),
1528 aFootprint->Points().end() );
1529 std::set<ZONE*, FOOTPRINT::cmp_zones> sorted_zones( aFootprint->Zones().begin(),
1530 aFootprint->Zones().end() );
1531 std::set<BOARD_ITEM*, PCB_GROUP::ptr_cmp> sorted_groups( aFootprint->Groups().begin(),
1532 aFootprint->Groups().end() );
1533
1534 // Save drawing elements.
1535
1536 for( BOARD_ITEM* gr : sorted_drawings )
1537 Format( gr );
1538
1539 for( PCB_POINT* point : sorted_points )
1540 Format( point );
1541
1542 // Save pads.
1543 for( PAD* pad : sorted_pads )
1544 Format( pad );
1545
1546 // Save zones.
1547 for( BOARD_ITEM* zone : sorted_zones )
1548 Format( zone );
1549
1550 // Save groups.
1551 for( BOARD_ITEM* group : sorted_groups )
1552 Format( group );
1553
1554 // Save variants.
1555 const bool baseDnp = aFootprint->IsDNP();
1556 const bool baseExcludedFromBOM = aFootprint->IsExcludedFromBOM();
1557 const bool baseExcludedFromPosFiles = aFootprint->IsExcludedFromPosFiles();
1558
1559 for( const auto& [variantName, variant] : aFootprint->GetVariants() )
1560 {
1561 m_out->Print( "(variant (name %s)", m_out->Quotew( variantName ).c_str() );
1562
1563 if( variant.GetDNP() != baseDnp )
1564 KICAD_FORMAT::FormatBool( m_out, "dnp", variant.GetDNP() );
1565
1566 if( variant.GetExcludedFromBOM() != baseExcludedFromBOM )
1567 KICAD_FORMAT::FormatBool( m_out, "exclude_from_bom", variant.GetExcludedFromBOM() );
1568
1569 if( variant.GetExcludedFromPosFiles() != baseExcludedFromPosFiles )
1570 {
1571 KICAD_FORMAT::FormatBool( m_out, "exclude_from_pos_files",
1572 variant.GetExcludedFromPosFiles() );
1573 }
1574
1575 for( const auto& [fieldName, fieldValue] : variant.GetFields() )
1576 {
1577 const PCB_FIELD* baseField = aFootprint->GetField( fieldName );
1578 const wxString baseValue = baseField ? baseField->GetText() : wxString();
1579
1580 if( fieldValue == baseValue )
1581 continue;
1582
1583 m_out->Print( "(field (name %s) (value %s))",
1584 m_out->Quotew( fieldName ).c_str(),
1585 m_out->Quotew( fieldValue ).c_str() );
1586 }
1587
1588 m_out->Print( ")" );
1589 }
1590
1591 KICAD_FORMAT::FormatBool( m_out, "embedded_fonts",
1592 aFootprint->GetEmbeddedFiles()->GetAreFontsEmbedded() );
1593
1594 if( !aFootprint->GetEmbeddedFiles()->IsEmpty() )
1595 aFootprint->WriteEmbeddedFiles( *m_out, !( m_ctl & CTL_FOR_BOARD ) );
1596
1597 // Save extruded 3D body info.
1598 if( const EXTRUDED_3D_BODY* body = aFootprint->GetExtrudedBody(); body && body->m_height > 0 )
1599 {
1600 m_out->Print( "(model" );
1601 m_out->Print( "(type extruded)" );
1602 KICAD_FORMAT::FormatBool( m_out, "hide", !body->m_show );
1603 m_out->Print( "(overall_height %s)", formatInternalUnits( body->m_height ).c_str() );
1604 m_out->Print( "(body_pcb_gap %s)", formatInternalUnits( body->m_standoff ).c_str() );
1605
1606 if( body->m_layer == UNSELECTED_LAYER )
1607 m_out->Print( "(layer pad_bbox)" );
1608 else if( body->m_layer != UNDEFINED_LAYER )
1609 m_out->Print( "(layer %s)", m_out->Quotew( LSET::Name( body->m_layer ) ).c_str() );
1610 else
1611 m_out->Print( "(layer auto)" );
1612
1613 {
1614 static const char* matNames[] = { "plastic", "matte", "metal", "copper" };
1615 m_out->Print( "(material %s)", matNames[static_cast<int>( body->m_material )] );
1616 }
1617
1618 if( body->m_color != KIGFX::COLOR4D::UNSPECIFIED )
1619 {
1620 m_out->Print( "(color %s %s %s %s)", FormatDouble2Str( body->m_color.r ).c_str(),
1621 FormatDouble2Str( body->m_color.g ).c_str(), FormatDouble2Str( body->m_color.b ).c_str(),
1622 FormatDouble2Str( body->m_color.a ).c_str() );
1623 }
1624 else
1625 {
1626 m_out->Print( "(color unspecified)" );
1627 }
1628
1629 m_out->Print( "(offset (xyz %s %s %s))", FormatDouble2Str( body->m_offset.x ).c_str(),
1630 FormatDouble2Str( body->m_offset.y ).c_str(), FormatDouble2Str( body->m_offset.z ).c_str() );
1631
1632 m_out->Print( "(scale (xyz %s %s %s))", FormatDouble2Str( body->m_scale.x ).c_str(),
1633 FormatDouble2Str( body->m_scale.y ).c_str(), FormatDouble2Str( body->m_scale.z ).c_str() );
1634
1635 m_out->Print( "(rotate (xyz %s %s %s))", FormatDouble2Str( body->m_rotation.x ).c_str(),
1636 FormatDouble2Str( body->m_rotation.y ).c_str(), FormatDouble2Str( body->m_rotation.z ).c_str() );
1637
1638 m_out->Print( ")" );
1639 }
1640
1641 // Save 3D info.
1642 auto bs3D = aFootprint->Models().begin();
1643 auto es3D = aFootprint->Models().end();
1644
1645 while( bs3D != es3D )
1646 {
1647 if( !bs3D->m_Filename.IsEmpty() )
1648 {
1649 m_out->Print( "(model %s", m_out->Quotew( bs3D->m_Filename ).c_str() );
1650
1651 if( !bs3D->m_Show )
1652 KICAD_FORMAT::FormatBool( m_out, "hide", !bs3D->m_Show );
1653
1654 if( bs3D->m_Opacity != 1.0 )
1655 m_out->Print( "%s", fmt::format("(opacity {:.4f})", bs3D->m_Opacity).c_str() );
1656
1657 m_out->Print( "(offset (xyz %s %s %s))",
1658 FormatDouble2Str( bs3D->m_Offset.x ).c_str(),
1659 FormatDouble2Str( bs3D->m_Offset.y ).c_str(),
1660 FormatDouble2Str( bs3D->m_Offset.z ).c_str() );
1661
1662 m_out->Print( "(scale (xyz %s %s %s))",
1663 FormatDouble2Str( bs3D->m_Scale.x ).c_str(),
1664 FormatDouble2Str( bs3D->m_Scale.y ).c_str(),
1665 FormatDouble2Str( bs3D->m_Scale.z ).c_str() );
1666
1667 m_out->Print( "(rotate (xyz %s %s %s))",
1668 FormatDouble2Str( bs3D->m_Rotation.x ).c_str(),
1669 FormatDouble2Str( bs3D->m_Rotation.y ).c_str(),
1670 FormatDouble2Str( bs3D->m_Rotation.z ).c_str() );
1671
1672 m_out->Print( ")" );
1673 }
1674
1675 ++bs3D;
1676 }
1677
1678 m_out->Print( ")" );
1679}
1680
1681
1682void PCB_IO_KICAD_SEXPR::formatLayers( LSET aLayerMask, bool aEnumerateLayers, bool aIsZone ) const
1683{
1684 static const LSET cu_all( LSET::AllCuMask() );
1685 static const LSET fr_bk( { B_Cu, F_Cu } );
1686 static const LSET adhes( { B_Adhes, F_Adhes } );
1687 static const LSET paste( { B_Paste, F_Paste } );
1688 static const LSET silks( { B_SilkS, F_SilkS } );
1689 static const LSET mask( { B_Mask, F_Mask } );
1690 static const LSET crt_yd( { B_CrtYd, F_CrtYd } );
1691 static const LSET fab( { B_Fab, F_Fab } );
1692
1693 LSET cu_board_mask = LSET::AllCuMask( m_board ? m_board->GetCopperLayerCount() : MAX_CU_LAYERS );
1694
1695 std::string output;
1696
1697 if( !aEnumerateLayers )
1698 {
1699 // If all copper layers present on the board are enabled, then output the wildcard
1700 if( ( aLayerMask & cu_board_mask ) == cu_board_mask )
1701 {
1702 output += ' ' + m_out->Quotew( "*.Cu" );
1703
1704 // Clear all copper bits because pads might have internal layers that aren't part of the
1705 // board enabled, and we don't want to output those in the layers listing if we already
1706 // output the wildcard.
1707 aLayerMask &= ~cu_all;
1708 }
1709 else if( ( aLayerMask & cu_board_mask ) == fr_bk )
1710 {
1711 if( aIsZone )
1712 output += ' ' + m_out->Quotew( "F&B.Cu" );
1713 else
1714 output += ' ' + m_out->Quotew( "*.Cu" );
1715
1716 aLayerMask &= ~fr_bk;
1717 }
1718
1719 if( ( aLayerMask & adhes ) == adhes )
1720 {
1721 output += ' ' + m_out->Quotew( "*.Adhes" );
1722 aLayerMask &= ~adhes;
1723 }
1724
1725 if( ( aLayerMask & paste ) == paste )
1726 {
1727 output += ' ' + m_out->Quotew( "*.Paste" );
1728 aLayerMask &= ~paste;
1729 }
1730
1731 if( ( aLayerMask & silks ) == silks )
1732 {
1733 output += ' ' + m_out->Quotew( "*.SilkS" );
1734 aLayerMask &= ~silks;
1735 }
1736
1737 if( ( aLayerMask & mask ) == mask )
1738 {
1739 output += ' ' + m_out->Quotew( "*.Mask" );
1740 aLayerMask &= ~mask;
1741 }
1742
1743 if( ( aLayerMask & crt_yd ) == crt_yd )
1744 {
1745 output += ' ' + m_out->Quotew( "*.CrtYd" );
1746 aLayerMask &= ~crt_yd;
1747 }
1748
1749 if( ( aLayerMask & fab ) == fab )
1750 {
1751 output += ' ' + m_out->Quotew( "*.Fab" );
1752 aLayerMask &= ~fab;
1753 }
1754 }
1755
1756 // output any individual layers not handled in wildcard combos above
1757 for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
1758 {
1759 if( aLayerMask[layer] )
1760 output += ' ' + m_out->Quotew( LSET::Name( PCB_LAYER_ID( layer ) ) );
1761 }
1762
1763 m_out->Print( "(layers %s)", output.c_str() );
1764}
1765
1766
1767void PCB_IO_KICAD_SEXPR::format( const PAD* aPad ) const
1768{
1769 const BOARD* board = aPad->GetBoard();
1770
1771 auto shapeName =
1772 [&]( PCB_LAYER_ID aLayer )
1773 {
1774 switch( aPad->GetShape( aLayer ) )
1775 {
1776 case PAD_SHAPE::CIRCLE: return "circle";
1777 case PAD_SHAPE::RECTANGLE: return "rect";
1778 case PAD_SHAPE::OVAL: return "oval";
1779 case PAD_SHAPE::TRAPEZOID: return "trapezoid";
1781 case PAD_SHAPE::ROUNDRECT: return "roundrect";
1782 case PAD_SHAPE::CUSTOM: return "custom";
1783
1784 default:
1785 THROW_IO_ERROR( wxString::Format( _( "unknown pad type: %d"),
1786 aPad->GetShape( aLayer ) ) );
1787 }
1788 };
1789
1790 const char* type;
1791
1792 switch( aPad->GetAttribute() )
1793 {
1794 case PAD_ATTRIB::PTH: type = "thru_hole"; break;
1795 case PAD_ATTRIB::SMD: type = "smd"; break;
1796 case PAD_ATTRIB::CONN: type = "connect"; break;
1797 case PAD_ATTRIB::NPTH: type = "np_thru_hole"; break;
1798
1799 default:
1800 THROW_IO_ERROR( wxString::Format( wxT( "unknown pad attribute: %d" ),
1801 aPad->GetAttribute() ) );
1802 }
1803
1804 const char* property = nullptr;
1805
1806 switch( aPad->GetProperty() )
1807 {
1808 case PAD_PROP::NONE: break; // could be "none"
1809 case PAD_PROP::BGA: property = "pad_prop_bga"; break;
1810 case PAD_PROP::FIDUCIAL_GLBL: property = "pad_prop_fiducial_glob"; break;
1811 case PAD_PROP::FIDUCIAL_LOCAL: property = "pad_prop_fiducial_loc"; break;
1812 case PAD_PROP::TESTPOINT: property = "pad_prop_testpoint"; break;
1813 case PAD_PROP::HEATSINK: property = "pad_prop_heatsink"; break;
1814 case PAD_PROP::CASTELLATED: property = "pad_prop_castellated"; break;
1815 case PAD_PROP::MECHANICAL: property = "pad_prop_mechanical"; break;
1816 case PAD_PROP::PRESSFIT: property = "pad_prop_pressfit"; break;
1817
1818 default:
1819 THROW_IO_ERROR( wxString::Format( wxT( "unknown pad property: %d" ),
1820 aPad->GetProperty() ) );
1821 }
1822
1823 m_out->Print( "(pad %s %s %s",
1824 m_out->Quotew( aPad->GetNumber() ).c_str(),
1825 type,
1826 shapeName( PADSTACK::ALL_LAYERS ) );
1827
1828 m_out->Print( "(at %s %s)",
1829 formatInternalUnits( aPad->GetFPRelativePosition() ).c_str(),
1830 aPad->GetOrientation().IsZero()
1831 ? ""
1832 : EDA_UNIT_UTILS::FormatAngle( aPad->GetOrientation() ).c_str() );
1833
1834 m_out->Print( "(size %s)", formatInternalUnits( aPad->GetSize( PADSTACK::ALL_LAYERS ) ).c_str() );
1835
1836 if( aPad->GetDelta( PADSTACK::ALL_LAYERS ).x != 0
1837 || aPad->GetDelta( PADSTACK::ALL_LAYERS ).y != 0 )
1838 {
1839 m_out->Print( "(rect_delta %s)",
1841 }
1842
1843 const VECTOR2I& drill = aPad->GetDrillSize();
1844 VECTOR2I shapeoffset = aPad->GetOffset( PADSTACK::ALL_LAYERS );
1845 bool forceShapeOffsetOutput = false;
1846
1848 [&]( PCB_LAYER_ID layer )
1849 {
1850 if( aPad->GetOffset( layer ) != shapeoffset )
1851 forceShapeOffsetOutput = true;
1852 } );
1853
1854 if( drill.x > 0 || drill.y > 0 || shapeoffset.x != 0 || shapeoffset.y != 0 || forceShapeOffsetOutput )
1855 {
1856 m_out->Print( "(drill" );
1857
1858 if( aPad->GetDrillShape() == PAD_DRILL_SHAPE::OBLONG )
1859 m_out->Print( " oval" );
1860
1861 if( drill.x > 0 )
1862 m_out->Print( " %s", formatInternalUnits( drill.x ).c_str() );
1863
1864 if( drill.y > 0 && drill.x != drill.y )
1865 m_out->Print( " %s", formatInternalUnits( drill.y ).c_str() );
1866
1867 // NOTE: Shape offest is a property of the copper shape, not of the drill, but this was put
1868 // in the file format under the drill section. So, it is left here to minimize file format
1869 // changes, but note that the other padstack layers (if present) will have an offset stored
1870 // separately.
1871 if( shapeoffset.x != 0 || shapeoffset.y != 0 || forceShapeOffsetOutput )
1872 m_out->Print( "(offset %s)", formatInternalUnits( aPad->GetOffset( PADSTACK::ALL_LAYERS ) ).c_str() );
1873
1874 m_out->Print( ")" );
1875 }
1876
1877 if( aPad->Padstack().SecondaryDrill().size.x > 0 )
1878 {
1879 m_out->Print( "(backdrill (size %s) (layers %s %s))",
1880 formatInternalUnits( aPad->Padstack().SecondaryDrill().size.x ).c_str(),
1881 m_out->Quotew( LSET::Name( aPad->Padstack().SecondaryDrill().start ) ).c_str(),
1882 m_out->Quotew( LSET::Name( aPad->Padstack().SecondaryDrill().end ) ).c_str() );
1883 }
1884
1885 if( aPad->Padstack().TertiaryDrill().size.x > 0 )
1886 {
1887 m_out->Print( "(tertiary_drill (size %s) (layers %s %s))",
1888 formatInternalUnits( aPad->Padstack().TertiaryDrill().size.x ).c_str(),
1889 m_out->Quotew( LSET::Name( aPad->Padstack().TertiaryDrill().start ) ).c_str(),
1890 m_out->Quotew( LSET::Name( aPad->Padstack().TertiaryDrill().end ) ).c_str() );
1891 }
1892
1893 auto formatPostMachining =
1894 [&]( const char* aName, const PADSTACK::POST_MACHINING_PROPS& aProps )
1895 {
1896 if( !aProps.mode.has_value() || aProps.mode == PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED )
1897 return;
1898
1899 m_out->Print( "(%s %s",
1900 aName,
1901 aProps.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ? "counterbore"
1902 : "countersink" );
1903
1904 if( aProps.size > 0 )
1905 m_out->Print( " (size %s)", formatInternalUnits( aProps.size ).c_str() );
1906
1907 if( aProps.depth > 0 )
1908 m_out->Print( " (depth %s)", formatInternalUnits( aProps.depth ).c_str() );
1909
1910 if( aProps.angle > 0 )
1911 m_out->Print( " (angle %s)", FormatDouble2Str( aProps.angle / 10.0 ).c_str() );
1912
1913 m_out->Print( ")" );
1914 };
1915
1916 formatPostMachining( "front_post_machining", aPad->Padstack().FrontPostMachining() );
1917 formatPostMachining( "back_post_machining", aPad->Padstack().BackPostMachining() );
1918
1919 // Add pad property, if exists.
1920 if( property )
1921 m_out->Print( "(property %s)", property );
1922
1923 formatLayers( aPad->GetLayerSet(), false /* enumerate layers */ );
1924
1925 if( aPad->GetAttribute() == PAD_ATTRIB::PTH )
1926 {
1927 KICAD_FORMAT::FormatBool( m_out, "remove_unused_layers", aPad->GetRemoveUnconnected() );
1928
1929 if( aPad->GetRemoveUnconnected() )
1930 {
1931 KICAD_FORMAT::FormatBool( m_out, "keep_end_layers", aPad->GetKeepTopBottom() );
1932
1933 if( board ) // Will be nullptr in footprint library
1934 {
1935 m_out->Print( "(zone_layer_connections" );
1936
1937 for( PCB_LAYER_ID layer : board->GetEnabledLayers().CuStack() )
1938 {
1939 if( aPad->GetZoneLayerOverride( layer ) == ZLO_FORCE_FLASHED )
1940 m_out->Print( " %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
1941 }
1942
1943 m_out->Print( ")" );
1944 }
1945 }
1946 }
1947
1948 auto formatCornerProperties =
1949 [&]( PCB_LAYER_ID aLayer )
1950 {
1951 // Output the radius ratio for rounded and chamfered rect pads
1952 if( aPad->GetShape( aLayer ) == PAD_SHAPE::ROUNDRECT
1953 || aPad->GetShape( aLayer ) == PAD_SHAPE::CHAMFERED_RECT)
1954 {
1955 m_out->Print( "(roundrect_rratio %s)",
1956 FormatDouble2Str( aPad->GetRoundRectRadiusRatio( aLayer ) ).c_str() );
1957 }
1958
1959 // Output the chamfer corners for chamfered rect pads
1960 if( aPad->GetShape( aLayer ) == PAD_SHAPE::CHAMFERED_RECT)
1961 {
1962 m_out->Print( "(chamfer_ratio %s)",
1963 FormatDouble2Str( aPad->GetChamferRectRatio( aLayer ) ).c_str() );
1964
1965 m_out->Print( "(chamfer" );
1966
1967 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_TOP_LEFT ) )
1968 m_out->Print( " top_left" );
1969
1970 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_TOP_RIGHT ) )
1971 m_out->Print( " top_right" );
1972
1973 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_BOTTOM_LEFT ) )
1974 m_out->Print( " bottom_left" );
1975
1976 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_BOTTOM_RIGHT ) )
1977 m_out->Print( " bottom_right" );
1978
1979 m_out->Print( ")" );
1980 }
1981
1982 };
1983
1984 // For normal padstacks, this is the one and only set of properties. For complex ones, this
1985 // will represent the front layer properties, and other layers will be formatted below
1986 formatCornerProperties( PADSTACK::ALL_LAYERS );
1987
1988 // Unconnected pad is default net so don't save it.
1989 if( !( m_ctl & CTL_OMIT_PAD_NETS ) && aPad->GetNetCode() > 0 )
1990 m_out->Print( "(net %s)", m_out->Quotew( aPad->GetNetname() ).c_str() );
1991
1992 // Pin functions and types are closely related to nets, so if CTL_OMIT_NETS is set, omit
1993 // them as well (for instance when saved from library editor).
1994 if( !( m_ctl & CTL_OMIT_PAD_NETS ) )
1995 {
1996 if( !aPad->GetPinFunction().IsEmpty() )
1997 m_out->Print( "(pinfunction %s)", m_out->Quotew( aPad->GetPinFunction() ).c_str() );
1998
1999 if( !aPad->GetPinType().IsEmpty() )
2000 m_out->Print( "(pintype %s)", m_out->Quotew( aPad->GetPinType() ).c_str() );
2001 }
2002
2003 if( aPad->GetPadToDieLength() != 0 )
2004 {
2005 m_out->Print( "(die_length %s)",
2006 formatInternalUnits( aPad->GetPadToDieLength() ).c_str() );
2007 }
2008
2009 if( aPad->GetPadToDieDelay() != 0 )
2010 {
2011 m_out->Print( "(die_delay %s)",
2013 }
2014
2015 if( aPad->GetLocalSolderMaskMargin().has_value() )
2016 {
2017 m_out->Print( "(solder_mask_margin %s)",
2018 formatInternalUnits( aPad->GetLocalSolderMaskMargin().value() ).c_str() );
2019 }
2020
2021 if( aPad->GetLocalSolderPasteMargin().has_value() )
2022 {
2023 m_out->Print( "(solder_paste_margin %s)",
2024 formatInternalUnits( aPad->GetLocalSolderPasteMargin().value() ).c_str() );
2025 }
2026
2027 if( aPad->GetLocalSolderPasteMarginRatio().has_value() )
2028 {
2029 m_out->Print( "(solder_paste_margin_ratio %s)",
2030 FormatDouble2Str( aPad->GetLocalSolderPasteMarginRatio().value() ).c_str() );
2031 }
2032
2033 if( aPad->GetLocalClearance().has_value() )
2034 {
2035 m_out->Print( "(clearance %s)",
2036 formatInternalUnits( aPad->GetLocalClearance().value() ).c_str() );
2037 }
2038
2040 {
2041 m_out->Print( "(zone_connect %d)",
2042 static_cast<int>( aPad->GetLocalZoneConnection() ) );
2043 }
2044
2045 if( aPad->GetLocalThermalSpokeWidthOverride().has_value() )
2046 {
2047 m_out->Print( "(thermal_bridge_width %s)",
2048 formatInternalUnits( aPad->GetLocalThermalSpokeWidthOverride().value() ).c_str() );
2049 }
2050
2051 EDA_ANGLE defaultThermalSpokeAngle = ANGLE_90;
2052
2056 {
2057 defaultThermalSpokeAngle = ANGLE_45;
2058 }
2059
2060 if( aPad->GetThermalSpokeAngle() != defaultThermalSpokeAngle )
2061 {
2062 m_out->Print( "(thermal_bridge_angle %s)",
2064 }
2065
2066 if( aPad->GetLocalThermalGapOverride().has_value() )
2067 {
2068 m_out->Print( "(thermal_gap %s)",
2069 formatInternalUnits( aPad->GetLocalThermalGapOverride().value() ).c_str() );
2070 }
2071
2072 auto anchorShape =
2073 [&]( PCB_LAYER_ID aLayer )
2074 {
2075 switch( aPad->GetAnchorPadShape( aLayer ) )
2076 {
2077 case PAD_SHAPE::RECTANGLE: return "rect";
2078 default:
2079 case PAD_SHAPE::CIRCLE: return "circle";
2080 }
2081 };
2082
2083 auto formatPrimitives =
2084 [&]( PCB_LAYER_ID aLayer )
2085 {
2086 m_out->Print( "(primitives" );
2087
2088 // Output all basic shapes
2089 for( const std::shared_ptr<PCB_SHAPE>& primitive : aPad->GetPrimitives( aLayer ) )
2090 {
2091 switch( primitive->GetShape() )
2092 {
2093 case SHAPE_T::SEGMENT:
2094 if( primitive->IsProxyItem() )
2095 {
2096 m_out->Print( "(gr_vector (start %s) (end %s)",
2097 formatInternalUnits( primitive->GetStart() ).c_str(),
2098 formatInternalUnits( primitive->GetEnd() ).c_str() );
2099 }
2100 else
2101 {
2102 m_out->Print( "(gr_line (start %s) (end %s)",
2103 formatInternalUnits( primitive->GetStart() ).c_str(),
2104 formatInternalUnits( primitive->GetEnd() ).c_str() );
2105 }
2106 break;
2107
2108 case SHAPE_T::RECTANGLE:
2109 if( primitive->IsProxyItem() )
2110 {
2111 m_out->Print( "(gr_bbox (start %s) (end %s)",
2112 formatInternalUnits( primitive->GetStart() ).c_str(),
2113 formatInternalUnits( primitive->GetEnd() ).c_str() );
2114 }
2115 else
2116 {
2117 m_out->Print( "(gr_rect (start %s) (end %s)",
2118 formatInternalUnits( primitive->GetStart() ).c_str(),
2119 formatInternalUnits( primitive->GetEnd() ).c_str() );
2120
2121 if( primitive->GetCornerRadius() > 0 )
2122 {
2123 m_out->Print( " (radius %s)",
2124 formatInternalUnits( primitive->GetCornerRadius() ).c_str() );
2125 }
2126 }
2127 break;
2128
2129 case SHAPE_T::ARC:
2130 m_out->Print( "(gr_arc (start %s) (mid %s) (end %s)",
2131 formatInternalUnits( primitive->GetStart() ).c_str(),
2132 formatInternalUnits( primitive->GetArcMid() ).c_str(),
2133 formatInternalUnits( primitive->GetEnd() ).c_str() );
2134 break;
2135
2136 case SHAPE_T::CIRCLE:
2137 m_out->Print( "(gr_circle (center %s) (end %s)",
2138 formatInternalUnits( primitive->GetStart() ).c_str(),
2139 formatInternalUnits( primitive->GetEnd() ).c_str() );
2140 break;
2141
2142 case SHAPE_T::BEZIER:
2143 m_out->Print( "(gr_curve (pts (xy %s) (xy %s) (xy %s) (xy %s))",
2144 formatInternalUnits( primitive->GetStart() ).c_str(),
2145 formatInternalUnits( primitive->GetBezierC1() ).c_str(),
2146 formatInternalUnits( primitive->GetBezierC2() ).c_str(),
2147 formatInternalUnits( primitive->GetEnd() ).c_str() );
2148 break;
2149
2150 case SHAPE_T::POLY:
2151 if( primitive->IsPolyShapeValid() )
2152 {
2153 const SHAPE_POLY_SET& poly = primitive->GetPolyShape();
2154 const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
2155
2156 m_out->Print( "(gr_poly" );
2157 formatPolyPts( outline );
2158 }
2159 break;
2160
2161 default:
2162 break;
2163 }
2164
2165 if( !primitive->IsProxyItem() )
2166 m_out->Print( "(width %s)", formatInternalUnits( primitive->GetWidth() ).c_str() );
2167
2168 // The filled flag represents if a solid fill is present on circles,
2169 // rectangles and polygons
2170 if( ( primitive->GetShape() == SHAPE_T::POLY )
2171 || ( primitive->GetShape() == SHAPE_T::RECTANGLE )
2172 || ( primitive->GetShape() == SHAPE_T::CIRCLE ) )
2173 {
2174 KICAD_FORMAT::FormatBool( m_out, "fill", primitive->IsSolidFill() );
2175 }
2176
2177 m_out->Print( ")" );
2178 }
2179
2180 m_out->Print( ")" ); // end of (primitives
2181 };
2182
2184 {
2185 m_out->Print( "(options" );
2186
2188 m_out->Print( "(clearance convexhull)" );
2189 else
2190 m_out->Print( "(clearance outline)" );
2191
2192 // Output the anchor pad shape (circle/rect)
2193 m_out->Print( "(anchor %s)", anchorShape( PADSTACK::ALL_LAYERS ) );
2194
2195 m_out->Print( ")"); // end of (options ...
2196
2197 // Output graphic primitive of the pad shape
2198 formatPrimitives( PADSTACK::ALL_LAYERS );
2199 }
2200
2203
2204 if( aPad->Padstack().FrontOuterLayers().has_solder_mask.has_value()
2205 || aPad->Padstack().BackOuterLayers().has_solder_mask.has_value() )
2206 {
2207 m_out->Print( 0, " (tenting " );
2212 m_out->Print( 0, ")" );
2213 }
2214
2216
2217 // TODO: Refactor so that we call formatPadLayer( ALL_LAYERS ) above instead of redundant code
2218 auto formatPadLayer =
2219 [&]( PCB_LAYER_ID aLayer )
2220 {
2221 const PADSTACK& padstack = aPad->Padstack();
2222
2223 m_out->Print( "(shape %s)", shapeName( aLayer ) );
2224 m_out->Print( "(size %s)", formatInternalUnits( aPad->GetSize( aLayer ) ).c_str() );
2225
2226 const VECTOR2I& delta = aPad->GetDelta( aLayer );
2227
2228 if( delta.x != 0 || delta.y != 0 )
2229 m_out->Print( "(rect_delta %s)", formatInternalUnits( delta ).c_str() );
2230
2231 shapeoffset = aPad->GetOffset( aLayer );
2232
2233 if( shapeoffset.x != 0 || shapeoffset.y != 0 )
2234 m_out->Print( "(offset %s)", formatInternalUnits( shapeoffset ).c_str() );
2235
2236 formatCornerProperties( aLayer );
2237
2238 if( aPad->GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
2239 {
2240 m_out->Print( "(options" );
2241
2242 // Output the anchor pad shape (circle/rect)
2243 m_out->Print( "(anchor %s)", anchorShape( aLayer ) );
2244
2245 m_out->Print( ")" ); // end of (options ...
2246
2247 // Output graphic primitive of the pad shape
2248 formatPrimitives( aLayer );
2249 }
2250
2251 EDA_ANGLE defaultLayerAngle = ANGLE_90;
2252
2253 if( aPad->GetShape( aLayer ) == PAD_SHAPE::CIRCLE ||
2254 ( aPad->GetShape( aLayer ) == PAD_SHAPE::CUSTOM
2255 && aPad->GetAnchorPadShape( aLayer ) == PAD_SHAPE::CIRCLE ) )
2256 {
2257 defaultLayerAngle = ANGLE_45;
2258 }
2259
2260 EDA_ANGLE layerSpokeAngle = padstack.ThermalSpokeAngle( aLayer );
2261
2262 if( layerSpokeAngle != defaultLayerAngle )
2263 {
2264 m_out->Print( "(thermal_bridge_angle %s)",
2265 EDA_UNIT_UTILS::FormatAngle( layerSpokeAngle ).c_str() );
2266 }
2267
2268 if( padstack.ThermalGap( aLayer ).has_value() )
2269 {
2270 m_out->Print( "(thermal_gap %s)",
2271 formatInternalUnits( *padstack.ThermalGap( aLayer ) ).c_str() );
2272 }
2273
2274 if( padstack.ThermalSpokeWidth( aLayer ).has_value() )
2275 {
2276 m_out->Print( "(thermal_bridge_width %s)",
2277 formatInternalUnits( *padstack.ThermalSpokeWidth( aLayer ) ).c_str() );
2278 }
2279
2280 if( padstack.Clearance( aLayer ).has_value() )
2281 {
2282 m_out->Print( "(clearance %s)",
2283 formatInternalUnits( *padstack.Clearance( aLayer ) ).c_str() );
2284 }
2285
2286 if( padstack.ZoneConnection( aLayer ).has_value() )
2287 {
2288 m_out->Print( "(zone_connect %d)",
2289 static_cast<int>( *padstack.ZoneConnection( aLayer ) ) );
2290 }
2291 };
2292
2293
2294 if( aPad->Padstack().Mode() != PADSTACK::MODE::NORMAL )
2295 {
2297 {
2298 m_out->Print( "(padstack (mode front_inner_back)" );
2299
2300 m_out->Print( "(layer \"Inner\"" );
2301 formatPadLayer( PADSTACK::INNER_LAYERS );
2302 m_out->Print( ")" );
2303 m_out->Print( "(layer \"B.Cu\"" );
2304 formatPadLayer( B_Cu );
2305 m_out->Print( ")" );
2306 }
2307 else
2308 {
2309 m_out->Print( "(padstack (mode custom)" );
2310
2311 int layerCount = board ? board->GetCopperLayerCount() : MAX_CU_LAYERS;
2312
2313 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, layerCount ) )
2314 {
2315 if( layer == F_Cu )
2316 continue;
2317
2318 m_out->Print( "(layer %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
2319 formatPadLayer( layer );
2320 m_out->Print( ")" );
2321 }
2322 }
2323
2324 m_out->Print( ")" );
2325 }
2326
2327 m_out->Print( ")" );
2328}
2329
2330
2331void PCB_IO_KICAD_SEXPR::format( const PCB_BARCODE* aBarcode ) const
2332{
2333 wxCHECK_RET( aBarcode != nullptr && m_out != nullptr, "" );
2334
2335 m_out->Print( "(barcode" );
2336
2337 if( aBarcode->IsLocked() )
2338 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2339
2340 m_out->Print( "(at %s %s)",
2341 formatInternalUnits( aBarcode->GetPosition() ).c_str(),
2342 EDA_UNIT_UTILS::FormatAngle( aBarcode->GetAngle() ).c_str() );
2343
2344 formatLayer( aBarcode->GetLayer() );
2345
2346 m_out->Print( "(size %s %s)",
2347 formatInternalUnits( aBarcode->GetWidth() ).c_str(),
2348 formatInternalUnits( aBarcode->GetHeight() ).c_str() );
2349
2350 m_out->Print( "(text %s)", m_out->Quotew( aBarcode->GetText() ).c_str() );
2351
2352 m_out->Print( "(text_height %s)", formatInternalUnits( aBarcode->GetTextSize() ).c_str() );
2353
2354 const char* typeStr = "code39";
2355
2356 switch( aBarcode->GetKind() )
2357 {
2358 case BARCODE_T::CODE_39: typeStr = "code39"; break;
2359 case BARCODE_T::CODE_128: typeStr = "code128"; break;
2360 case BARCODE_T::DATA_MATRIX: typeStr = "datamatrix"; break;
2361 case BARCODE_T::QR_CODE: typeStr = "qr"; break;
2362 case BARCODE_T::MICRO_QR_CODE: typeStr = "microqr"; break;
2363 }
2364
2365 m_out->Print( "(type %s)", typeStr );
2366
2367 if( aBarcode->GetKind() == BARCODE_T::QR_CODE
2368 || aBarcode->GetKind() == BARCODE_T::MICRO_QR_CODE )
2369 {
2370 const char* eccStr = "L";
2371 switch( aBarcode->GetErrorCorrection() )
2372 {
2373 case BARCODE_ECC_T::L: eccStr = "L"; break;
2374 case BARCODE_ECC_T::M: eccStr = "M"; break;
2375 case BARCODE_ECC_T::Q: eccStr = "Q"; break;
2376 case BARCODE_ECC_T::H: eccStr = "H"; break;
2377 }
2378
2379 m_out->Print( "(ecc_level %s)", eccStr );
2380 }
2381
2382 KICAD_FORMAT::FormatBool( m_out, "hide", !aBarcode->GetShowText() );
2383 KICAD_FORMAT::FormatBool( m_out, "knockout", aBarcode->IsKnockout() );
2384
2385 if( aBarcode->GetMargin().x != 0 || aBarcode->GetMargin().y != 0 )
2386 {
2387 m_out->Print( "(margins %s %s)", formatInternalUnits( aBarcode->GetMargin().x ).c_str(),
2388 formatInternalUnits( aBarcode->GetMargin().y ).c_str() );
2389 }
2390
2392
2393 m_out->Print( ")" );
2394}
2395
2396
2397void PCB_IO_KICAD_SEXPR::format( const PCB_TEXT* aText ) const
2398{
2399 FOOTPRINT* parentFP = aText->GetParentFootprint();
2400 std::string prefix;
2401 std::string type;
2402 VECTOR2I pos = aText->GetTextPos();
2403 const PCB_FIELD* field = dynamic_cast<const PCB_FIELD*>( aText );
2404
2405 // Always format dimension text as gr_text
2406 if( dynamic_cast<const PCB_DIMENSION_BASE*>( aText ) )
2407 parentFP = nullptr;
2408
2409 if( parentFP )
2410 {
2411 prefix = "fp";
2412 type = "user";
2413
2414 pos -= parentFP->GetPosition();
2415 RotatePoint( pos, -parentFP->GetOrientation() );
2416 }
2417 else
2418 {
2419 prefix = "gr";
2420 }
2421
2422 if( !field )
2423 {
2424 m_out->Print( "(%s_text %s %s",
2425 prefix.c_str(),
2426 type.c_str(),
2427 m_out->Quotew( aText->GetText() ).c_str() );
2428
2429 if( aText->IsLocked() )
2430 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2431 }
2432
2433 m_out->Print( "(at %s %s)",
2434 formatInternalUnits( pos ).c_str(),
2435 EDA_UNIT_UTILS::FormatAngle( aText->GetTextAngle() ).c_str() );
2436
2437 if( parentFP && !aText->IsKeepUpright() )
2438 KICAD_FORMAT::FormatBool( m_out, "unlocked", true );
2439
2440 formatLayer( aText->GetLayer(), aText->IsKnockout() );
2441
2442 if( field && !field->IsVisible() )
2443 KICAD_FORMAT::FormatBool( m_out, "hide", true );
2444
2446
2447 // Currently, texts have no specific color and no hyperlink.
2448 // so ensure they are never written in kicad_pcb file
2449 int ctl_flags = CTL_OMIT_COLOR | CTL_OMIT_HYPERLINK;
2450
2451 aText->EDA_TEXT::Format( m_out, ctl_flags );
2452
2453 if( aText->GetFont() && aText->GetFont()->IsOutline() )
2454 formatRenderCache( aText );
2455
2456 if( !field )
2457 m_out->Print( ")" );
2458}
2459
2460
2461void PCB_IO_KICAD_SEXPR::format( const PCB_TEXTBOX* aTextBox ) const
2462{
2463 FOOTPRINT* parentFP = aTextBox->GetParentFootprint();
2464
2465 m_out->Print( "(%s %s",
2466 aTextBox->Type() == PCB_TABLECELL_T ? "table_cell"
2467 : parentFP ? "fp_text_box"
2468 : "gr_text_box",
2469 m_out->Quotew( aTextBox->GetText() ).c_str() );
2470
2471 if( aTextBox->IsLocked() )
2472 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2473
2474 if( aTextBox->GetShape() == SHAPE_T::RECTANGLE )
2475 {
2476 m_out->Print( "(start %s) (end %s)",
2477 formatInternalUnits( aTextBox->GetStart(), parentFP ).c_str(),
2478 formatInternalUnits( aTextBox->GetEnd(), parentFP ).c_str() );
2479 }
2480 else if( aTextBox->GetShape() == SHAPE_T::POLY )
2481 {
2482 const SHAPE_POLY_SET& poly = aTextBox->GetPolyShape();
2483 const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
2484
2485 formatPolyPts( outline, parentFP );
2486 }
2487 else
2488 {
2489 UNIMPLEMENTED_FOR( aTextBox->SHAPE_T_asString() );
2490 }
2491
2492 m_out->Print( "(margins %s %s %s %s)",
2493 formatInternalUnits( aTextBox->GetMarginLeft() ).c_str(),
2494 formatInternalUnits( aTextBox->GetMarginTop() ).c_str(),
2495 formatInternalUnits( aTextBox->GetMarginRight() ).c_str(),
2496 formatInternalUnits( aTextBox->GetMarginBottom() ).c_str() );
2497
2498 if( const PCB_TABLECELL* cell = dynamic_cast<const PCB_TABLECELL*>( aTextBox ) )
2499 m_out->Print( "(span %d %d)", cell->GetColSpan(), cell->GetRowSpan() );
2500
2501 EDA_ANGLE angle = aTextBox->GetTextAngle();
2502
2503 if( parentFP )
2504 {
2505 angle -= parentFP->GetOrientation();
2506 angle.Normalize720();
2507 }
2508
2509 if( !angle.IsZero() )
2510 m_out->Print( "(angle %s)", EDA_UNIT_UTILS::FormatAngle( angle ).c_str() );
2511
2512 formatLayer( aTextBox->GetLayer() );
2513
2515
2516 aTextBox->EDA_TEXT::Format( m_out, 0 );
2517
2518 if( aTextBox->Type() != PCB_TABLECELL_T )
2519 {
2520 KICAD_FORMAT::FormatBool( m_out, "border", aTextBox->IsBorderEnabled() );
2521 aTextBox->GetStroke().Format( m_out, pcbIUScale );
2522
2523 KICAD_FORMAT::FormatBool( m_out, "knockout", aTextBox->IsKnockout() );
2524 }
2525
2526 if( aTextBox->GetFont() && aTextBox->GetFont()->IsOutline() )
2527 formatRenderCache( aTextBox );
2528
2529 m_out->Print( ")" );
2530}
2531
2532
2533void PCB_IO_KICAD_SEXPR::format( const PCB_TABLE* aTable ) const
2534{
2535 wxCHECK_RET( aTable != nullptr && m_out != nullptr, "" );
2536
2537 m_out->Print( "(table (column_count %d)", aTable->GetColCount() );
2538
2540
2541 if( aTable->IsLocked() )
2542 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2543
2544 formatLayer( aTable->GetLayer() );
2545
2546 m_out->Print( "(border" );
2547 KICAD_FORMAT::FormatBool( m_out, "external", aTable->StrokeExternal() );
2549
2550 if( aTable->StrokeExternal() || aTable->StrokeHeaderSeparator() )
2551 aTable->GetBorderStroke().Format( m_out, pcbIUScale );
2552
2553 m_out->Print( ")" ); // Close `border` token.
2554
2555 m_out->Print( "(separators" );
2556 KICAD_FORMAT::FormatBool( m_out, "rows", aTable->StrokeRows() );
2557 KICAD_FORMAT::FormatBool( m_out, "cols", aTable->StrokeColumns() );
2558
2559 if( aTable->StrokeRows() || aTable->StrokeColumns() )
2561
2562 m_out->Print( ")" ); // Close `separators` token.
2563
2564 m_out->Print( "(column_widths" );
2565
2566 for( int col = 0; col < aTable->GetColCount(); ++col )
2567 m_out->Print( " %s", formatInternalUnits( aTable->GetColWidth( col ) ).c_str() );
2568
2569 m_out->Print( ")" );
2570
2571 m_out->Print( "(row_heights" );
2572
2573 for( int row = 0; row < aTable->GetRowCount(); ++row )
2574 m_out->Print( " %s", formatInternalUnits( aTable->GetRowHeight( row ) ).c_str() );
2575
2576 m_out->Print( ")" );
2577
2578 m_out->Print( "(cells" );
2579
2580 for( PCB_TABLECELL* cell : aTable->GetCells() )
2581 format( static_cast<PCB_TEXTBOX*>( cell ) );
2582
2583 m_out->Print( ")" ); // Close `cells` token.
2584 m_out->Print( ")" ); // Close `table` token.
2585}
2586
2587
2588void PCB_IO_KICAD_SEXPR::format( const PCB_GROUP* aGroup ) const
2589{
2590 wxArrayString memberIds;
2591
2592 // Validate member pointers against the board cache to avoid use-after-free on dangling
2593 // pointers (e.g. when a group held a reference to a deleted item). This validation only
2594 // applies when the group itself is part of m_board; for groups created off-board (e.g. a
2595 // DeepClone() used by the clipboard) the cache contains the originals, not our clones, so
2596 // skip the validation in that case and trust the member pointers.
2597 bool validateAgainstBoard = false;
2598 std::unordered_set<const EDA_ITEM*> validPtrs;
2599
2600 if( m_board )
2601 {
2602 const auto& cache = m_board->GetItemByIdCache();
2603
2604 for( const auto& [uuid, item] : cache )
2605 validPtrs.insert( item );
2606
2607 validateAgainstBoard = validPtrs.count( aGroup ) > 0;
2608 }
2609
2610 if( validateAgainstBoard )
2611 {
2612 for( EDA_ITEM* member : aGroup->GetItems() )
2613 {
2614 if( validPtrs.count( member ) )
2615 memberIds.Add( member->m_Uuid.AsString() );
2616 }
2617 }
2618 else
2619 {
2620 for( EDA_ITEM* member : aGroup->GetItems() )
2621 memberIds.Add( member->m_Uuid.AsString() );
2622 }
2623
2624 if( memberIds.empty() )
2625 return;
2626
2627 m_out->Print( "(group %s", m_out->Quotew( aGroup->GetName() ).c_str() );
2628
2630
2631 if( aGroup->IsLocked() )
2632 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2633
2634 if( aGroup->HasDesignBlockLink() )
2635 m_out->Print( "(lib_id \"%s\")", aGroup->GetDesignBlockLibId().Format().c_str() );
2636
2637 memberIds.Sort();
2638
2639 m_out->Print( "(members" );
2640
2641 for( const wxString& memberId : memberIds )
2642 m_out->Print( " %s", m_out->Quotew( memberId ).c_str() );
2643
2644 m_out->Print( ")" ); // Close `members` token.
2645 m_out->Print( ")" ); // Close `group` token.
2646}
2647
2648
2649void PCB_IO_KICAD_SEXPR::format( const PCB_GENERATOR* aGenerator ) const
2650{
2651 // Some conditions appear to still be creating ghost tuning patterns. Don't save them.
2652 if( aGenerator->GetGeneratorType() == wxT( "tuning_pattern" )
2653 && aGenerator->GetItems().empty() )
2654 {
2655 return;
2656 }
2657
2658 m_out->Print( "(generated" );
2659
2660 KICAD_FORMAT::FormatUuid( m_out, aGenerator->m_Uuid );
2661
2662 m_out->Print( "(type %s) (name %s) (layer %s)",
2663 TO_UTF8( aGenerator->GetGeneratorType() ),
2664 m_out->Quotew( aGenerator->GetName() ).c_str(),
2665 m_out->Quotew( LSET::Name( aGenerator->GetLayer() ) ).c_str() );
2666
2667 if( aGenerator->IsLocked() )
2668 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2669
2670 for( const auto& [key, value] : aGenerator->GetProperties() )
2671 {
2672 if( value.CheckType<double>() || value.CheckType<int>() || value.CheckType<long>()
2673 || value.CheckType<long long>() )
2674 {
2675 double val;
2676
2677 if( !value.GetAs( &val ) )
2678 continue;
2679
2680 std::string buf = fmt::format( "{:.10g}", val );
2681
2682 // Don't quote numbers
2683 m_out->Print( "(%s %s)", key.c_str(), buf.c_str() );
2684 }
2685 else if( value.CheckType<bool>() )
2686 {
2687 bool val;
2688 value.GetAs( &val );
2689
2690 KICAD_FORMAT::FormatBool( m_out, key, val );
2691 }
2692 else if( value.CheckType<VECTOR2I>() )
2693 {
2694 VECTOR2I val;
2695 value.GetAs( &val );
2696
2697 m_out->Print( "(%s (xy %s))",
2698 key.c_str(),
2699 formatInternalUnits( val ).c_str() );
2700 }
2701 else if( value.CheckType<SHAPE_LINE_CHAIN>() )
2702 {
2703 SHAPE_LINE_CHAIN val;
2704 value.GetAs( &val );
2705
2706 m_out->Print( "(%s ", key.c_str() );
2707 formatPolyPts( val );
2708 m_out->Print( ")" );
2709 }
2710 else
2711 {
2712 wxString val;
2713
2714 if( value.CheckType<wxString>() )
2715 {
2716 value.GetAs( &val );
2717 }
2718 else if( value.CheckType<std::string>() )
2719 {
2720 std::string str;
2721 value.GetAs( &str );
2722
2723 val = wxString::FromUTF8( str );
2724 }
2725
2726 m_out->Print( "(%s %s)", key.c_str(), m_out->Quotew( val ).c_str() );
2727 }
2728 }
2729
2730 wxArrayString memberIds;
2731
2732 for( EDA_ITEM* member : aGenerator->GetItems() )
2733 memberIds.Add( member->m_Uuid.AsString() );
2734
2735 memberIds.Sort();
2736
2737 m_out->Print( "(members" );
2738
2739 for( const wxString& memberId : memberIds )
2740 m_out->Print( " %s", m_out->Quotew( memberId ).c_str() );
2741
2742 m_out->Print( ")" ); // Close `members` token.
2743 m_out->Print( ")" ); // Close `generated` token.
2744}
2745
2746
2747void PCB_IO_KICAD_SEXPR::format( const PCB_TRACK* aTrack ) const
2748{
2749 if( aTrack->Type() == PCB_VIA_T )
2750 {
2751 PCB_LAYER_ID layer1, layer2;
2752
2753 const PCB_VIA* via = static_cast<const PCB_VIA*>( aTrack );
2754 const BOARD* board = via->GetBoard();
2755
2756 wxCHECK_RET( board != nullptr, wxT( "Via has no parent." ) );
2757
2758 m_out->Print( "(via" );
2759
2760 via->LayerPair( &layer1, &layer2 );
2761
2762 switch( via->GetViaType() )
2763 {
2764 case VIATYPE::THROUGH: // Default shape not saved.
2765 break;
2766
2767 case VIATYPE::BLIND:
2768 m_out->Print( " blind " );
2769 break;
2770
2771 case VIATYPE::BURIED:
2772 m_out->Print( " buried " );
2773 break;
2774
2775 case VIATYPE::MICROVIA:
2776 m_out->Print( " micro " );
2777 break;
2778
2779 default:
2780 THROW_IO_ERROR( wxString::Format( _( "unknown via type %d" ), via->GetViaType() ) );
2781 }
2782
2783 m_out->Print( "(at %s) (size %s)",
2784 formatInternalUnits( aTrack->GetStart() ).c_str(),
2785 formatInternalUnits( via->GetWidth( F_Cu ) ).c_str() );
2786
2787 // Old boards were using UNDEFINED_DRILL_DIAMETER value in file for via drill when
2788 // via drill was the netclass value.
2789 // recent boards always set the via drill to the actual value, but now we need to
2790 // always store the drill value, because netclass value is not stored in the board file.
2791 // Otherwise the drill value of some (old) vias can be unknown
2792 if( via->GetDrill() != UNDEFINED_DRILL_DIAMETER )
2793 m_out->Print( "(drill %s)", formatInternalUnits( via->GetDrill() ).c_str() );
2794 else
2795 m_out->Print( "(drill %s)", formatInternalUnits( via->GetDrillValue() ).c_str() );
2796
2797 if( via->Padstack().SecondaryDrill().size.x > 0 )
2798 {
2799 m_out->Print( "(backdrill (size %s) (layers %s %s))",
2800 formatInternalUnits( via->Padstack().SecondaryDrill().size.x ).c_str(),
2801 m_out->Quotew( LSET::Name( via->Padstack().SecondaryDrill().start ) ).c_str(),
2802 m_out->Quotew( LSET::Name( via->Padstack().SecondaryDrill().end ) ).c_str() );
2803 }
2804
2805 if( via->Padstack().TertiaryDrill().size.x > 0 )
2806 {
2807 m_out->Print( "(tertiary_drill (size %s) (layers %s %s))",
2808 formatInternalUnits( via->Padstack().TertiaryDrill().size.x ).c_str(),
2809 m_out->Quotew( LSET::Name( via->Padstack().TertiaryDrill().start ) ).c_str(),
2810 m_out->Quotew( LSET::Name( via->Padstack().TertiaryDrill().end ) ).c_str() );
2811 }
2812
2813 auto formatPostMachining = [&]( const char* aName, const PADSTACK::POST_MACHINING_PROPS& aProps )
2814 {
2815 if( !aProps.mode.has_value() || aProps.mode == PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED )
2816 return;
2817
2818 m_out->Print( "(%s %s", aName,
2819 aProps.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ? "counterbore" : "countersink" );
2820
2821 if( aProps.size > 0 )
2822 m_out->Print( " (size %s)", formatInternalUnits( aProps.size ).c_str() );
2823
2824 if( aProps.depth > 0 )
2825 m_out->Print( " (depth %s)", formatInternalUnits( aProps.depth ).c_str() );
2826
2827 if( aProps.angle > 0 )
2828 m_out->Print( " (angle %s)", FormatDouble2Str( aProps.angle / 10.0 ).c_str() );
2829
2830 m_out->Print( ")" );
2831 };
2832
2833 formatPostMachining( "front_post_machining", via->Padstack().FrontPostMachining() );
2834 formatPostMachining( "back_post_machining", via->Padstack().BackPostMachining() );
2835
2836 m_out->Print( "(layers %s %s)",
2837 m_out->Quotew( LSET::Name( layer1 ) ).c_str(),
2838 m_out->Quotew( LSET::Name( layer2 ) ).c_str() );
2839
2840 switch( via->Padstack().UnconnectedLayerMode() )
2841 {
2843 KICAD_FORMAT::FormatBool( m_out, "remove_unused_layers", true );
2844 KICAD_FORMAT::FormatBool( m_out, "keep_end_layers", false );
2845 break;
2846
2848 KICAD_FORMAT::FormatBool( m_out, "remove_unused_layers", true );
2849 KICAD_FORMAT::FormatBool( m_out, "keep_end_layers", true );
2850 break;
2851
2853 KICAD_FORMAT::FormatBool( m_out, "start_end_only", true );
2854 break;
2855
2857 break;
2858 }
2859
2860 if( via->IsLocked() )
2861 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2862
2863 if( via->GetIsFree() )
2864 KICAD_FORMAT::FormatBool( m_out, "free", true );
2865
2866 if( via->GetRemoveUnconnected() )
2867 {
2868 m_out->Print( "(zone_layer_connections" );
2869
2870 for( PCB_LAYER_ID layer : board->GetEnabledLayers().CuStack() )
2871 {
2872 if( via->GetZoneLayerOverride( layer ) == ZLO_FORCE_FLASHED )
2873 m_out->Print( " %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
2874 }
2875
2876 m_out->Print( ")" );
2877 }
2878
2879 const PADSTACK& padstack = via->Padstack();
2880
2881 if( padstack.FrontOuterLayers().has_solder_mask.has_value()
2882 || padstack.BackOuterLayers().has_solder_mask.has_value() )
2883 {
2884 m_out->Print( 0, " (tenting " );
2886 padstack.FrontOuterLayers().has_solder_mask );
2888 padstack.BackOuterLayers().has_solder_mask );
2889 m_out->Print( 0, ")" );
2890 }
2891
2892 if( padstack.Drill().is_capped.has_value() )
2893 KICAD_FORMAT::FormatOptBool( m_out, "capping", padstack.Drill().is_capped );
2894
2895 if( padstack.FrontOuterLayers().has_covering.has_value()
2896 || padstack.BackOuterLayers().has_covering.has_value() )
2897 {
2898 m_out->Print( 0, " (covering " );
2900 padstack.FrontOuterLayers().has_covering );
2902 padstack.BackOuterLayers().has_covering );
2903 m_out->Print( 0, ")" );
2904 }
2905
2906 if( padstack.FrontOuterLayers().has_plugging.has_value()
2907 || padstack.BackOuterLayers().has_plugging.has_value() )
2908 {
2909 m_out->Print( 0, " (plugging " );
2911 padstack.FrontOuterLayers().has_plugging );
2913 padstack.BackOuterLayers().has_plugging );
2914 m_out->Print( 0, ")" );
2915 }
2916
2917 if( padstack.Drill().is_filled.has_value() )
2918 KICAD_FORMAT::FormatOptBool( m_out, "filling", padstack.Drill().is_filled );
2919
2920 if( padstack.Mode() != PADSTACK::MODE::NORMAL )
2921 {
2922 m_out->Print( "(padstack" );
2923
2924 if( padstack.Mode() == PADSTACK::MODE::FRONT_INNER_BACK )
2925 {
2926 m_out->Print( "(mode front_inner_back)" );
2927
2928 m_out->Print( "(layer \"Inner\"" );
2929 m_out->Print( "(size %s)",
2930 formatInternalUnits( padstack.Size( PADSTACK::INNER_LAYERS ).x ).c_str() );
2931 m_out->Print( ")" );
2932 m_out->Print( "(layer \"B.Cu\"" );
2933 m_out->Print( "(size %s)",
2934 formatInternalUnits( padstack.Size( B_Cu ).x ).c_str() );
2935 m_out->Print( ")" );
2936 }
2937 else
2938 {
2939 m_out->Print( "(mode custom)" );
2940
2941 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, board->GetCopperLayerCount() ) )
2942 {
2943 if( layer == F_Cu )
2944 continue;
2945
2946 m_out->Print( "(layer %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
2947 m_out->Print( "(size %s)",
2948 formatInternalUnits( padstack.Size( layer ).x ).c_str() );
2949 m_out->Print( ")" );
2950 }
2951 }
2952
2953 m_out->Print( ")" );
2954 }
2955
2956 if( !isDefaultTeardropParameters( via->GetTeardropParams() ) )
2957 formatTeardropParameters( via->GetTeardropParams() );
2958 }
2959 else
2960 {
2961 if( aTrack->Type() == PCB_ARC_T )
2962 {
2963 const PCB_ARC* arc = static_cast<const PCB_ARC*>( aTrack );
2964
2965 m_out->Print( "(arc (start %s) (mid %s) (end %s) (width %s)",
2966 formatInternalUnits( arc->GetStart() ).c_str(),
2967 formatInternalUnits( arc->GetMid() ).c_str(),
2968 formatInternalUnits( arc->GetEnd() ).c_str(),
2969 formatInternalUnits( arc->GetWidth() ).c_str() );
2970 }
2971 else
2972 {
2973 m_out->Print( "(segment (start %s) (end %s) (width %s)",
2974 formatInternalUnits( aTrack->GetStart() ).c_str(),
2975 formatInternalUnits( aTrack->GetEnd() ).c_str(),
2976 formatInternalUnits( aTrack->GetWidth() ).c_str() );
2977 }
2978
2979 if( aTrack->IsLocked() )
2980 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2981
2982 if( aTrack->GetLayerSet().count() > 1 )
2983 formatLayers( aTrack->GetLayerSet(), false /* enumerate layers */ );
2984 else
2985 formatLayer( aTrack->GetLayer() );
2986
2987 if( aTrack->HasSolderMask()
2988 && aTrack->GetLocalSolderMaskMargin().has_value()
2989 && IsExternalCopperLayer( aTrack->GetLayer() ) )
2990 {
2991 m_out->Print( "(solder_mask_margin %s)",
2992 formatInternalUnits( aTrack->GetLocalSolderMaskMargin().value() ).c_str() );
2993 }
2994 }
2995
2996 if( !( m_ctl & CTL_OMIT_PAD_NETS ) )
2997 m_out->Print( "(net %s)", m_out->Quotew( aTrack->GetNetname() ).c_str() );
2998
3000 m_out->Print( ")" );
3001}
3002
3003
3004void PCB_IO_KICAD_SEXPR::format( const ZONE* aZone ) const
3005{
3006 m_out->Print( "(zone" );
3007
3008 if( !( m_ctl & CTL_OMIT_PAD_NETS ) && aZone->IsOnCopperLayer() && !aZone->GetIsRuleArea()
3009 && aZone->GetNetCode() > 0 )
3010 {
3011 m_out->Print( "(net %s)", m_out->Quotew( aZone->GetNetname() ).c_str() );
3012 }
3013
3014 if( aZone->IsLocked() )
3015 KICAD_FORMAT::FormatBool( m_out, "locked", true );
3016
3017 // If a zone exists on multiple layers, format accordingly
3018 LSET layers = aZone->GetLayerSet();
3019
3020 if( aZone->GetBoard() )
3021 layers &= aZone->GetBoard()->GetEnabledLayers();
3022
3023 // Always enumerate every layer for a zone on a copper layer
3024 if( layers.count() > 1 )
3025 formatLayers( layers, aZone->IsOnCopperLayer(), true );
3026 else
3027 formatLayer( aZone->GetFirstLayer() );
3028
3029 if( !aZone->IsTeardropArea() )
3031
3032 if( !aZone->GetZoneName().empty() && !aZone->IsTeardropArea() )
3033 m_out->Print( "(name %s)", m_out->Quotew( aZone->GetZoneName() ).c_str() );
3034
3035 // Save the outline aux info
3036 std::string hatch;
3037
3038 switch( aZone->GetHatchStyle() )
3039 {
3040 default:
3041 case ZONE_BORDER_DISPLAY_STYLE::NO_HATCH: hatch = "none"; break;
3042 case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE: hatch = "edge"; break;
3043 case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL: hatch = "full"; break;
3044 }
3045
3046 m_out->Print( "(hatch %s %s)", hatch.c_str(),
3047 formatInternalUnits( aZone->GetBorderHatchPitch() ).c_str() );
3048
3049
3050
3051 if( aZone->GetAssignedPriority() > 0 )
3052 m_out->Print( "(priority %d)", aZone->GetAssignedPriority() );
3053
3054 // Add teardrop keywords in file: (attr (teardrop (type xxx))) where xxx is the teardrop type
3055 if( aZone->IsTeardropArea() )
3056 {
3057 m_out->Print( "(attr (teardrop (type %s)))",
3058 aZone->GetTeardropAreaType() == TEARDROP_TYPE::TD_VIAPAD ? "padvia"
3059 : "track_end" );
3060 }
3061
3062 m_out->Print( "(connect_pads" );
3063
3064 switch( aZone->GetPadConnection() )
3065 {
3066 default:
3067 case ZONE_CONNECTION::THERMAL: // Default option not saved or loaded.
3068 break;
3069
3071 m_out->Print( " thru_hole_only" );
3072 break;
3073
3075 m_out->Print( " yes" );
3076 break;
3077
3079 m_out->Print( " no" );
3080 break;
3081 }
3082
3083 m_out->Print( "(clearance %s)",
3084 formatInternalUnits( aZone->GetLocalClearance().value() ).c_str() );
3085
3086 m_out->Print( ")" );
3087
3088 m_out->Print( "(min_thickness %s)",
3089 formatInternalUnits( aZone->GetMinThickness() ).c_str() );
3090
3091 if( aZone->GetIsRuleArea() )
3092 {
3093 // Keepout settings
3094 m_out->Print( "(keepout (tracks %s) (vias %s) (pads %s) (copperpour %s) (footprints %s))",
3095 aZone->GetDoNotAllowTracks() ? "not_allowed" : "allowed",
3096 aZone->GetDoNotAllowVias() ? "not_allowed" : "allowed",
3097 aZone->GetDoNotAllowPads() ? "not_allowed" : "allowed",
3098 aZone->GetDoNotAllowZoneFills() ? "not_allowed" : "allowed",
3099 aZone->GetDoNotAllowFootprints() ? "not_allowed" : "allowed" );
3100
3101 // Multichannel settings
3102 m_out->Print( "(placement" );
3104
3105 switch( aZone->GetPlacementAreaSourceType() )
3106 {
3108 m_out->Print( "(sheetname %s)", m_out->Quotew( aZone->GetPlacementAreaSource() ).c_str() );
3109 break;
3111 m_out->Print( "(component_class %s)", m_out->Quotew( aZone->GetPlacementAreaSource() ).c_str() );
3112 break;
3114 m_out->Print( "(group %s)", m_out->Quotew( aZone->GetPlacementAreaSource() ).c_str() );
3115 break;
3116 // These are transitory and should not be saved
3118 break;
3119 }
3120
3121 m_out->Print( ")" );
3122 }
3123
3124 m_out->Print( "(fill" );
3125
3126 // Default is not filled.
3127 if( aZone->IsFilled() )
3128 m_out->Print( " yes" );
3129
3130 // Default is polygon filled.
3132 m_out->Print( "(mode hatch)" );
3133 else if( aZone->GetFillMode() == ZONE_FILL_MODE::COPPER_THIEVING )
3134 m_out->Print( "(mode thieving)" );
3135
3136 if( !aZone->IsTeardropArea() )
3137 {
3138 m_out->Print( "(thermal_gap %s) (thermal_bridge_width %s)",
3139 formatInternalUnits( aZone->GetThermalReliefGap() ).c_str(),
3140 formatInternalUnits( aZone->GetThermalReliefSpokeWidth() ).c_str() );
3141 }
3142
3144 {
3145 switch( aZone->GetCornerSmoothingType() )
3146 {
3148 m_out->Print( "(smoothing chamfer)" );
3149 break;
3150
3152 m_out->Print( "(smoothing fillet)" );
3153 break;
3154
3155 default:
3156 THROW_IO_ERROR( wxString::Format( _( "unknown zone corner smoothing type %d" ),
3157 aZone->GetCornerSmoothingType() ) );
3158 }
3159
3160 if( aZone->GetCornerRadius() != 0 )
3161 m_out->Print( "(radius %s)", formatInternalUnits( aZone->GetCornerRadius() ).c_str() );
3162 }
3163
3164 m_out->Print( "(island_removal_mode %d)",
3165 static_cast<int>( aZone->GetIslandRemovalMode() ) );
3166
3168 {
3169 m_out->Print( "(island_area_min %s)",
3170 formatInternalUnits( aZone->GetMinIslandArea() / pcbIUScale.IU_PER_MM ).c_str() );
3171 }
3172
3174 {
3175 m_out->Print( "(hatch_thickness %s) (hatch_gap %s) (hatch_orientation %s)",
3176 formatInternalUnits( aZone->GetHatchThickness() ).c_str(),
3177 formatInternalUnits( aZone->GetHatchGap() ).c_str(),
3178 FormatDouble2Str( aZone->GetHatchOrientation().AsDegrees() ).c_str() );
3179
3180 if( aZone->GetHatchSmoothingLevel() > 0 )
3181 {
3182 m_out->Print( "(hatch_smoothing_level %d) (hatch_smoothing_value %s)",
3183 aZone->GetHatchSmoothingLevel(),
3184 FormatDouble2Str( aZone->GetHatchSmoothingValue() ).c_str() );
3185 }
3186
3187 m_out->Print( "(hatch_border_algorithm %s) (hatch_min_hole_area %s)",
3188 aZone->GetHatchBorderAlgorithm() ? "hatch_thickness" : "min_thickness",
3189 FormatDouble2Str( aZone->GetHatchHoleMinArea() ).c_str() );
3190 }
3191 else if( aZone->GetFillMode() == ZONE_FILL_MODE::COPPER_THIEVING )
3192 {
3193 const THIEVING_SETTINGS& thieving = aZone->GetThievingSettings();
3194 const char* patternStr = "dots";
3195
3196 switch( thieving.pattern )
3197 {
3198 case THIEVING_PATTERN::SQUARES: patternStr = "squares"; break;
3199 case THIEVING_PATTERN::HATCH: patternStr = "hatch"; break;
3201 default: patternStr = "dots"; break;
3202 }
3203
3204 m_out->Print( "(thieving (type %s) (size %s) (gap %s) (width %s) "
3205 "(stagger %s) (orientation %s))",
3206 patternStr,
3207 formatInternalUnits( thieving.element_size ).c_str(),
3208 formatInternalUnits( thieving.gap ).c_str(),
3209 formatInternalUnits( thieving.line_width ).c_str(),
3210 thieving.stagger ? "yes" : "no",
3211 FormatDouble2Str( thieving.orientation.AsDegrees() ).c_str() );
3212 }
3213
3214 m_out->Print( ")" );
3215
3216 for( const auto& [layer, properties] : aZone->LayerProperties() )
3217 {
3218 format( properties, 0, layer );
3219 }
3220
3221 if( aZone->GetNumCorners() )
3222 {
3223 SHAPE_POLY_SET::POLYGON poly = aZone->Outline()->Polygon(0);
3224
3225 for( const SHAPE_LINE_CHAIN& chain : poly )
3226 {
3227 if( chain.PointCount() == 0 )
3228 continue;
3229
3230 m_out->Print( "(polygon" );
3232 m_out->Print( ")" );
3233 }
3234 }
3235
3236 // Save the PolysList (filled areas)
3237 for( PCB_LAYER_ID layer : aZone->GetLayerSet().Seq() )
3238 {
3239 const std::shared_ptr<SHAPE_POLY_SET>& fv = aZone->GetFilledPolysList( layer );
3240
3241 for( int ii = 0; ii < fv->OutlineCount(); ++ii )
3242 {
3243 m_out->Print( "(filled_polygon" );
3244 m_out->Print( "(layer %s)", m_out->Quotew( LSET::Name( layer ) ).c_str() );
3245
3246 if( aZone->IsIsland( layer, ii ) )
3247 KICAD_FORMAT::FormatBool( m_out, "island", true );
3248
3249 const SHAPE_LINE_CHAIN& chain = fv->COutline( ii );
3250
3252 m_out->Print( ")" );
3253 }
3254 }
3255
3256 m_out->Print( ")" );
3257}
3258
3259
3260void PCB_IO_KICAD_SEXPR::format( const ZONE_LAYER_PROPERTIES& aZoneLayerProperties, int aNestLevel,
3261 PCB_LAYER_ID aLayer ) const
3262{
3263 // Do not store the layer properties if no value is actually set.
3264 if( !aZoneLayerProperties.hatching_offset.has_value() )
3265 return;
3266
3267 m_out->Print( aNestLevel, "(property\n" );
3268 m_out->Print( aNestLevel, "(layer %s)\n", m_out->Quotew( LSET::Name( aLayer ) ).c_str() );
3269
3270 if( aZoneLayerProperties.hatching_offset.has_value() )
3271 {
3272 m_out->Print( aNestLevel, "(hatch_position (xy %s))",
3273 formatInternalUnits( aZoneLayerProperties.hatching_offset.value() ).c_str() );
3274 }
3275
3276 m_out->Print( aNestLevel, ")\n" );
3277}
3278
3279
3280PCB_IO_KICAD_SEXPR::PCB_IO_KICAD_SEXPR( int aControlFlags ) : PCB_IO( wxS( "KiCad" ) ),
3281 m_cache( nullptr ),
3282 m_ctl( aControlFlags )
3283{
3284 init( nullptr );
3285 m_out = &m_sf;
3286}
3287
3288
3293
3294
3295BOARD* PCB_IO_KICAD_SEXPR::LoadBoard( const wxString& aFileName, BOARD* aAppendToMe,
3296 const std::map<std::string, UTF8>* aProperties,
3297 PROJECT* aProject )
3298{
3299 FILE_LINE_READER reader( aFileName );
3300
3301 unsigned lineCount = 0;
3302
3303 // Collect the font substitution warnings (RAII - automatically reset on scope exit)
3305
3306 if( m_progressReporter )
3307 {
3308 m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
3309
3310 if( !m_progressReporter->KeepRefreshing() )
3311 THROW_IO_ERROR( _( "Open canceled by user." ) );
3312
3313 while( reader.ReadLine() )
3314 lineCount++;
3315
3316 reader.Rewind();
3317 }
3318
3319 BOARD* board = DoLoad( reader, aAppendToMe, aProperties, m_progressReporter, lineCount );
3320
3321 // Give the filename to the board if it's new
3322 if( !aAppendToMe )
3323 board->SetFileName( aFileName );
3324
3325 return board;
3326}
3327
3328
3330 const std::map<std::string, UTF8>* aProperties,
3331 PROGRESS_REPORTER* aProgressReporter, unsigned aLineCount)
3332{
3333 init( aProperties );
3334
3335 bool preserveDestinationStackup =
3336 aProperties && aProperties->contains( PCB_IO_LOAD_PROPERTIES::APPEND_PRESERVE_DESTINATION_STACKUP );
3337
3338 PCB_IO_KICAD_SEXPR_PARSER parser( &aReader, aAppendToMe, m_queryUserCallback, aProgressReporter, aLineCount,
3339 preserveDestinationStackup );
3340 BOARD* board;
3341
3342 try
3343 {
3344 board = dynamic_cast<BOARD*>( parser.Parse() );
3345 }
3346 catch( const FUTURE_FORMAT_ERROR& )
3347 {
3348 // Don't wrap a FUTURE_FORMAT_ERROR in another
3349 throw;
3350 }
3351 catch( const PARSE_ERROR& parse_error )
3352 {
3353 if( parser.IsTooRecent() )
3354 throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
3355 else
3356 throw;
3357 }
3358
3359 if( !board )
3360 {
3361 // The parser loaded something that was valid, but wasn't a board.
3362 THROW_PARSE_ERROR( _( "This file does not contain a PCB." ), parser.CurSource(),
3363 parser.CurLine(), parser.CurLineNumber(), parser.CurOffset() );
3364 }
3365
3366 // Report any non-fatal parse warnings to the load info reporter
3367 for( const wxString& warning : parser.GetParseWarnings() )
3369
3370 return board;
3371}
3372
3373
3374void PCB_IO_KICAD_SEXPR::init( const std::map<std::string, UTF8>* aProperties )
3375{
3376 m_board = nullptr;
3377 m_reader = nullptr;
3378 m_props = aProperties;
3379}
3380
3381
3382void PCB_IO_KICAD_SEXPR::validateCache( const wxString& aLibraryPath, bool checkModified )
3383{
3384 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
3385 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
3386
3387 if( !m_cache || !m_cache->IsPath( aLibraryPath ) || ( checkModified && m_cache->IsModified() ) )
3388 {
3389 // a spectacular episode in memory management:
3390 delete m_cache;
3391 m_cache = new FP_CACHE( this, aLibraryPath );
3392 m_cache->Load();
3393 }
3394}
3395
3396
3397void PCB_IO_KICAD_SEXPR::FootprintEnumerate( wxArrayString& aFootprintNames,
3398 const wxString& aLibPath, bool aBestEfforts,
3399 const std::map<std::string, UTF8>* aProperties )
3400{
3401 wxDir dir( aLibPath );
3402 wxString errorMsg;
3403
3404 init( aProperties );
3405
3406 try
3407 {
3408 validateCache( aLibPath );
3409 }
3410 catch( const IO_ERROR& ioe )
3411 {
3412 errorMsg = ioe.What();
3413 }
3414
3415 // Some of the files may have been parsed correctly so we want to add the valid files to
3416 // the library.
3417
3418 for( const auto& footprint : m_cache->GetFootprints() )
3419 aFootprintNames.Add( footprint.first );
3420
3421 if( !errorMsg.IsEmpty() && !aBestEfforts )
3422 THROW_IO_ERROR( errorMsg );
3423}
3424
3425
3426const FOOTPRINT* PCB_IO_KICAD_SEXPR::getFootprint( const wxString& aLibraryPath,
3427 const wxString& aFootprintName,
3428 const std::map<std::string, UTF8>* aProperties,
3429 bool checkModified )
3430{
3431 init( aProperties );
3432
3433 try
3434 {
3435 validateCache( aLibraryPath, checkModified );
3436 }
3437 catch( const IO_ERROR& )
3438 {
3439 // do nothing with the error
3440 }
3441
3442 auto it = m_cache->GetFootprints().find( aFootprintName );
3443
3444 if( it == m_cache->GetFootprints().end() )
3445 return nullptr;
3446
3447 return it->second->GetFootprint().get();
3448}
3449
3450
3451const FOOTPRINT* PCB_IO_KICAD_SEXPR::GetEnumeratedFootprint( const wxString& aLibraryPath,
3452 const wxString& aFootprintName,
3453 const std::map<std::string, UTF8>* aProperties )
3454{
3455 return getFootprint( aLibraryPath, aFootprintName, aProperties, false );
3456}
3457
3458
3459bool PCB_IO_KICAD_SEXPR::FootprintExists( const wxString& aLibraryPath,
3460 const wxString& aFootprintName,
3461 const std::map<std::string, UTF8>* aProperties )
3462{
3463 // Note: checking the cache sounds like a good idea, but won't catch files which differ
3464 // only in case.
3465 //
3466 // Since this goes out to the native filesystem, we get platform differences (ie: MSW's
3467 // case-insensitive filesystem) handled "for free".
3468 // Warning: footprint names frequently contain a point. So be careful when initializing
3469 // wxFileName, and use a CTOR with extension specified
3470 wxFileName footprintFile( aLibraryPath, aFootprintName, FILEEXT::KiCadFootprintFileExtension );
3471
3472 return footprintFile.Exists();
3473}
3474
3475
3476FOOTPRINT* PCB_IO_KICAD_SEXPR::ImportFootprint( const wxString& aFootprintPath,
3477 wxString& aFootprintNameOut,
3478 const std::map<std::string, UTF8>* aProperties )
3479{
3480 wxString fcontents;
3481 wxFFile f( aFootprintPath );
3482
3483 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
3484 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
3485
3486 if( !f.IsOpened() )
3487 return nullptr;
3488
3489 f.ReadAll( &fcontents );
3490
3491 aFootprintNameOut = wxFileName( aFootprintPath ).GetName();
3492
3493 return dynamic_cast<FOOTPRINT*>( Parse( fcontents ) );
3494}
3495
3496
3497FOOTPRINT* PCB_IO_KICAD_SEXPR::FootprintLoad( const wxString& aLibraryPath,
3498 const wxString& aFootprintName,
3499 bool aKeepUUID,
3500 const std::map<std::string, UTF8>* aProperties )
3501{
3502 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
3503 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
3504
3505 const FOOTPRINT* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
3506
3507 if( footprint )
3508 {
3509 FOOTPRINT* copy;
3510
3511 if( aKeepUUID )
3512 copy = static_cast<FOOTPRINT*>( footprint->Clone() );
3513 else
3514 copy = static_cast<FOOTPRINT*>( footprint->Duplicate( IGNORE_PARENT_GROUP ) );
3515
3516 copy->SetParent( nullptr );
3517 return copy;
3518 }
3519
3520 return nullptr;
3521}
3522
3523
3524void PCB_IO_KICAD_SEXPR::FootprintSave( const wxString& aLibraryPath, const FOOTPRINT* aFootprint,
3525 const std::map<std::string, UTF8>* aProperties )
3526{
3527 init( aProperties );
3528
3529 // In this public PLUGIN API function, we can safely assume it was
3530 // called for saving into a library path.
3532
3533 // Support saving to a single-file path like "/tmp/foo.kicad_mod" by treating the directory
3534 // as the library path and the file base-name as the footprint name.
3535 wxString libPath = aLibraryPath;
3536 wxString singleFileBaseName; // without extension
3537 bool saveSingleFile = false;
3538
3539 {
3540 wxFileName asFile( aLibraryPath );
3541
3542 if( asFile.GetExt() == FILEEXT::KiCadFootprintFileExtension )
3543 {
3544 saveSingleFile = true;
3545 libPath = asFile.GetPath();
3546 singleFileBaseName = asFile.GetName();
3547 }
3548 }
3549
3550 validateCache( libPath, !aProperties || !aProperties->contains( "skip_cache_validation" ) );
3551
3552 if( !m_cache->IsWritable() )
3553 {
3554 if( !m_cache->Exists() )
3555 {
3556 const wxString msg = wxString::Format( _( "Library '%s' does not exist.\n"
3557 "Would you like to create it?"),
3558 libPath );
3559
3560 if( !Pgm().IsGUI() || wxMessageBox( msg, _( "Library Not Found" ), wxYES_NO | wxICON_QUESTION ) != wxYES )
3561 return;
3562
3563 // Save throws its own IO_ERROR on failure, so no need to recreate here
3564 m_cache->Save( nullptr );
3565 }
3566 else
3567 {
3568 wxString msg = wxString::Format( _( "Library '%s' is read only." ), libPath );
3569 THROW_IO_ERROR( msg );
3570 }
3571 }
3572
3573 // The map key used by the cache and the on-disk filename base.
3574 wxString footprintName = saveSingleFile ? singleFileBaseName
3575 : aFootprint->GetFPID().GetUniStringLibItemName();
3576
3577 wxString fpName = saveSingleFile ? singleFileBaseName
3578 : aFootprint->GetFPID().GetUniStringLibItemName();
3579 ReplaceIllegalFileNameChars( fpName, '_' );
3580
3581 // Quietly overwrite footprint and delete footprint file from path for any by same name.
3582 wxFileName fn( libPath, fpName, FILEEXT::KiCadFootprintFileExtension );
3583
3584 // Write through symlinks, don't replace them
3586
3587 if( !fn.IsOk() )
3588 {
3589 THROW_IO_ERROR( wxString::Format( _( "Footprint file name '%s' is not valid." ), fn.GetFullPath() ) );
3590 }
3591
3592 if( fn.FileExists() && !fn.IsFileWritable() )
3593 {
3594 THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete '%s'." ),
3595 fn.GetFullPath() ) );
3596 }
3597
3598 wxString fullPath = fn.GetFullPath();
3599 wxString fullName = fn.GetFullName();
3600 auto it = m_cache->GetFootprints().find( footprintName );
3601
3602 if( it != m_cache->GetFootprints().end() )
3603 {
3604 // Save() below writes atomically via sibling temp + rename, so no pre-delete.
3605 wxLogTrace( traceKicadPcbPlugin, wxT( "Replacing footprint file '%s'." ), fullPath );
3606 m_cache->GetFootprints().erase( footprintName );
3607 }
3608
3609 // I need my own copy for the cache
3610 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aFootprint->Clone() );
3611
3612 // It's orientation should be zero and it should be on the front layer.
3613 footprint->SetOrientation( ANGLE_0 );
3614
3615 if( footprint->GetLayer() != F_Cu )
3616 {
3617 PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() );
3618
3619 if( cfg )
3620 footprint->Flip( footprint->GetPosition(), cfg->m_FlipDirection );
3621 else
3622 footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
3623 }
3624
3625 // Detach it from the board and its group
3626 footprint->SetParent( nullptr );
3627 footprint->SetParentGroup( nullptr );
3628
3629 // Now that the clone is detached from its parent board, any m_netinfo pointers its
3630 // descendants still carry reference NETINFO_ITEMs owned by that board and may dangle.
3631 // Force them all to the board-independent ORPHANED singleton before serialization.
3632 footprint->ClearAllNets();
3633
3634 wxLogTrace( traceKicadPcbPlugin, wxT( "Creating s-expr footprint file '%s'." ), fullPath );
3635 m_cache->GetFootprints().insert( footprintName,
3636 new FP_CACHE_ENTRY( footprint,
3637 WX_FILENAME( fn.GetPath(), fullName ) ) );
3638 m_cache->Save( footprint );
3639}
3640
3641
3642void PCB_IO_KICAD_SEXPR::FootprintDelete( const wxString& aLibraryPath,
3643 const wxString& aFootprintName,
3644 const std::map<std::string, UTF8>* aProperties )
3645{
3646 init( aProperties );
3647
3648 validateCache( aLibraryPath );
3649
3650 if( !m_cache->IsWritable() )
3651 {
3652 THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only." ),
3653 aLibraryPath.GetData() ) );
3654 }
3655
3656 m_cache->Remove( aFootprintName );
3657}
3658
3659
3660void PCB_IO_KICAD_SEXPR::ClearCachedFootprints( const wxString& aLibraryPath )
3661{
3662 if( m_cache && m_cache->IsPath( aLibraryPath ) )
3663 {
3664 delete m_cache;
3665 m_cache = nullptr;
3666 }
3667}
3668
3669
3670long long PCB_IO_KICAD_SEXPR::GetLibraryTimestamp( const wxString& aLibraryPath ) const
3671{
3672 return FP_CACHE::GetTimestamp( aLibraryPath );
3673}
3674
3675
3676void PCB_IO_KICAD_SEXPR::CreateLibrary( const wxString& aLibraryPath,
3677 const std::map<std::string, UTF8>* aProperties )
3678{
3679 if( wxDir::Exists( aLibraryPath ) )
3680 {
3681 THROW_IO_ERROR( wxString::Format( _( "Cannot overwrite library path '%s'." ),
3682 aLibraryPath.GetData() ) );
3683 }
3684
3685 init( aProperties );
3686
3687 delete m_cache;
3688 m_cache = new FP_CACHE( this, aLibraryPath );
3689 m_cache->Save();
3690}
3691
3692
3693bool PCB_IO_KICAD_SEXPR::DeleteLibrary( const wxString& aLibraryPath,
3694 const std::map<std::string, UTF8>* aProperties )
3695{
3696 wxFileName fn;
3697 fn.SetPath( aLibraryPath );
3698
3699 // Return if there is no library path to delete.
3700 if( !fn.DirExists() )
3701 return false;
3702
3703 if( !fn.IsDirWritable() )
3704 {
3705 THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete folder '%s'." ),
3706 aLibraryPath.GetData() ) );
3707 }
3708
3709 wxDir dir( aLibraryPath );
3710
3711 if( dir.HasSubDirs() )
3712 {
3713 THROW_IO_ERROR( wxString::Format( _( "Library folder '%s' has unexpected sub-folders." ),
3714 aLibraryPath.GetData() ) );
3715 }
3716
3717 // All the footprint files must be deleted before the directory can be deleted.
3718 if( dir.HasFiles() )
3719 {
3720 unsigned i;
3721 wxFileName tmp;
3722 wxArrayString files;
3723
3724 wxDir::GetAllFiles( aLibraryPath, &files );
3725
3726 for( i = 0; i < files.GetCount(); i++ )
3727 {
3728 tmp = files[i];
3729
3730 if( tmp.GetExt() != FILEEXT::KiCadFootprintFileExtension )
3731 {
3732 THROW_IO_ERROR( wxString::Format( _( "Unexpected file '%s' found in library "
3733 "path '%s'." ),
3734 files[i].GetData(),
3735 aLibraryPath.GetData() ) );
3736 }
3737 }
3738
3739 for( i = 0; i < files.GetCount(); i++ )
3740 wxRemoveFile( files[i] );
3741 }
3742
3743 wxLogTrace( traceKicadPcbPlugin, wxT( "Removing footprint library '%s'." ),
3744 aLibraryPath.GetData() );
3745
3746 // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
3747 // we don't want that. we want bare metal portability with no UI here.
3748 if( !wxRmdir( aLibraryPath ) )
3749 {
3750 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' cannot be deleted." ),
3751 aLibraryPath.GetData() ) );
3752 }
3753
3754 // For some reason removing a directory in Windows is not immediately updated. This delay
3755 // prevents an error when attempting to immediately recreate the same directory when over
3756 // writing an existing library.
3757#ifdef __WINDOWS__
3758 wxMilliSleep( 250L );
3759#endif
3760
3761 if( m_cache && !m_cache->IsPath( aLibraryPath ) )
3762 {
3763 delete m_cache;
3764 m_cache = nullptr;
3765 }
3766
3767 return true;
3768}
3769
3770
3771bool PCB_IO_KICAD_SEXPR::IsLibraryWritable( const wxString& aLibraryPath )
3772{
3773 init( nullptr );
3774
3775 validateCache( aLibraryPath );
3776
3777 return m_cache->IsWritable();
3778}
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
@ LT_FRONT
Definition board.h:193
@ LT_BACK
Definition board.h:194
@ ZLO_FORCE_FLASHED
Definition board_item.h:74
wxString GetMajorMinorVersion()
Get only the major and minor version in a string major.minor.
bool SaveImageData(wxOutputStream &aOutStream) const
Write the bitmap data to aOutStream.
wxImage * GetImageData()
Definition bitmap_base.h:68
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
TEARDROP_PARAMETERS & GetTeardropParams()
Container for design settings for a BOARD object.
std::map< PCB_LAYER_ID, ZONE_LAYER_PROPERTIES > m_ZoneLayerProperties
const VECTOR2I & GetGridOrigin() const
int GetBoardThickness() const
The full thickness of the board including copper and masks.
const VECTOR2I & GetAuxOrigin() const
BOARD_STACKUP & GetStackupDescriptor()
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:268
virtual bool IsKnockout() const
Definition board_item.h:355
bool IsLocked() const override
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
VECTOR2I GetFPRelativePosition() const
Manage layers needed to make a physical board.
void FormatBoardStackup(OUTPUTFORMATTER *aFormatter, const BOARD *aBoard) const
Write the stackup info on board file.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1004
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition board.cpp:3362
const std::vector< wxString > & GetVariantNames() const
Definition board.h:401
const GENERATORS & Generators() const
Definition board.h:370
void SetFileName(const wxString &aFileName)
Definition board.h:358
const PCB_POINTS & Points() const
Definition board.h:378
const PAGE_INFO & GetPageSettings() const
Definition board.h:807
const ZONES & Zones() const
Definition board.h:368
const GROUPS & Groups() const
The groups must maintain the following invariants.
Definition board.h:390
LAYER_T GetLayerType(PCB_LAYER_ID aLayer) const
Return the type of the copper layer given by aLayer.
Definition board.cpp:801
TITLE_BLOCK & GetTitleBlock()
Definition board.h:813
int GetCopperLayerCount() const
Definition board.cpp:937
const std::map< wxString, wxString > & GetProperties() const
Definition board.h:394
const FOOTPRINTS & Footprints() const
Definition board.h:364
const TRACKS & Tracks() const
Definition board.h:362
wxString GetVariantDescription(const wxString &aVariantName) const
Definition board.cpp:2897
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition board.h:810
bool LegacyTeardrops() const
Definition board.h:1413
wxString GroupsSanityCheck(bool repair=false)
Consistency check of internal m_groups structure.
Definition board.cpp:3578
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1101
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:986
const DRAWINGS & Drawings() const
Definition board.h:366
A lightweight representation of a component class.
const std::vector< COMPONENT_CLASS * > & GetConstituentClasses() const
Fetches a vector of the constituent classes for this (effective) class.
double AsDegrees() const
Definition eda_angle.h:116
bool IsZero() const
Definition eda_angle.h:136
EDA_ANGLE Normalize720()
Definition eda_angle.h:279
const LIB_ID & GetDesignBlockLibId() const
Definition eda_group.h:73
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:54
wxString GetName() const
Definition eda_group.h:51
bool HasDesignBlockLink() const
Definition eda_group.h:70
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:100
const KIID m_Uuid
Definition eda_item.h:528
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
virtual void SetParentGroup(EDA_GROUP *aGroup)
Definition eda_item.h:117
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:93
int GetEllipseMinorRadius() const
Definition eda_shape.h:306
const VECTOR2I & GetBezierC2() const
Definition eda_shape.h:279
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:288
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:334
FILL_T GetFillMode() const
Definition eda_shape.h:162
int GetEllipseMajorRadius() const
Definition eda_shape.h:297
SHAPE_POLY_SET & GetPolyShape()
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:315
SHAPE_T GetShape() const
Definition eda_shape.h:189
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:236
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:194
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:325
wxString SHAPE_T_asString() const
const VECTOR2I & GetBezierC1() const
Definition eda_shape.h:276
int GetCornerRadius() const
bool IsPolyShapeValid() const
VECTOR2I GetArcMid() const
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:93
const VECTOR2I & GetTextPos() const
Definition eda_text.h:298
const EDA_ANGLE & GetTextAngle() const
Definition eda_text.h:172
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:114
bool IsKeepUpright() const
Definition eda_text.h:231
virtual bool IsVisible() const
Definition eda_text.h:212
KIFONT::FONT * GetFont() const
Definition eda_text.h:272
std::vector< std::unique_ptr< KIFONT::GLYPH > > * GetRenderCache(const KIFONT::FONT *aFont, const wxString &forResolvedText, const VECTOR2I &aOffset={ 0, 0 }) const
Definition eda_text.cpp:702
virtual EDA_ANGLE GetDrawRotation() const
Definition eda_text.h:404
virtual wxString GetShownText(bool aAllowExtraText, int aDepth=0) const
Return the string actually shown after processing of the base text.
Definition eda_text.h:125
int GetTextThickness() const
Definition eda_text.h:153
bool IsEmpty() const
void WriteEmbeddedFiles(OUTPUTFORMATTER &aOut, bool aWriteData) const
Output formatter for the embedded files.
void ClearEmbeddedFiles(bool aDeleteFiles=true)
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Load a file from disk and adds it to the collection.
const std::map< wxString, EMBEDDED_FILE * > & EmbeddedFileMap() const
bool GetAreFontsEmbedded() const
A LINE_READER that reads from an open file.
Definition richio.h:158
void Rewind()
Rewind the file and resets the line number back to zero.
Definition richio.h:207
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition richio.cpp:212
RAII class to set and restore the fontconfig reporter.
Definition reporter.h:336
bool GetDuplicatePadNumbersAreJumpers() const
Definition footprint.h:1137
const CASE_INSENSITIVE_MAP< FOOTPRINT_VARIANT > & GetVariants() const
Get all variants.
Definition footprint.h:1021
bool AllowSolderMaskBridges() const
Definition footprint.h:501
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:430
wxString GetLibDescription() const
Definition footprint.h:446
ZONE_CONNECTION GetLocalZoneConnection() const
Definition footprint.h:477
bool IsDNP() const
Definition footprint.h:957
EDA_ANGLE GetOrientation() const
Definition footprint.h:408
ZONES & Zones()
Definition footprint.h:383
PCB_POINTS & Points()
Definition footprint.h:389
bool IsExcludedFromBOM() const
Definition footprint.h:948
void SetOrientation(const EDA_ANGLE &aNewAngle)
wxString GetSheetname() const
Definition footprint.h:455
const std::vector< FP_UNIT_INFO > & GetUnitInfo() const
Definition footprint.h:928
const EXTRUDED_3D_BODY * GetExtrudedBody() const
Definition footprint.h:398
std::optional< int > GetLocalSolderPasteMargin() const
Definition footprint.h:470
EDA_ITEM * Clone() const override
Invoke a function on all children.
PCB_FIELD & Value()
read/write accessors:
Definition footprint.h:865
std::optional< int > GetLocalClearance() const
Definition footprint.h:464
std::vector< std::set< wxString > > & JumperPadGroups()
Each jumper pad group is a set of pad numbers that should be treated as internally connected.
Definition footprint.h:1144
PCB_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this footprint.
std::deque< PAD * > & Pads()
Definition footprint.h:377
int GetAttributes() const
Definition footprint.h:495
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:417
LSET GetPrivateLayers() const
Definition footprint.h:317
bool AllowMissingCourtyard() const
Definition footprint.h:498
wxString GetSheetfile() const
Definition footprint.h:458
const std::vector< wxString > & GetNetTiePadGroups() const
Definition footprint.h:550
const LIB_ID & GetFPID() const
Definition footprint.h:429
bool IsLocked() const override
Definition footprint.h:622
bool IsExcludedFromPosFiles() const
Definition footprint.h:939
const LSET & GetStackupLayers() const
Definition footprint.h:493
PCB_FIELD & Reference()
Definition footprint.h:866
void ClearAllNets()
Clear (i.e.
bool IsNetTie() const
Definition footprint.h:508
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition footprint.h:473
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
GROUPS & Groups()
Definition footprint.h:386
wxString GetFilters() const
Definition footprint.h:461
const wxArrayString * GetInitialComments() const
Return the initial comments block or NULL if none, without transfer of ownership.
Definition footprint.h:1265
void GetFields(std::vector< PCB_FIELD * > &aVector, bool aVisibleOnly) const
Populate a std::vector with PCB_TEXTs.
std::vector< FP_3DMODEL > & Models()
Definition footprint.h:394
BOARD_ITEM * Duplicate(bool addToParentGroup, BOARD_COMMIT *aCommit=nullptr) const override
Create a copy of this BOARD_ITEM.
const COMPONENT_CLASS * GetStaticComponentClass() const
Returns the component class for this footprint.
const KIID_PATH & GetPath() const
Definition footprint.h:452
std::optional< int > GetLocalSolderMaskMargin() const
Definition footprint.h:467
wxString GetKeywords() const
Definition footprint.h:449
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition footprint.h:1293
FOOTPRINT_STACKUP GetStackupMode() const
Definition footprint.h:486
bool IsPlaced() const
Definition footprint.h:645
VECTOR2I GetPosition() const override
Definition footprint.h:405
DRAWINGS & GraphicalItems()
Definition footprint.h:380
Helper class for creating a footprint library cache.
std::unique_ptr< FOOTPRINT > m_footprint
WX_FILENAME m_filename
FP_CACHE_ENTRY(FOOTPRINT *aFootprint, const WX_FILENAME &aFileName)
const WX_FILENAME & GetFileName() const
std::unique_ptr< FOOTPRINT > & GetFootprint()
static long long GetTimestamp(const wxString &aLibPath)
Generate a timestamp representing all source files in the cache (including the parent directory).
boost::ptr_map< wxString, FP_CACHE_ENTRY > m_footprints
PCB_IO_KICAD_SEXPR * m_owner
bool IsModified()
Return true if the cache is not up-to-date.
long long m_cache_timestamp
wxString m_lib_raw_path
void SetPath(const wxString &aPath)
wxFileName m_lib_path
bool IsPath(const wxString &aPath) const
Check if aPath is the same as the current cache path.
void Save(FOOTPRINT *aFootprintFilter=nullptr)
Save the footprint cache or a single footprint from it to disk.
FP_CACHE(PCB_IO_KICAD_SEXPR *aOwner, const wxString &aLibraryPath)
boost::ptr_map< wxString, FP_CACHE_ENTRY > & GetFootprints()
void Remove(const wxString &aFootprintName)
PROGRESS_REPORTER * m_progressReporter
Progress reporter to track the progress of the operation, may be nullptr.
Definition io_base.h:240
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
APP_SETTINGS_BASE * KifaceSettings() const
Definition kiface_base.h:95
virtual bool IsOutline() const
Definition font.h:106
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:402
virtual void SetLineWidth(float aLineWidth)
Set the line width.
virtual wxString GetClass() const =0
Return the class name.
wxString AsString() const
Definition kiid.cpp:365
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
UTF8 Format() const
Definition lib_id.cpp:119
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition lib_id.h:112
const UTF8 & GetLibItemName() const
Definition lib_id.h:102
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition richio.h:66
static LOAD_INFO_REPORTER & GetInstance()
Definition reporter.cpp:249
REPORTER & Report(const wxString &aMsg, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition reporter.cpp:234
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
LSEQ CuStack() const
Return a sequence of copper layers in starting from the front/top and extending to the back/bottom.
Definition lset.cpp:263
LSEQ TechAndUserUIOrder() const
Return the technical and user layers in the order shown in layer widget.
Definition lset.cpp:276
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:313
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:599
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:188
Handle the data for a net.
Definition netinfo.h:50
const wxString & GetNetname() const
Definition netinfo.h:103
An interface used to output 8 bit text in a convenient way.
Definition richio.h:295
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
Definition padstack.h:157
std::optional< int > & Clearance(PCB_LAYER_ID aLayer=F_Cu)
MASK_LAYER_PROPS & FrontOuterLayers()
Definition padstack.h:372
void ForEachUniqueLayer(const std::function< void(PCB_LAYER_ID)> &aMethod) const
Runs the given callable for each active unique copper layer in this padstack, meaning F_Cu for MODE::...
std::optional< int > & ThermalSpokeWidth(PCB_LAYER_ID aLayer=F_Cu)
EDA_ANGLE ThermalSpokeAngle(PCB_LAYER_ID aLayer=F_Cu) const
POST_MACHINING_PROPS & FrontPostMachining()
Definition padstack.h:360
std::optional< int > & ThermalGap(PCB_LAYER_ID aLayer=F_Cu)
DRILL_PROPS & TertiaryDrill()
Definition padstack.h:357
DRILL_PROPS & Drill()
Definition padstack.h:351
const VECTOR2I & Size(PCB_LAYER_ID aLayer) const
Definition padstack.cpp:875
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:171
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
Definition padstack.h:172
DRILL_PROPS & SecondaryDrill()
Definition padstack.h:354
POST_MACHINING_PROPS & BackPostMachining()
Definition padstack.h:363
MODE Mode() const
Definition padstack.h:335
MASK_LAYER_PROPS & BackOuterLayers()
Definition padstack.h:375
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
static constexpr PCB_LAYER_ID INNER_LAYERS
! The layer identifier to use for "inner layers" on top/inner/bottom padstacks
Definition padstack.h:180
std::optional< ZONE_CONNECTION > & ZoneConnection(PCB_LAYER_ID aLayer=F_Cu)
Definition pad.h:55
PAD_PROP GetProperty() const
Definition pad.h:566
bool GetRemoveUnconnected() const
Definition pad.h:867
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition pad.h:560
const std::vector< std::shared_ptr< PCB_SHAPE > > & GetPrimitives(PCB_LAYER_ID aLayer) const
Accessor to the basic shape list for custom-shaped pads.
Definition pad.h:377
const ZONE_LAYER_OVERRIDE & GetZoneLayerOverride(PCB_LAYER_ID aLayer) const
Definition pad.cpp:269
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition pad.h:600
const wxString & GetPinType() const
Definition pad.h:154
const VECTOR2I & GetDrillSize() const
Definition pad.h:317
PAD_ATTRIB GetAttribute() const
Definition pad.h:563
const wxString & GetPinFunction() const
Definition pad.h:148
const wxString & GetNumber() const
Definition pad.h:137
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition pad.h:304
EDA_ANGLE GetThermalSpokeAngle() const
Definition pad.h:750
double GetRoundRectRadiusRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:805
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:196
bool GetKeepTopBottom() const
Definition pad.h:882
int GetPadToDieDelay() const
Definition pad.h:581
std::optional< int > GetLocalClearance() const override
Return any local clearances set in the "classic" (ie: pre-rule) system.
Definition pad.h:583
const PADSTACK & Padstack() const
Definition pad.h:333
const VECTOR2I & GetOffset(PCB_LAYER_ID aLayer) const
Definition pad.h:329
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.h:420
PAD_DRILL_SHAPE GetDrillShape() const
Definition pad.h:437
int GetChamferPositions(PCB_LAYER_ID aLayer) const
Definition pad.h:845
std::optional< int > GetLocalSolderPasteMargin() const
Definition pad.h:593
std::optional< int > GetLocalSolderMaskMargin() const
Definition pad.h:586
double GetChamferRectRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:828
std::optional< int > GetLocalThermalSpokeWidthOverride() const
Definition pad.h:734
ZONE_CONNECTION GetLocalZoneConnection() const
Definition pad.h:611
CUSTOM_SHAPE_ZONE_MODE GetCustomShapeInZoneOpt() const
Definition pad.h:222
int GetLocalThermalGapOverride(wxString *aSource) const
Definition pad.cpp:1877
PAD_SHAPE GetAnchorPadShape(PCB_LAYER_ID aLayer) const
Definition pad.h:214
int GetPadToDieLength() const
Definition pad.h:578
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition pad.h:264
void Format(OUTPUTFORMATTER *aFormatter) const
Output the page class to aFormatter in s-expression form.
FLIP_DIRECTION m_FlipDirection
const VECTOR2I & GetMid() const
Definition pcb_track.h:290
const VECTOR2I & GetMargin() const
Get the barcode margin (in internal units).
VECTOR2I GetPosition() const override
Get the position (center) of the barcode in internal units.
wxString GetText() const
int GetTextSize() const
bool IsKnockout() const override
int GetHeight() const
Get the barcode height (in internal units).
BARCODE_ECC_T GetErrorCorrection() const
bool GetShowText() const
EDA_ANGLE GetAngle() const
BARCODE_T GetKind() const
Returns the type of the barcode (QR, CODE_39, etc.).
int GetWidth() const
Get the barcode width (in internal units).
Abstract dimension API.
wxString GetOverrideText() const
wxString GetSuffix() const
int GetLineThickness() const
DIM_TEXT_POSITION GetTextPositionMode() const
bool GetKeepTextAligned() const
DIM_PRECISION GetPrecision() const
wxString GetPrefix() const
DIM_UNITS_MODE GetUnitsMode() const
DIM_UNITS_FORMAT GetUnitsFormat() const
virtual const VECTOR2I & GetStart() const
The dimension's origin is the first feature point for the dimension.
DIM_ARROW_DIRECTION GetArrowDirection() const
bool GetSuppressZeroes() const
int GetExtensionOffset() const
int GetArrowLength() const
bool GetOverrideTextEnabled() const
virtual const VECTOR2I & GetEnd() const
For better understanding of the points that make a dimension:
int GetHeight() const
int GetExtensionHeight() const
Mark the center of a circle or arc with a cross shape.
A leader is a dimension-like object pointing to a specific point.
DIM_TEXT_BORDER GetTextBorder() const
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
A radial dimension indicates either the radius or diameter of an arc or circle.
int GetLeaderLength() const
virtual const STRING_ANY_MAP GetProperties() const
virtual wxString GetGeneratorType() const
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:53
Read a Pcbnew s-expression formatted LINE_READER object and returns the appropriate BOARD_ITEM object...
const std::vector< wxString > & GetParseWarnings() const
Return any non-fatal parse warnings that occurred during parsing.
bool IsTooRecent()
Return whether a version number, if any was parsed, was too recent.
bool IsValidBoardHeader()
Partially parse the input and check if it matches expected header.
wxString GetRequiredVersion()
Return a string representing the version of KiCad required to open this file.
A #PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
BOARD * DoLoad(LINE_READER &aReader, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties, PROGRESS_REPORTER *aProgressReporter, unsigned aLineCount)
bool CanReadBoard(const wxString &aFileName) const override
Checks if this PCB_IO can read the specified board file.
void formatProperties(const BOARD *aBoard) const
formats the Nets and Netclasses
FOOTPRINT * ImportFootprint(const wxString &aFootprintPath, wxString &aFootprintNameOut, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a single footprint from aFootprintPath and put its name in aFootprintNameOut.
void FootprintDelete(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Delete aFootprintName from the library at aLibraryPath.
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
bool IsLibraryWritable(const wxString &aLibraryPath) override
Return true if the library at aLibraryPath is writable.
void formatTeardropParameters(const TEARDROP_PARAMETERS &tdParams) const
bool DeleteLibrary(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Delete an existing library and returns true, or if library does not exist returns false,...
const FOOTPRINT * GetEnumeratedFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
A version of FootprintLoad() for use after FootprintEnumerate() for more efficient cache management.
void CreateLibrary(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Create a new empty library at aLibraryPath empty.
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Return a list of footprint names contained within the library at aLibraryPath.
void formatPolyPts(const SHAPE_LINE_CHAIN &outline, const FOOTPRINT *aParentFP=nullptr) const
FP_CACHE * m_cache
Footprint library cache.
void formatBoardLayers(const BOARD *aBoard) const
formats the board layer information
FOOTPRINT * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, bool aKeepUUID=false, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a footprint having aFootprintName from the aLibraryPath containing a library format that this PC...
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
bool FootprintExists(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Check for the existence of a footprint.
void FootprintSave(const wxString &aLibraryPath, const FOOTPRINT *aFootprint, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aFootprint to an existing library located at aLibraryPath.
void format(const BOARD *aBoard) const
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...
void formatLayers(LSET aLayerMask, bool aEnumerateLayers, bool aIsZone=false) const
void formatGeneral(const BOARD *aBoard) const
formats the General section of the file
void formatVariants(const BOARD *aBoard) const
formats the board variant registry
void ClearCachedFootprints(const wxString &aLibraryPath) override
Clear any cached footprint data for the given library path.
void formatSetup(const BOARD *aBoard) const
formats the board setup information
void FormatBoardToFormatter(OUTPUTFORMATTER *aOut, BOARD *aBoard, const std::map< std::string, UTF8 > *aProperties=nullptr)
Serialize a BOARD to an OUTPUTFORMATTER without file I/O or Prettify.
std::function< bool(wxString aTitle, int aIcon, wxString aMsg, wxString aAction)> m_queryUserCallback
BOARD_ITEM * Parse(const wxString &aClipboardSourceInput)
void init(const std::map< std::string, UTF8 > *aProperties)
STRING_FORMATTER m_sf
void Format(const BOARD_ITEM *aItem) const
Output aItem to aFormatter in s-expression format.
void formatLayer(PCB_LAYER_ID aLayer, bool aIsKnockout=false) const
void formatHeader(const BOARD *aBoard) const
writes everything that comes before the board_items, like settings and layers etc
const FOOTPRINT * getFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties, bool checkModified)
PCB_IO_KICAD_SEXPR(int aControlFlags=CTL_FOR_BOARD)
OUTPUTFORMATTER * m_out
output any Format()s to this, no ownership
void validateCache(const wxString &aLibraryPath, bool checkModified=true)
void formatRenderCache(const EDA_TEXT *aText) const
LINE_READER * m_reader
no ownership
BOARD * m_board
The board BOARD being worked on, no ownership here.
Definition pcb_io.h:353
virtual bool CanReadBoard(const wxString &aFileName) const
Checks if this PCB_IO can read the specified board file.
Definition pcb_io.cpp:42
PCB_IO(const wxString &aName)
Definition pcb_io.h:346
const std::map< std::string, UTF8 > * m_props
Properties passed via Save() or Load(), no ownership, may be NULL.
Definition pcb_io.h:356
void Format(OUTPUTFORMATTER *aFormatter) const
A PCB_POINT is a 0-dimensional point that is used to mark a position on a PCB, or more usually a foot...
Definition pcb_point.h:43
int GetSize() const
Definition pcb_point.h:67
VECTOR2I GetPosition() const override
Definition pcb_point.h:64
Object to handle a bitmap image that can be inserted in a PCB.
VECTOR2I GetPosition() const override
Get the position of the image (this is the center of the image).
REFERENCE_IMAGE & GetReferenceImage()
std::optional< int > GetLocalSolderMaskMargin() const
Definition pcb_shape.h:216
bool HasSolderMask() const
Definition pcb_shape.h:213
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
STROKE_PARAMS GetStroke() const override
Definition pcb_shape.h:97
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition pcb_shape.h:71
bool StrokeRows() const
Definition pcb_table.h:107
int GetRowCount() const
Definition pcb_table.h:125
bool StrokeHeaderSeparator() const
Definition pcb_table.h:65
bool StrokeColumns() const
Definition pcb_table.h:104
bool StrokeExternal() const
Definition pcb_table.h:62
std::vector< PCB_TABLECELL * > GetCells() const
Definition pcb_table.h:160
int GetColCount() const
Definition pcb_table.h:123
const STROKE_PARAMS & GetSeparatorsStroke() const
Definition pcb_table.h:86
const STROKE_PARAMS & GetBorderStroke() const
Definition pcb_table.h:68
int GetColWidth(int aCol) const
Definition pcb_table.h:132
int GetRowHeight(int aRow) const
Definition pcb_table.h:142
int GetShape() const
Definition pcb_target.h:58
int GetWidth() const
Definition pcb_target.h:64
int GetSize() const
Definition pcb_target.h:61
VECTOR2I GetPosition() const override
Definition pcb_target.h:55
bool IsBorderEnabled() const
Disables the border, this is done by changing the stroke internally.
int GetMarginBottom() const
Definition pcb_textbox.h:99
int GetMarginLeft() const
Definition pcb_textbox.h:96
int GetMarginRight() const
Definition pcb_textbox.h:98
int GetMarginTop() const
Definition pcb_textbox.h:97
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
bool HasSolderMask() const
Definition pcb_track.h:121
std::optional< int > GetLocalSolderMaskMargin() const
Definition pcb_track.h:124
const VECTOR2I & GetStart() const
Definition pcb_track.h:97
const VECTOR2I & GetEnd() const
Definition pcb_track.h:94
virtual int GetWidth() const
Definition pcb_track.h:91
bool Finish() override
Runs prettification over the buffered bytes, writes them to the sibling temp file,...
Definition richio.cpp:700
A progress reporter interface for use in multi-threaded environments.
Container for project specific data.
Definition project.h:66
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
const BITMAP_BASE & GetImage() const
Get the underlying image.
double GetImageScale() const
const VECTOR2I & GetArcMid() const
Definition shape_arc.h:120
const VECTOR2I & GetP1() const
Definition shape_arc.h:119
const VECTOR2I & GetP0() const
Definition shape_arc.h:118
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_ARC & Arc(size_t aArc) const
int PointCount() const
Return the number of points (vertices) in this line chain.
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the set.
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
Is a LINE_READER that reads from a multiline 8 bit wide std::string.
Definition richio.h:226
void Format(OUTPUTFORMATTER *out, const EDA_IU_SCALE &aIuScale) const
TEARDROP_PARAMETARS is a helper class to handle parameters needed to build teardrops for a board thes...
double m_BestWidthRatio
The height of a teardrop as ratio between height and size of pad/via.
int m_TdMaxLen
max allowed length for teardrops in IU. <= 0 to disable
bool m_AllowUseTwoTracks
True to create teardrops using 2 track segments if the first in too small.
int m_TdMaxWidth
max allowed height for teardrops in IU. <= 0 to disable
double m_BestLengthRatio
The length of a teardrop as ratio between length and size of pad/via.
double m_WidthtoSizeFilterRatio
The ratio (H/D) between the via/pad size and the track width max value to create a teardrop 1....
bool m_TdOnPadsInZones
A filter to exclude pads inside zone fills.
bool m_Enabled
Flag to enable teardrops.
bool m_CurvedEdges
True if the teardrop should be curved.
virtual void Format(OUTPUTFORMATTER *aFormatter) const
Output the object to aFormatter in s-expression form.
const char * c_str() const
Definition utf8.h:108
A wrapper around a wxFileName which is much more performant with a subset of the API.
Definition wx_filename.h:50
void SetFullName(const wxString &aFileNameAndExtension)
static void ResolvePossibleSymlinks(wxFileName &aFilename)
wxString GetName() const
wxString GetFullPath() const
long long GetTimestamp()
Handle a list of polygons defining a copper zone.
Definition zone.h:74
int GetHatchBorderAlgorithm() const
Definition zone.h:347
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:802
std::optional< int > GetLocalClearance() const override
Definition zone.cpp:936
const THIEVING_SETTINGS & GetThievingSettings() const
Definition zone.h:355
bool GetDoNotAllowVias() const
Definition zone.h:813
ZONE_LAYER_PROPERTIES & LayerProperties(PCB_LAYER_ID aLayer)
Definition zone.h:150
wxString GetPlacementAreaSource() const
Definition zone.h:807
std::shared_ptr< SHAPE_POLY_SET > GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition zone.h:688
bool GetDoNotAllowPads() const
Definition zone.h:815
PLACEMENT_SOURCE_T GetPlacementAreaSourceType() const
Definition zone.h:809
bool GetDoNotAllowTracks() const
Definition zone.h:814
bool IsFilled() const
Definition zone.h:310
ISLAND_REMOVAL_MODE GetIslandRemovalMode() const
Definition zone.h:824
SHAPE_POLY_SET * Outline()
Definition zone.h:422
bool IsIsland(PCB_LAYER_ID aLayer, int aPolyIdx) const
Check if a given filled polygon is an insulated island.
Definition zone.cpp:1483
long long int GetMinIslandArea() const
Definition zone.h:827
const wxString & GetZoneName() const
Definition zone.h:164
int GetMinThickness() const
Definition zone.h:319
ZONE_CONNECTION GetPadConnection() const
Definition zone.h:316
int GetHatchThickness() const
Definition zone.h:329
double GetHatchHoleMinArea() const
Definition zone.h:344
bool GetPlacementAreaEnabled() const
Definition zone.h:804
bool IsTeardropArea() const
Definition zone.h:777
int GetThermalReliefSpokeWidth() const
Definition zone.h:263
int GetBorderHatchPitch() const
HatchBorder related methods.
Definition zone.h:837
ZONE_BORDER_DISPLAY_STYLE GetHatchStyle() const
Definition zone.h:676
EDA_ANGLE GetHatchOrientation() const
Definition zone.h:335
bool GetDoNotAllowFootprints() const
Definition zone.h:816
ZONE_FILL_MODE GetFillMode() const
Definition zone.h:242
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:137
int GetHatchGap() const
Definition zone.h:332
TEARDROP_TYPE GetTeardropAreaType() const
Definition zone.h:788
double GetHatchSmoothingValue() const
Definition zone.h:341
bool GetDoNotAllowZoneFills() const
Definition zone.h:812
int GetHatchSmoothingLevel() const
Definition zone.h:338
unsigned int GetCornerRadius() const
Definition zone.h:747
int GetCornerSmoothingType() const
Definition zone.h:743
bool IsOnCopperLayer() const override
Definition zone.cpp:578
PCB_LAYER_ID GetFirstLayer() const
Definition zone.cpp:558
int GetThermalReliefGap() const
Definition zone.h:252
unsigned GetAssignedPriority() const
Definition zone.h:126
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition zone.h:606
This file is part of the common library.
#define CTL_OMIT_HYPERLINK
Omit the hyperlink attribute in .kicad_xxx files.
Definition ctl_flags.h:46
#define CTL_OMIT_UUIDS
Omit component unique ids (useless in library)
Definition ctl_flags.h:30
#define CTL_OMIT_FOOTPRINT_VERSION
Omit the version string from the (footprint) sexpr group.
Definition ctl_flags.h:39
#define CTL_OMIT_INITIAL_COMMENTS
Omit FOOTPRINT initial comments.
Definition ctl_flags.h:43
#define CTL_OMIT_LIBNAME
Omit lib alias when saving (used for board/not library).
Definition ctl_flags.h:37
#define CTL_OMIT_PATH
Omit component sheet time stamp (useless in library).
Definition ctl_flags.h:33
#define CTL_OMIT_AT
Omit position and rotation.
Definition ctl_flags.h:35
#define CTL_OMIT_PAD_NETS
Omit pads net names (useless in library).
Definition ctl_flags.h:29
#define CTL_OMIT_COLOR
Omit the color attribute in .kicad_xxx files.
Definition ctl_flags.h:45
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
static constexpr EDA_ANGLE ANGLE_45
Definition eda_angle.h:412
#define IGNORE_PARENT_GROUP
Definition eda_item.h:57
@ ELLIPSE
Definition eda_shape.h:56
@ SEGMENT
Definition eda_shape.h:50
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:51
@ ELLIPSE_ARC
Definition eda_shape.h:57
@ REVERSE_HATCH
Definition eda_shape.h:69
@ HATCH
Definition eda_shape.h:68
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:65
@ CROSS_HATCH
Definition eda_shape.h:70
EDA_DATA_TYPE
The type of unit.
Definition eda_units.h:38
@ FP_SMD
Definition footprint.h:86
@ FP_DNP
Definition footprint.h:91
@ FP_EXCLUDE_FROM_POS_FILES
Definition footprint.h:87
@ FP_BOARD_ONLY
Definition footprint.h:89
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:88
@ FP_THROUGH_HOLE
Definition footprint.h:85
@ EXPAND_INNER_LAYERS
The 'normal' stackup handling, where there is a single inner layer (In1) and rule areas using it expa...
Definition footprint.h:150
static const std::string KiCadFootprintFileExtension
const wxChar *const traceKicadPcbPlugin
Flag to enable KiCad PCB plugin debug output.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
#define MAX_CU_LAYERS
Definition layer_ids.h:176
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:679
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition layer_ids.h:690
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_CrtYd
Definition layer_ids.h:116
@ B_Adhes
Definition layer_ids.h:103
@ F_Paste
Definition layer_ids.h:104
@ F_Adhes
Definition layer_ids.h:102
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ F_Mask
Definition layer_ids.h:97
@ B_Paste
Definition layer_ids.h:105
@ UNSELECTED_LAYER
Definition layer_ids.h:62
@ F_Fab
Definition layer_ids.h:119
@ F_SilkS
Definition layer_ids.h:100
@ B_CrtYd
Definition layer_ids.h:115
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ User_1
Definition layer_ids.h:124
@ B_SilkS
Definition layer_ids.h:101
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:171
@ F_Cu
Definition layer_ids.h:64
@ B_Fab
Definition layer_ids.h:118
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:96
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:29
KICOMMON_API std::string FormatAngle(const EDA_ANGLE &aAngle)
Convert aAngle from board units to a string appropriate for writing to file.
KICOMMON_API std::string FormatInternalUnits(const EDA_IU_SCALE &aIuScale, int aValue, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
Converts aValue from internal units to a string appropriate for writing to file.
void FormatOptBool(OUTPUTFORMATTER *aOut, const wxString &aKey, std::optional< bool > aValue)
Writes an optional boolean to the formatter.
void FormatUuid(OUTPUTFORMATTER *aOut, const KIID &aUuid)
void FormatStreamData(OUTPUTFORMATTER &aOut, const wxStreamBuffer &aStream)
Write binary data to the formatter as base 64 encoded string.
void FormatBool(OUTPUTFORMATTER *aOut, const wxString &aKey, bool aValue)
Writes a boolean to the formatter, in the style (aKey [yes|no])
long long TimestampDir(const wxString &aDirPath, const wxString &aFilespec)
Computes a hash of modification times and sizes for files matching a pattern.
Definition unix/io.cpp:123
constexpr char APPEND_PRESERVE_DESTINATION_STACKUP[]
Definition pcb_io.h:47
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:99
@ PTH
Plated through hole pad.
Definition padstack.h:98
@ CONN
Like smd, does not appear on the solder paste layer (default) Note: also has a special attribute in G...
Definition padstack.h:100
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
@ TRAPEZOID
Definition padstack.h:56
@ RECTANGLE
Definition padstack.h:54
@ FIDUCIAL_LOCAL
a fiducial (usually a smd) local to the parent footprint
Definition padstack.h:118
@ FIDUCIAL_GLBL
a fiducial (usually a smd) for the full board
Definition padstack.h:117
@ MECHANICAL
a pad used for mechanical support
Definition padstack.h:122
@ PRESSFIT
a PTH with a hole diameter with tight tolerances for press fit pin
Definition padstack.h:123
@ HEATSINK
a pad used as heat sink, usually in SMD footprints
Definition padstack.h:120
@ NONE
no special fabrication property
Definition padstack.h:115
@ TESTPOINT
a test point pad
Definition padstack.h:119
@ CASTELLATED
a pad with a castellated through hole
Definition padstack.h:121
@ BGA
Smd pad, used in BGA footprints.
Definition padstack.h:116
BARCODE class definition.
Class to handle a set of BOARD_ITEMs.
bool isDefaultTeardropParameters(const TEARDROP_PARAMETERS &tdParams)
std::string formatInternalUnits(const int aValue, const EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
#define SEXPR_BOARD_FILE_VERSION
Current s-expression file format version. 2 was the last legacy format version.
#define CTL_FOR_BOARD
The zero arg constructor when PCB_PLUGIN is used for PLUGIN::Load() and PLUGIN::Save()ing a BOARD fil...
#define CTL_FOR_LIBRARY
Format output for a footprint library instead of clipboard or BOARD.
Pcbnew s-expression file format parser definition.
#define UNDEFINED_DRILL_DIAMETER
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
@ RPT_SEVERITY_WARNING
std::string FormatDouble2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 This function is intended in...
bool ReplaceIllegalFileNameChars(std::string &aName, int aReplaceChar)
Checks aName for illegal file name characters.
int ValueStringCompare(const wxString &strFWord, const wxString &strSWord)
Compare strings like the strcmp function but handle numbers and modifiers within the string text corr...
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
PCB_LAYER_ID start
Definition padstack.h:269
PCB_LAYER_ID end
Definition padstack.h:270
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
Definition padstack.h:267
std::optional< bool > is_capped
True if the drill hole should be capped.
Definition padstack.h:273
std::optional< bool > is_filled
True if the drill hole should be filled completely.
Definition padstack.h:272
std::optional< bool > has_covering
True if the pad on this side should have covering.
Definition padstack.h:257
std::optional< bool > has_solder_mask
True if this outer layer has mask (is not tented)
Definition padstack.h:255
std::optional< bool > has_plugging
True if the drill hole should be plugged on this side.
Definition padstack.h:258
A filename or source description, a problem input line, a line number, a byte offset,...
Parameters that drive copper-thieving fill generation.
EDA_ANGLE orientation
THIEVING_PATTERN pattern
std::optional< VECTOR2I > hatching_offset
nlohmann::json output
VECTOR2I center
const SHAPE_LINE_CHAIN chain
int delta
wxLogTrace helper definitions.
#define kv
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
@ PCB_T
Definition typeinfo.h:79
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:85
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:103
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:100
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:94
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:101
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:108
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:90
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:105
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:89
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:86
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:87
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:98
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:104
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:92
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:83
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:99
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:84
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:95
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:91
@ PCB_POINT_T
class PCB_POINT, a 0-dimensional point
Definition typeinfo.h:110
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:93
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:102
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
Definition of file extensions used in Kicad.
@ THERMAL
Use thermal relief for pads.
Definition zones.h:50
@ THT_THERMAL
Thermal relief only for THT pads.
Definition zones.h:52
@ NONE
Pads are not covered.
Definition zones.h:49
@ FULL
pads are covered by copper
Definition zones.h:51