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