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