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, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <wx/dir.h>
22#include <wx/ffile.h>
23#include <wx/log.h>
24#include <wx/msgdlg.h>
25#include <wx/mstream.h>
26
27#include <board.h>
29#include <callback_gal.h>
31#include <confirm.h>
32#include <convert_basic_shapes_to_polygon.h> // for enum RECT_CHAMFER_POSITIONS definition
33#include <fmt/core.h>
34#include <font/fontconfig.h>
35#include <footprint.h>
36#include <gestfich.h>
38#include <kiface_base.h>
39#include <kiplatform/io.h>
40#include <layer_range.h>
41#include <macros.h>
42#include <pad.h>
43#include <pcb_dimension.h>
44#include <pcb_generator.h>
45#include <pcb_grid_item.h>
46#include <pcb_group.h>
50#include <pcb_point.h>
51#include <pcb_reference_image.h>
52#include <pcb_barcode.h>
53#include <pcb_shape.h>
54#include <pcb_drill_chart.h>
55#include <pcb_drill_map.h>
56#include <pcb_table.h>
57#include <pcb_tablecell.h>
58#include <pcb_target.h>
59#include <pcb_text.h>
60#include <pcb_textbox.h>
61#include <pcb_track.h>
62#include <pcbnew_settings.h>
63#include <pgm_base.h>
64#include <progress_reporter.h>
65#include <reporter.h>
66#include <string_utils.h>
67#include <trace_helpers.h>
69#include <zone.h>
70
71#include <build_version.h>
72#include <filter_reader.h>
73#include <ctl_flags.h>
74
75
76using namespace PCB_KEYS_T;
77
78
79FP_CACHE_ENTRY::FP_CACHE_ENTRY( FOOTPRINT* aFootprint, const WX_FILENAME& aFileName ) :
80 m_filename( aFileName ),
81 m_footprint( aFootprint )
82{ }
83
84
85FP_CACHE::FP_CACHE( PCB_IO_KICAD_SEXPR* aOwner, const wxString& aLibraryPath )
86{
87 m_owner = aOwner;
88 m_lib_raw_path = aLibraryPath;
89 m_lib_path.SetPath( aLibraryPath );
91 m_cache_dirty = true;
92}
93
94
95void FP_CACHE::Save( FOOTPRINT* aFootprintFilter )
96{
98
99 if( !m_lib_path.DirExists() && !m_lib_path.Mkdir() )
100 THROW_IO_ERRORF( _( "Cannot create footprint library '%s'." ), m_lib_raw_path );
101
102 if( !m_lib_path.IsDirWritable() )
103 THROW_IO_ERRORF( _( "Footprint library '%s' is read only." ), m_lib_raw_path );
104
105 for( auto it = m_footprints.begin(); it != m_footprints.end(); ++it )
106 {
107 FP_CACHE_ENTRY* fpCacheEntry = it->second;
108 std::unique_ptr<FOOTPRINT>& footprint = fpCacheEntry->GetFootprint();
109
110 if( aFootprintFilter && footprint.get() != aFootprintFilter )
111 continue;
112
113 // If we've requested to embed the fonts in the footprint, do so. Otherwise, clear the
114 // embedded fonts from the footprint. Embedded fonts will be used if available.
115 if( footprint->GetAreFontsEmbedded() )
116 footprint->EmbedFonts();
117 else
118 footprint->GetEmbeddedFiles()->ClearEmbeddedFonts();
119
120 WX_FILENAME fn = fpCacheEntry->GetFileName();
121 wxString fileName = fn.GetFullPath();
122
123 // Allow file output stream to go out of scope to close the file stream before
124 // renaming the file.
125 {
126 wxLogTrace( traceKicadPcbPlugin, wxT( "Writing library file '%s'." ),
127 fileName );
128
129 PRETTIFIED_FILE_OUTPUTFORMATTER formatter( fileName );
130
131 m_owner->SetOutputFormatter( &formatter );
132 m_owner->Format( footprint.get() );
133 formatter.Finish();
134 }
135
137 }
138
139 if( m_lib_path.IsFileReadable() && m_lib_path.GetModificationTime().IsValid() )
140 m_cache_timestamp += m_lib_path.GetModificationTime().GetValue().GetValue();
141
142 // If we've saved the full cache, we clear the dirty flag.
143 if( !aFootprintFilter )
144 m_cache_dirty = false;
145}
146
147
149{
150 m_cache_dirty = false;
152
153 wxDir dir( m_lib_raw_path );
154
155 if( !dir.IsOpened() )
156 THROW_IO_ERRORF( _( "Footprint library '%s' not found." ), m_lib_raw_path );
157
158 wxString fullName;
159 wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::KiCadFootprintFileExtension );
160
161 // wxFileName construction is egregiously slow. Construct it once and just swap out
162 // the filename thereafter.
163 WX_FILENAME fn( m_lib_raw_path, wxT( "dummyName" ) );
164
165 if( dir.GetFirst( &fullName, fileSpec ) )
166 {
167 wxString cacheError;
168
169 do
170 {
171 fn.SetFullName( fullName );
172
173 // Queue I/O errors so only files that fail to parse don't get loaded.
174 try
175 {
176 FILE_LINE_READER reader( fn.GetFullPath() );
177 PCB_IO_KICAD_SEXPR_PARSER parser( &reader, nullptr, nullptr );
178
179 FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( parser.Parse() );
180 wxString fpName = fn.GetName();
181
182 if( !footprint )
183 THROW_IO_ERROR( wxEmptyString ); // caught locally, just below...
184
185 footprint->SetFPID( LIB_ID( wxEmptyString, fpName ) );
186 m_footprints.insert( fpName, new FP_CACHE_ENTRY( footprint, fn ) );
187
188 // Collect any non-fatal parse warnings
189 for( const wxString& warning : parser.GetParseWarnings() )
190 {
191 if( !cacheError.IsEmpty() )
192 cacheError += wxT( "\n\n" );
193
194 cacheError += wxString::Format( _( "Warning in file '%s'" ) + '\n',
195 fn.GetFullPath() );
196 cacheError += warning;
197 }
198 }
199 catch( const IO_ERROR& ioe )
200 {
201 if( !cacheError.IsEmpty() )
202 cacheError += wxT( "\n\n" );
203
204 cacheError += wxString::Format( _( "Unable to read file '%s'" ) + '\n',
205 fn.GetFullPath() );
206 cacheError += ioe.What();
207 }
208 } while( dir.GetNext( &fullName ) );
209
211
212 if( !cacheError.IsEmpty() )
213 THROW_IO_ERROR( cacheError );
214 }
215}
216
217
218void FP_CACHE::Remove( const wxString& aFootprintName )
219{
220 auto it = m_footprints.find( aFootprintName );
221
222 if( it == m_footprints.end() )
223 {
224 THROW_IO_ERRORF( _( "Library '%s' has no footprint '%s'." ),
226 aFootprintName );
227 }
228
229 // Remove the footprint from the cache and delete the footprint file from the library.
230 wxString fullPath = it->second->GetFileName().GetFullPath();
231 m_footprints.erase( aFootprintName );
232 wxRemoveFile( fullPath );
233}
234
235
236bool FP_CACHE::IsPath( const wxString& aPath ) const
237{
238 return aPath == m_lib_raw_path;
239}
240
241
242void FP_CACHE::SetPath( const wxString& aPath )
243{
244 m_lib_raw_path = aPath;
245 m_lib_path.SetPath( aPath );
246
247
248 for( const auto& footprint : GetFootprints() )
249 footprint.second->SetFilePath( aPath );
250}
251
252
254{
256
257 return m_cache_dirty;
258}
259
260
261long long FP_CACHE::GetTimestamp( const wxString& aLibPath )
262{
263 wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::KiCadFootprintFileExtension );
264
265 return KIPLATFORM::IO::TimestampDir( aLibPath, fileSpec );
266}
267
268
269bool PCB_IO_KICAD_SEXPR::CanReadBoard( const wxString& aFileName ) const
270{
271 if( !PCB_IO::CanReadBoard( aFileName ) )
272 return false;
273
274 try
275 {
276 FILE_LINE_READER reader( aFileName );
277 PCB_IO_KICAD_SEXPR_PARSER parser( &reader, nullptr, m_queryUserCallback );
278
279 return parser.IsValidBoardHeader();
280 }
281 catch( const IO_ERROR& )
282 {
283 }
284
285 return false;
286}
287
288
289void PCB_IO_KICAD_SEXPR::SaveBoard( const wxString& aFileName, BOARD& aBoard,
290 const std::map<std::string, UTF8>* aProperties )
291{
292 wxString sanityResult = aBoard.GroupsSanityCheck();
293
294 if( sanityResult != wxEmptyString && m_queryUserCallback )
295 {
297 _( "Internal Group Data Error" ), wxICON_ERROR,
298 wxString::Format( _( "Please report this bug. Error validating group "
299 "structure: %s\n\nSave anyway?" ), sanityResult ),
300 _( "Save Anyway" ) ) )
301 {
302 return;
303 }
304 }
305
306 PRETTIFIED_FILE_OUTPUTFORMATTER formatter( aFileName );
307 FormatBoardToFormatter( &formatter, &aBoard, aProperties );
308 formatter.Finish();
309}
310
311
313 const std::map<std::string, UTF8>* aProperties )
314{
315 init( aProperties );
316
317 m_board = aBoard; // after init()
318
319 // If the user wants fonts embedded, make sure that they are added to the board. Otherwise,
320 // remove any fonts that were previously embedded.
321 if( m_board->GetAreFontsEmbedded() )
322 m_board->EmbedFonts();
323 else
324 m_board->GetEmbeddedFiles()->ClearEmbeddedFonts();
325
326 m_out = aOut;
327
328 m_out->Print( "(kicad_pcb (version %d) (generator \"pcbnew\") (generator_version %s)", SEXPR_BOARD_FILE_VERSION,
329 m_out->Quotew( GetMajorMinorVersion() ).c_str() );
330
331 Format( aBoard );
332
333 m_out->Print( ")" );
334
335 m_out = nullptr;
336}
337
338
339BOARD_ITEM* PCB_IO_KICAD_SEXPR::Parse( const wxString& aClipboardSourceInput )
340{
341 std::string input = TO_UTF8( aClipboardSourceInput );
342
343 STRING_LINE_READER reader( input, wxT( "clipboard" ) );
344 PCB_IO_KICAD_SEXPR_PARSER parser( &reader, nullptr, m_queryUserCallback );
345
346 try
347 {
348 return parser.Parse();
349 }
350 catch( const PARSE_ERROR& parse_error )
351 {
352 if( parser.IsTooRecent() )
353 throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
354 else
355 throw;
356 }
357}
358
359
360void PCB_IO_KICAD_SEXPR::Format( const BOARD_ITEM* aItem ) const
361{
362 switch( aItem->Type() )
363 {
364 case PCB_T:
365 format( static_cast<const BOARD*>( aItem ) );
366 break;
367
369 case PCB_DIM_CENTER_T:
370 case PCB_DIM_RADIAL_T:
372 case PCB_DIM_LEADER_T:
373 format( static_cast<const PCB_DIMENSION_BASE*>( aItem ) );
374 break;
375
376 case PCB_SHAPE_T:
377 format( static_cast<const PCB_SHAPE*>( aItem ) );
378 break;
379
381 format( static_cast<const PCB_REFERENCE_IMAGE*>( aItem ) );
382 break;
383
384 case PCB_POINT_T:
385 format( static_cast<const PCB_POINT*>( aItem ) );
386 break;
387
388 case PCB_TARGET_T:
389 format( static_cast<const PCB_TARGET*>( aItem ) );
390 break;
391
392 case PCB_GRID_ITEM_T:
393 format( static_cast<const PCB_GRID_ITEM*>( aItem ) );
394 break;
395
396 case PCB_FOOTPRINT_T:
397 format( static_cast<const FOOTPRINT*>( aItem ) );
398 break;
399
400 case PCB_PAD_T:
401 format( static_cast<const PAD*>( aItem ) );
402 break;
403
404 case PCB_FIELD_T:
405 // Handled in the footprint formatter when properties are formatted
406 break;
407
408 case PCB_TEXT_T:
409 format( static_cast<const PCB_TEXT*>( aItem ) );
410 break;
411
412 case PCB_TEXTBOX_T:
413 format( static_cast<const PCB_TEXTBOX*>( aItem ) );
414 break;
415
416 case PCB_BARCODE_T:
417 format( static_cast<const PCB_BARCODE*>( aItem ) );
418 break;
419
420 case PCB_TABLE_T:
421 format( static_cast<const PCB_TABLE*>( aItem ) );
422 break;
423
425 format( static_cast<const PCB_GENERATED_TABLE*>( aItem ) );
426 break;
427
428 case PCB_DRILL_MAP_T:
429 format( static_cast<const PCB_DRILL_MAP*>( aItem ) );
430 break;
431
432 case PCB_GROUP_T:
433 format( static_cast<const PCB_GROUP*>( aItem ) );
434 break;
435
436 case PCB_GENERATOR_T:
437 format( static_cast<const PCB_GENERATOR*>( aItem ) );
438 break;
439
440 case PCB_CONSTRAINT_T:
441 format( static_cast<const PCB_CONSTRAINT*>( aItem ) );
442 break;
443
444 case PCB_TRACE_T:
445 case PCB_ARC_T:
446 case PCB_VIA_T:
447 format( static_cast<const PCB_TRACK*>( aItem ) );
448 break;
449
450 case PCB_ZONE_T:
451 format( static_cast<const ZONE*>( aItem ) );
452 break;
453
454 default:
455 wxFAIL_MSG( wxT( "Cannot format item " ) + aItem->GetClass() );
456 }
457}
458
459
460std::string formatInternalUnits( const int aValue, const EDA_DATA_TYPE aDataType = EDA_DATA_TYPE::DISTANCE )
461{
462 return EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aValue, aDataType );
463}
464
465
466std::string formatInternalUnits( const VECTOR2I& aCoord )
467{
469}
470
471
472std::string formatInternalUnits( const VECTOR2I& aCoord, const FOOTPRINT* aParentFP )
473{
474 if( aParentFP )
475 {
476 return formatInternalUnits( aParentFP->GetTransform().InverseApply( aCoord ) );
477 }
478
479 return formatInternalUnits( aCoord );
480}
481
482
483static VECTOR2I unbakeSize( const VECTOR2I& aSize, const FOOTPRINT* aParentFP )
484{
485 if( !aParentFP )
486 return aSize;
487
488 const TRANSFORM_TRS& xform = aParentFP->GetTransform();
489 return { KiROUND( aSize.x / xform.GetScaleX() ), KiROUND( aSize.y / xform.GetScaleY() ) };
490}
491
492
493static VECTOR2I unbakeSizeUniform( const VECTOR2I& aSize, const FOOTPRINT* aParentFP )
494{
495 if( !aParentFP )
496 return aSize;
497
498 const TRANSFORM_TRS& xform = aParentFP->GetTransform();
499 double avg = ( xform.GetScaleX() + xform.GetScaleY() ) * 0.5;
500 return { KiROUND( aSize.x / avg ), KiROUND( aSize.y / avg ) };
501}
502
503
504static int unbakeLinear( int aValue, const FOOTPRINT* aParentFP )
505{
506 if( !aParentFP )
507 return aValue;
508
509 const TRANSFORM_TRS& xform = aParentFP->GetTransform();
510 double avg = ( xform.GetScaleX() + xform.GetScaleY() ) * 0.5;
511 return KiROUND( aValue / avg );
512}
513
514
515void PCB_IO_KICAD_SEXPR::formatLayer( PCB_LAYER_ID aLayer, bool aIsKnockout ) const
516{
517 m_out->Print( "(layer %s %s)",
518 m_out->Quotew( LSET::Name( aLayer ) ).c_str(),
519 aIsKnockout ? "knockout" : "" );
520}
521
522
524 const FOOTPRINT* aParentFP ) const
525{
526 m_out->Print( "(pts" );
527
528 for( int ii = 0; ii < outline.PointCount(); ++ii )
529 {
530 int ind = outline.ArcIndex( ii );
531
532 if( ind < 0 )
533 {
534 m_out->Print( "(xy %s)",
535 formatInternalUnits( outline.CPoint( ii ), aParentFP ).c_str() );
536 }
537 else
538 {
539 const SHAPE_ARC& arc = outline.Arc( ind );
540 m_out->Print( "(arc (start %s) (mid %s) (end %s))",
541 formatInternalUnits( arc.GetP0(), aParentFP ).c_str(),
542 formatInternalUnits( arc.GetArcMid(), aParentFP ).c_str(),
543 formatInternalUnits( arc.GetP1(), aParentFP ).c_str() );
544
545 do
546 {
547 ++ii;
548 } while( ii < outline.PointCount() && outline.ArcIndex( ii ) == ind );
549
550 --ii;
551 }
552 }
553
554 m_out->Print( ")" );
555}
556
557
559{
560 wxString resolvedText( aText->GetShownText( FOR_CANVAS ) );
561 std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = aText->GetRenderCache( aText->GetFont(),
562 resolvedText );
563
564 m_out->Print( "(render_cache %s %s",
565 m_out->Quotew( resolvedText ).c_str(),
566 EDA_UNIT_UTILS::FormatAngle( aText->GetDrawRotation() ).c_str() );
567
569
570 CALLBACK_GAL callback_gal( empty_opts,
571 // Polygon callback
572 [&]( const SHAPE_LINE_CHAIN& aPoly )
573 {
574 m_out->Print( "(polygon" );
575 formatPolyPts( aPoly );
576 m_out->Print( ")" );
577 } );
578
579 callback_gal.SetLineWidth( aText->GetTextThickness() );
580 callback_gal.DrawGlyphs( *cache );
581
582 m_out->Print( ")" );
583}
584
585
586void PCB_IO_KICAD_SEXPR::formatSetup( const BOARD* aBoard ) const
587{
588 // Setup
589 m_out->Print( "(setup" );
590
591 // Save the board physical stackup structure
592 const BOARD_STACKUP& stackup = aBoard->GetDesignSettings().GetStackupDescriptor();
593
594 if( aBoard->GetDesignSettings().m_HasStackup )
595 stackup.FormatBoardStackup( m_out, aBoard );
596
598
599 m_out->Print( "(pad_to_mask_clearance %s)", formatInternalUnits( bds.m_SolderMaskExpansion ).c_str() );
600
601 if( bds.m_SolderMaskMinWidth )
602 m_out->Print( "(solder_mask_min_width %s)", formatInternalUnits( bds.m_SolderMaskMinWidth ).c_str() );
603
604 if( bds.m_SolderPasteMargin != 0 )
605 m_out->Print( "(pad_to_paste_clearance %s)", formatInternalUnits( bds.m_SolderPasteMargin ).c_str() );
606
607 if( bds.m_SolderPasteMarginRatio != 0 )
608 m_out->Print( "(pad_to_paste_clearance_ratio %s)", FormatDouble2Str( bds.m_SolderPasteMarginRatio ).c_str() );
609
610 KICAD_FORMAT::FormatBool( m_out, "allow_soldermask_bridges_in_footprints", bds.m_AllowSoldermaskBridgesInFPs );
611
612 m_out->Print( 0, " (tenting " );
615 m_out->Print( 0, ")" );
616
617 m_out->Print( 0, " (covering " );
620 m_out->Print( 0, ")" );
621
622 m_out->Print( 0, " (plugging " );
625 m_out->Print( 0, ")" );
626
627 KICAD_FORMAT::FormatBool( m_out, "capping", bds.m_CapVias );
628
629 KICAD_FORMAT::FormatBool( m_out, "filling", bds.m_FillVias );
630
631 if( !bds.m_ZoneLayerProperties.empty() )
632 {
633 m_out->Print( 0, " (zone_defaults" );
634
635 for( const auto& [layer, properties] : bds.m_ZoneLayerProperties )
636 format( properties, 0, layer );
637
638 m_out->Print( 0, ")\n" );
639 }
640
641 VECTOR2I origin = bds.GetAuxOrigin();
642
643 if( origin != VECTOR2I( 0, 0 ) )
644 {
645 m_out->Print( "(aux_axis_origin %s %s)",
646 formatInternalUnits( origin.x ).c_str(),
647 formatInternalUnits( origin.y ).c_str() );
648 }
649
650 origin = bds.GetGridOrigin();
651
652 if( origin != VECTOR2I( 0, 0 ) )
653 {
654 m_out->Print( "(grid_origin %s %s)",
655 formatInternalUnits( origin.x ).c_str(),
656 formatInternalUnits( origin.y ).c_str() );
657 }
658
660
661 aBoard->GetPlotOptions().Format( m_out );
662
663 m_out->Print( ")" );
664}
665
666
669static void formatDrillSpan( OUTPUTFORMATTER* aOut, const DRILL_SPAN& aSpan )
670{
671 aOut->Print( "%s %s", aOut->Quotew( LSET::Name( aSpan.DrillStartLayer() ) ).c_str(),
672 aOut->Quotew( LSET::Name( aSpan.DrillEndLayer() ) ).c_str() );
673
674 if( aSpan.m_IsBackdrill )
675 aOut->Print( " backdrill" );
676
677 if( aSpan.m_IsNonPlatedFile )
678 aOut->Print( " npth" );
679}
680
681
683{
684 const DRILL_SYMBOL_PROFILE& profile = aSettings.GetDrillSymbolProfile();
685 const DRILL_SYMBOL_PROFILE defaults;
686
687 // A board nobody has configured writes nothing, so existing files keep their bytes
688 if( profile == defaults )
689 {
690 return;
691 }
692
693 m_out->Print( "(drill_symbol_profile" );
694
695 if( !profile.GetName().IsEmpty() )
696 m_out->Print( "(name %s)", m_out->Quotew( profile.GetName() ).c_str() );
697
698 m_out->Print( "(group_by" );
699
700 for( DRILL_GROUP_KEY key : profile.GroupKeys() )
701 m_out->Print( " %s", DrillGroupKeyToken( key ) );
702
703 m_out->Print( ")" );
704
705 m_out->Print( "(default_marks %s)", DrillMarkPolicyToken( profile.GetMarkPolicy() ) );
706 m_out->Print( "(size %s)", formatInternalUnits( profile.GetSymbolSize() ).c_str() );
707 m_out->Print( "(width %s)", formatInternalUnits( profile.GetSymbolWidth() ).c_str() );
708 KICAD_FORMAT::FormatBool( m_out, "freeze_assignments", profile.GetFreezeAssignments() );
709
710
711 for( const auto& [key, assignment] : profile.Assignments() )
712 {
713 m_out->Print( "(assignment (key %s) (mark %s",
714 m_out->Quotew( wxString::FromUTF8( key ) ).c_str(),
715 DrillMarkModeToken( assignment.m_MarkMode ) );
716
717 if( assignment.m_MarkMode == DRILL_MARK_MODE::SHAPE )
718 m_out->Print( " %d", assignment.m_ShapeIndex );
719 else if( assignment.m_MarkMode == DRILL_MARK_MODE::LETTER )
720 m_out->Print( " %s", m_out->Quotew( assignment.m_Letter ).c_str() );
721
722 m_out->Print( ")" );
723
724 if( !assignment.m_Description.IsEmpty() )
725 m_out->Print( "(descr %s)", m_out->Quotew( assignment.m_Description ).c_str() );
726
727 m_out->Print( ")" );
728 }
729
730 m_out->Print( ")" );
731}
732
733
734void PCB_IO_KICAD_SEXPR::formatGeneral( const BOARD* aBoard ) const
735{
736 const BOARD_DESIGN_SETTINGS& bds = aBoard->GetDesignSettings();
737
738 m_out->Print( "(general" );
739
740 m_out->Print( "(thickness %s)", formatInternalUnits( bds.GetBoardThickness() ).c_str() );
741
742 KICAD_FORMAT::FormatBool( m_out, "legacy_teardrops", aBoard->LegacyTeardrops() );
743
744 m_out->Print( ")" );
745
746 aBoard->GetPageSettings().Format( m_out );
747 aBoard->GetTitleBlock().Format( m_out );
748}
749
750
752{
753 m_out->Print( "(layers" );
754
755 // Save only the used copper layers from front to back.
756
757 for( PCB_LAYER_ID layer : aBoard->GetEnabledLayers().CuStack() )
758 {
759 m_out->Print( "(%d %s %s %s)",
760 layer,
761 m_out->Quotew( LSET::Name( layer ) ).c_str(),
762 LAYER::ShowType( aBoard->GetLayerType( layer ) ),
763 LSET::Name( layer ) == m_board->GetLayerName( layer )
764 ? ""
765 : m_out->Quotew( m_board->GetLayerName( layer ) ).c_str() );
766
767 }
768
769 // Save used non-copper layers in the order they are defined.
770 LSEQ seq = aBoard->GetEnabledLayers().TechAndUserUIOrder();
771
772 for( PCB_LAYER_ID layer : seq )
773 {
774 bool print_type = false;
775
776 // User layers (layer id >= User_1) have a qualifier
777 // default is "user", but other qualifiers exist
778 if( layer >= User_1 )
779 {
780 if( IsCopperLayer( layer ) )
781 print_type = true;
782
783 if( aBoard->GetLayerType( layer ) == LT_FRONT
784 || aBoard->GetLayerType( layer ) == LT_BACK )
785 print_type = true;
786 }
787
788 m_out->Print( "(%d %s %s %s)",
789 layer,
790 m_out->Quotew( LSET::Name( layer ) ).c_str(),
791 print_type
792 ? LAYER::ShowType( aBoard->GetLayerType( layer ) )
793 : "user",
794 m_board->GetLayerName( layer ) == LSET::Name( layer )
795 ? ""
796 : m_out->Quotew( m_board->GetLayerName( layer ) ).c_str() );
797 }
798
799 m_out->Print( ")" );
800}
801
802
804{
805 for( const std::pair<const wxString, wxString>& prop : aBoard->GetProperties() )
806 {
807 m_out->Print( "(property %s %s)",
808 m_out->Quotew( prop.first ).c_str(),
809 m_out->Quotew( prop.second ).c_str() );
810 }
811}
812
813
814void PCB_IO_KICAD_SEXPR::formatVariants( const BOARD* aBoard ) const
815{
816 const std::vector<wxString>& variantNames = aBoard->GetVariantNames();
817
818 if( variantNames.empty() )
819 return;
820
821 m_out->Print( "(variants" );
822
823 for( const wxString& variantName : variantNames )
824 {
825 m_out->Print( "(variant (name %s)", m_out->Quotew( variantName ).c_str() );
826
827 wxString description = aBoard->GetVariantDescription( variantName );
828
829 if( !description.IsEmpty() )
830 m_out->Print( "(description %s)", m_out->Quotew( description ).c_str() );
831
832 m_out->Print( ")" );
833 }
834
835 m_out->Print( ")" );
836}
837
838
839void PCB_IO_KICAD_SEXPR::formatHeader( const BOARD* aBoard ) const
840{
841 formatGeneral( aBoard );
842
843 // Layers list.
844 formatBoardLayers( aBoard );
845
846 // Setup
847 formatSetup( aBoard );
848
849 // Properties
850 formatProperties( aBoard );
851
852 // Variants
853 formatVariants( aBoard );
854}
855
856
858{
859 static const TEARDROP_PARAMETERS defaults;
860
861 return tdParams.m_Enabled == defaults.m_Enabled
862 && tdParams.m_BestLengthRatio == defaults.m_BestLengthRatio
863 && tdParams.m_TdMaxLen == defaults.m_TdMaxLen
864 && tdParams.m_BestWidthRatio == defaults.m_BestWidthRatio
865 && tdParams.m_TdMaxWidth == defaults.m_TdMaxWidth
866 && tdParams.m_CurvedEdges == defaults.m_CurvedEdges
868 && tdParams.m_AllowUseTwoTracks == defaults.m_AllowUseTwoTracks
869 && tdParams.m_TdOnPadsInZones == defaults.m_TdOnPadsInZones;
870}
871
872
874{
875 m_out->Print( "(teardrops (best_length_ratio %s) (max_length %s) (best_width_ratio %s) "
876 "(max_width %s)",
877 FormatDouble2Str( tdParams.m_BestLengthRatio ).c_str(),
878 formatInternalUnits( tdParams.m_TdMaxLen ).c_str(),
879 FormatDouble2Str( tdParams.m_BestWidthRatio ).c_str(),
880 formatInternalUnits( tdParams.m_TdMaxWidth ).c_str() );
881
882 KICAD_FORMAT::FormatBool( m_out, "curved_edges", tdParams.m_CurvedEdges );
883
884 m_out->Print( "(filter_ratio %s)",
885 FormatDouble2Str( tdParams.m_WidthtoSizeFilterRatio ).c_str() );
886
887 KICAD_FORMAT::FormatBool( m_out, "enabled", tdParams.m_Enabled );
888 KICAD_FORMAT::FormatBool( m_out, "allow_two_segments", tdParams.m_AllowUseTwoTracks );
889 KICAD_FORMAT::FormatBool( m_out, "prefer_zone_connections", !tdParams.m_TdOnPadsInZones );
890 m_out->Print( ")" );
891}
892
893
894void PCB_IO_KICAD_SEXPR::format( const BOARD* aBoard ) const
895{
896 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_footprints( aBoard->Footprints().begin(),
897 aBoard->Footprints().end() );
898 std::set<BOARD_ITEM*, BOARD::cmp_drawings> sorted_drawings( aBoard->Drawings().begin(),
899 aBoard->Drawings().end() );
900 std::set<PCB_TRACK*, PCB_TRACK::cmp_tracks> sorted_tracks( aBoard->Tracks().begin(),
901 aBoard->Tracks().end() );
902 std::set<PCB_POINT*, PCB_POINT::cmp_points> sorted_points( aBoard->Points().begin(),
903 aBoard->Points().end() );
904 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_zones( aBoard->Zones().begin(),
905 aBoard->Zones().end() );
906 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_groups( aBoard->Groups().begin(),
907 aBoard->Groups().end() );
908 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_generators( aBoard->Generators().begin(),
909 aBoard->Generators().end() );
910 std::set<BOARD_ITEM*, BOARD_ITEM::ptr_cmp> sorted_constraints( aBoard->Constraints().begin(),
911 aBoard->Constraints().end() );
912 formatHeader( aBoard );
913
914 // Save the footprints.
915 for( BOARD_ITEM* footprint : sorted_footprints )
916 Format( footprint );
917
918 // Save the graphical items on the board (not owned by a footprint)
919 for( BOARD_ITEM* item : sorted_drawings )
920 Format( item );
921
922 // Save the points
923 for( PCB_POINT* point : sorted_points )
924 Format( point );
925
926 // Do not save PCB_MARKERs, they can be regenerated easily.
927
928 // Save the tracks and vias.
929 for( PCB_TRACK* track : sorted_tracks )
930 Format( track );
931
932 // Save the polygon (which are the newer technology) zones.
933 for( auto zone : sorted_zones )
934 Format( zone );
935
936 // Save the groups
937 for( BOARD_ITEM* group : sorted_groups )
938 Format( group );
939
940 // Save the generators
941 for( BOARD_ITEM* gen : sorted_generators )
942 Format( gen );
943
944 // Save the geometric constraints last, after every item they may reference.
945 for( BOARD_ITEM* constraint : sorted_constraints )
946 Format( constraint );
947
948 // After writing all items, write the aggregated net chains section (if any)
949 struct CHAIN_INFO
950 {
951 std::vector<NETINFO_ITEM*> nets;
952 PAD* pads[2] = { nullptr, nullptr };
953 };
954
955 // Simple lexicographic ordering using ValueStringCompare comparator logic
956 auto cmp = []( const wxString& a, const wxString& b )
957 {
958 return ValueStringCompare( a, b ) < 0;
959 };
960
961 std::map<wxString, CHAIN_INFO, decltype( cmp )> chains( cmp );
962
963 for( NETINFO_ITEM* net : aBoard->GetNetInfo() )
964 {
965 if( !net )
966 continue;
967
968 if( net->GetNetChain().IsEmpty() && !net->GetTerminalPad( 0 ) && !net->GetTerminalPad( 1 ) )
969 continue; // nothing to aggregate
970
971 wxString chainName = net->GetNetChain();
972
973 if( chainName.IsEmpty() && ( net->GetTerminalPad( 0 ) || net->GetTerminalPad( 1 ) ) )
974 chainName = net->GetNetname(); // synthetic name for unnamed terminal association
975
976 CHAIN_INFO& info = chains[chainName];
977 info.nets.push_back( net );
978 for( int i = 0; i < 2; ++i )
979 {
980 if( net->GetTerminalPad( i ) && !info.pads[i] )
981 info.pads[i] = net->GetTerminalPad( i );
982 }
983 }
984
985 size_t count = 0;
986 for( const auto& kv : chains )
987 {
988 const CHAIN_INFO& si = kv.second;
989 const wxString& chainName = kv.first;
990 // Persist if: multi-net OR terminal pads OR explicit (non-empty) chain name
991 if( si.nets.size() > 1 || si.pads[0] || si.pads[1] || !chainName.IsEmpty() )
992 ++count;
993 }
994
995 if( count )
996 {
997 m_out->Print( "(net_chains" );
998 for( const auto& kv : chains )
999 {
1000 const wxString& name = kv.first;
1001 const CHAIN_INFO& si = kv.second;
1002
1003 if( si.nets.size() == 1 && !si.pads[0] && !si.pads[1] && name.IsEmpty() )
1004 continue;
1005
1006 m_out->Print( " (net_chain (name %s)", m_out->Quotew( name ).c_str() );
1007 m_out->Print( " (members" );
1008 for( NETINFO_ITEM* n : si.nets )
1009 {
1010 m_out->Print( " (net %s)", m_out->Quotew( n->GetNetname() ).c_str() );
1011 }
1012 m_out->Print( ")" );
1013
1014 for( int i = 0; i < 2; ++i )
1015 {
1016 if( si.pads[i] )
1017 m_out->Print( " (terminal_pad %s)",
1018 m_out->Quotew( si.pads[i]->m_Uuid.AsString() ).c_str() );
1019 }
1020
1021 m_out->Print( ")" );
1022 }
1023 m_out->Print( ")" );
1024 }
1025
1026 // Save any embedded files
1027 // Consolidate the embedded models in footprints into a single map
1028 // to avoid duplicating the same model in the board file.
1029 EMBEDDED_FILES files_to_write;
1030
1031 for( auto& file : aBoard->GetEmbeddedFiles()->EmbeddedFileMap() )
1032 files_to_write.AddFile( file.second );
1033
1034 for( BOARD_ITEM* item : sorted_footprints )
1035 {
1036 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
1037
1038 for( auto& file : fp->GetEmbeddedFiles()->EmbeddedFileMap() )
1039 files_to_write.AddFile( file.second );
1040 }
1041
1042 m_out->Print( "(embedded_fonts %s)",
1043 aBoard->GetEmbeddedFiles()->GetAreFontsEmbedded() ? "yes" : "no" );
1044
1045 if( !files_to_write.IsEmpty() )
1046 files_to_write.WriteEmbeddedFiles( *m_out, ( m_ctl & CTL_FOR_BOARD ) );
1047
1048 // Remove the files so that they are not freed in the DTOR
1049 files_to_write.ClearEmbeddedFiles( false );
1050}
1051
1052
1053void PCB_IO_KICAD_SEXPR::format( const PCB_DIMENSION_BASE* aDimension ) const
1054{
1055 const PCB_DIM_ALIGNED* aligned = dynamic_cast<const PCB_DIM_ALIGNED*>( aDimension );
1056 const PCB_DIM_ORTHOGONAL* ortho = dynamic_cast<const PCB_DIM_ORTHOGONAL*>( aDimension );
1057 const PCB_DIM_CENTER* center = dynamic_cast<const PCB_DIM_CENTER*>( aDimension );
1058 const PCB_DIM_RADIAL* radial = dynamic_cast<const PCB_DIM_RADIAL*>( aDimension );
1059 const PCB_DIM_LEADER* leader = dynamic_cast<const PCB_DIM_LEADER*>( aDimension );
1060
1061 m_out->Print( "(dimension" );
1062
1063 if( ortho ) // must be tested before aligned, because ortho is derived from aligned
1064 // and aligned is not null
1065 m_out->Print( "(type orthogonal)" );
1066 else if( aligned )
1067 m_out->Print( "(type aligned)" );
1068 else if( leader )
1069 m_out->Print( "(type leader)" );
1070 else if( center )
1071 m_out->Print( "(type center)" );
1072 else if( radial )
1073 m_out->Print( "(type radial)" );
1074 else
1075 wxFAIL_MSG( wxT( "Cannot format unknown dimension type!" ) );
1076
1077 if( aDimension->IsLocked() )
1078 KICAD_FORMAT::FormatBool( m_out, "locked", aDimension->IsLocked() );
1079
1080 formatLayer( aDimension->GetLayer() );
1081
1082 KICAD_FORMAT::FormatUuid( m_out, aDimension->m_Uuid );
1083
1084 m_out->Print( "(pts (xy %s %s) (xy %s %s))",
1085 formatInternalUnits( aDimension->GetStart().x ).c_str(),
1086 formatInternalUnits( aDimension->GetStart().y ).c_str(),
1087 formatInternalUnits( aDimension->GetEnd().x ).c_str(),
1088 formatInternalUnits( aDimension->GetEnd().y ).c_str() );
1089
1090 if( aligned )
1091 m_out->Print( "(height %s)", formatInternalUnits( aligned->GetHeight() ).c_str() );
1092
1093 if( radial )
1094 {
1095 m_out->Print( "(leader_length %s)",
1096 formatInternalUnits( radial->GetLeaderLength() ).c_str() );
1097 }
1098
1099 if( ortho )
1100 m_out->Print( "(orientation %d)", static_cast<int>( ortho->GetOrientation() ) );
1101
1102 if( !center )
1103 {
1104 m_out->Print( "(format (prefix %s) (suffix %s) (units %d) (units_format %d) (precision %d)",
1105 m_out->Quotew( aDimension->GetPrefix() ).c_str(),
1106 m_out->Quotew( aDimension->GetSuffix() ).c_str(),
1107 static_cast<int>( aDimension->GetUnitsMode() ),
1108 static_cast<int>( aDimension->GetUnitsFormat() ),
1109 static_cast<int>( aDimension->GetPrecision() ) );
1110
1111 if( aDimension->GetOverrideTextEnabled() )
1112 {
1113 m_out->Print( "(override_value %s)",
1114 m_out->Quotew( aDimension->GetOverrideText() ).c_str() );
1115 }
1116
1117 if( aDimension->GetSuppressZeroes() )
1118 KICAD_FORMAT::FormatBool( m_out, "suppress_zeroes", true );
1119
1120 m_out->Print( ")" );
1121 }
1122
1123 m_out->Print( "(style (thickness %s) (arrow_length %s) (text_position_mode %d)",
1124 formatInternalUnits( aDimension->GetLineThickness() ).c_str(),
1125 formatInternalUnits( aDimension->GetArrowLength() ).c_str(),
1126 static_cast<int>( aDimension->GetTextPositionMode() ) );
1127
1128 if( ortho || aligned )
1129 {
1130 switch( aDimension->GetArrowDirection() )
1131 {
1133 m_out->Print( "(arrow_direction outward)" );
1134 break;
1136 m_out->Print( "(arrow_direction inward)" );
1137 break;
1138 // No default, handle all cases
1139 }
1140 }
1141
1142 if( aligned )
1143 {
1144 m_out->Print( "(extension_height %s)",
1145 formatInternalUnits( aligned->GetExtensionHeight() ).c_str() );
1146 }
1147
1148 if( leader )
1149 m_out->Print( "(text_frame %d)", static_cast<int>( leader->GetTextBorder() ) );
1150
1151 m_out->Print( "(extension_offset %s)",
1152 formatInternalUnits( aDimension->GetExtensionOffset() ).c_str() );
1153
1154 if( aDimension->GetKeepTextAligned() )
1155 KICAD_FORMAT::FormatBool( m_out, "keep_text_aligned", true );
1156
1157 m_out->Print( ")" );
1158
1159 // Write dimension text after all other options to be sure the
1160 // text options are known when reading the file
1161 if( !center )
1162 format( static_cast<const PCB_TEXT*>( aDimension ) );
1163
1165 m_out->Print( ")" );
1166}
1167
1168
1169void PCB_IO_KICAD_SEXPR::format( const PCB_SHAPE* aShape ) const
1170{
1171 FOOTPRINT* parentFP = aShape->GetParentFootprint();
1172 std::string prefix = parentFP ? "fp" : "gr";
1173
1174 switch( aShape->GetLibraryShape() )
1175 {
1176 case SHAPE_T::SEGMENT:
1177 m_out->Print( "(%s_line (start %s) (end %s)",
1178 prefix.c_str(),
1179 formatInternalUnits( aShape->GetLibraryStart() ).c_str(),
1180 formatInternalUnits( aShape->GetLibraryEnd() ).c_str() );
1181 break;
1182
1183 case SHAPE_T::RECTANGLE:
1184 m_out->Print( "(%s_rect (start %s) (end %s)", prefix.c_str(),
1185 formatInternalUnits( aShape->GetLibraryStart() ).c_str(),
1186 formatInternalUnits( aShape->GetLibraryEnd() ).c_str() );
1187
1188 if( aShape->GetCornerRadius() > 0 )
1189 m_out->Print( " (radius %s)", formatInternalUnits( aShape->GetLibraryCornerRadius() ).c_str() );
1190 break;
1191
1192 case SHAPE_T::CIRCLE:
1193 m_out->Print( "(%s_circle (center %s) (end %s)", prefix.c_str(),
1194 formatInternalUnits( aShape->GetLibraryStart() ).c_str(),
1195 formatInternalUnits( aShape->GetLibraryEnd() ).c_str() );
1196 break;
1197
1198 case SHAPE_T::ARC:
1199 m_out->Print( "(%s_arc (start %s) (mid %s) (end %s)", prefix.c_str(),
1200 formatInternalUnits( aShape->GetLibraryStart() ).c_str(),
1201 formatInternalUnits( aShape->GetLibraryArcMid() ).c_str(),
1202 formatInternalUnits( aShape->GetLibraryEnd() ).c_str() );
1203 break;
1204
1205 case SHAPE_T::POLY:
1206 if( aShape->IsPolyShapeValid() )
1207 {
1208 const SHAPE_POLY_SET& poly = aShape->GetPolyShape();
1209 const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
1210
1211 m_out->Print( "(%s_poly", prefix.c_str() );
1212 formatPolyPts( outline, parentFP );
1213 }
1214 else
1215 {
1216 return;
1217 }
1218
1219 break;
1220
1221 case SHAPE_T::BEZIER:
1222 m_out->Print( "(%s_curve (pts (xy %s) (xy %s) (xy %s) (xy %s))",
1223 prefix.c_str(),
1224 formatInternalUnits( aShape->GetStart(), parentFP ).c_str(),
1225 formatInternalUnits( aShape->GetBezierC1(), parentFP ).c_str(),
1226 formatInternalUnits( aShape->GetBezierC2(), parentFP ).c_str(),
1227 formatInternalUnits( aShape->GetEnd(), parentFP ).c_str() );
1228 break;
1229
1230 case SHAPE_T::ELLIPSE:
1231 m_out->Print( "(%s_ellipse (center %s) (major_radius %s) (minor_radius %s) "
1232 "(rotation_angle %s)",
1233 prefix.c_str(),
1234 formatInternalUnits( aShape->GetLibraryEllipseCenter() ).c_str(),
1238 break;
1239
1241 m_out->Print( "(%s_ellipse_arc (center %s) (major_radius %s) (minor_radius %s) "
1242 "(rotation_angle %s) (start_angle %s) (end_angle %s)",
1243 prefix.c_str(),
1244 formatInternalUnits( aShape->GetLibraryEllipseCenter() ).c_str(),
1250 break;
1251
1252 default:
1254 return;
1255 };
1256
1257 {
1258 STROKE_PARAMS stroke = aShape->GetStroke();
1259 stroke.SetWidth( unbakeLinear( stroke.GetWidth(), parentFP ) );
1260 stroke.Format( m_out, pcbIUScale );
1261 }
1262
1263 aShape->GetStartEnding().Format( m_out, pcbIUScale, "start_shape" );
1264 aShape->GetEndEnding().Format( m_out, pcbIUScale, "end_shape" );
1265
1266 // The filled flag represents if a solid fill is present on circles, rectangles and polygons
1267 if( ( aShape->GetShape() == SHAPE_T::POLY ) || ( aShape->GetShape() == SHAPE_T::RECTANGLE )
1268 || ( aShape->GetShape() == SHAPE_T::CIRCLE ) || ( aShape->GetShape() == SHAPE_T::ELLIPSE ) )
1269 {
1270 switch( aShape->GetFillMode() )
1271 {
1272 case FILL_T::HATCH:
1273 m_out->Print( "(fill hatch)" );
1274 break;
1275
1277 m_out->Print( "(fill reverse_hatch)" );
1278 break;
1279
1281 m_out->Print( "(fill cross_hatch)" );
1282 break;
1283
1285 KICAD_FORMAT::FormatBool( m_out, "fill", true );
1286 break;
1287
1288 default:
1289 KICAD_FORMAT::FormatBool( m_out, "fill", false );
1290 break;
1291 }
1292 }
1293
1294 if( aShape->IsLocked() )
1295 KICAD_FORMAT::FormatBool( m_out, "locked", true );
1296
1297 if( aShape->GetLayerSet().count() > 1 )
1298 formatLayers( aShape->GetLayerSet(), false /* enumerate layers */ );
1299 else
1300 formatLayer( aShape->GetLayer() );
1301
1302 if( aShape->HasSolderMask()
1303 && aShape->GetLocalSolderMaskMargin().has_value()
1304 && IsExternalCopperLayer( aShape->GetLayer() ) )
1305 {
1306 m_out->Print( "(solder_mask_margin %s)",
1307 formatInternalUnits( aShape->GetLocalSolderMaskMargin().value() ).c_str() );
1308 }
1309
1310 if( !( m_ctl & CTL_OMIT_PAD_NETS ) && aShape->GetNetCode() > 0 )
1311 m_out->Print( "(net %s)", m_out->Quotew( aShape->GetNetname() ).c_str() );
1312
1315 m_out->Print( ")" );
1316}
1317
1318
1320{
1321 wxCHECK_RET( aBitmap != nullptr && m_out != nullptr, "" );
1322
1323 const REFERENCE_IMAGE& refImage = aBitmap->GetReferenceImage();
1324
1325 const wxImage* image = refImage.GetImage().GetImageData();
1326
1327 wxCHECK_RET( image != nullptr, "wxImage* is NULL" );
1328
1329 m_out->Print( "(image (at %s %s)",
1330 formatInternalUnits( aBitmap->GetPosition().x ).c_str(),
1331 formatInternalUnits( aBitmap->GetPosition().y ).c_str() );
1332
1333 formatLayer( aBitmap->GetLayer() );
1334
1335 if( refImage.GetImageScale() != 1.0 )
1336 m_out->Print( "%s", fmt::format("(scale {:g})", refImage.GetImageScale()).c_str() );
1337
1338 if( aBitmap->IsLocked() )
1339 KICAD_FORMAT::FormatBool( m_out, "locked", true );
1340
1341 wxMemoryOutputStream ostream;
1342 refImage.GetImage().SaveImageData( ostream );
1343
1344 KICAD_FORMAT::FormatStreamData( *m_out, *ostream.GetOutputStreamBuffer() );
1345
1348 m_out->Print( ")" ); // Closes image token.
1349}
1350
1351
1352void PCB_IO_KICAD_SEXPR::format( const PCB_POINT* aPoint ) const
1353{
1354 m_out->Print( "(point (at %s) (size %s)", formatInternalUnits( aPoint->GetLibraryPosition() ).c_str(),
1355 formatInternalUnits( aPoint->GetSize() ).c_str() );
1356
1357 formatLayer( aPoint->GetLayer() );
1358
1361 m_out->Print( ")" );
1362}
1363
1364
1365void PCB_IO_KICAD_SEXPR::format( const PCB_TARGET* aTarget ) const
1366{
1367 m_out->Print( "(target %s (at %s) (size %s)",
1368 ( aTarget->GetShape() ) ? "x" : "plus",
1369 formatInternalUnits( aTarget->GetPosition() ).c_str(),
1370 formatInternalUnits( aTarget->GetSize() ).c_str() );
1371
1372 if( aTarget->GetWidth() != 0 )
1373 m_out->Print( "(width %s)", formatInternalUnits( aTarget->GetWidth() ).c_str() );
1374
1375 formatLayer( aTarget->GetLayer() );
1378 m_out->Print( ")" );
1379}
1380
1381
1382void PCB_IO_KICAD_SEXPR::format( const PCB_GRID_ITEM* aGridItem ) const
1383{
1384 const bool polar = aGridItem->GetGridItemType() == PCB_GRID_TYPE::POLAR;
1385
1386 // Grid type (polar/xy) must be emitted before extent/spacing: those tokens change
1387 // meaning based on the type (y component = angle for polar, length for cartesian).
1388 m_out->Print( "(grid_item %s (at %s)", polar ? "polar" : "xy",
1389 formatInternalUnits( aGridItem->GetPosition() ).c_str() );
1390
1391 if( polar )
1392 {
1393 // Polar y components are angles; use FormatAngle, not formatInternalUnits.
1394 m_out->Print( " (extent %s %s) (spacing %s %s)", formatInternalUnits( aGridItem->GetRadiusExtent() ).c_str(),
1395 EDA_UNIT_UTILS::FormatAngle( aGridItem->GetPhiExtent() ).c_str(),
1396 formatInternalUnits( aGridItem->GetRadiusSpacing() ).c_str(),
1397 EDA_UNIT_UTILS::FormatAngle( aGridItem->GetPhiSpacing() ).c_str() );
1398 }
1399 else
1400 {
1401 m_out->Print( " (extent %s) (spacing %s)", formatInternalUnits( aGridItem->GetExtent() ).c_str(),
1402 formatInternalUnits( aGridItem->GetSpacing() ).c_str() );
1403 }
1404
1405 if( !aGridItem->GetOrientation().IsZero() )
1406 {
1407 m_out->Print( " (angle %s)", EDA_UNIT_UTILS::FormatAngle( aGridItem->GetOrientation() ).c_str() );
1408 }
1409
1410 // Priority is always set
1411 m_out->Print( " (priority %u)", aGridItem->GetAssignedPriority() );
1412
1413 if( aGridItem->GetTickInterval() > 0 )
1414 m_out->Print( " (tick_interval %u)", aGridItem->GetTickInterval() );
1415
1416 const PCB_GRID_AFFECTS& aff = aGridItem->Affects();
1417 m_out->Print( " (affects (cursor %s) (routing %s) (placement %s))",
1418 aff.cursor ? "yes" : "no",
1419 aff.routing ? "yes" : "no",
1420 aff.placement ? "yes" : "no" );
1421
1422 if( aGridItem->IsLocked() )
1423 KICAD_FORMAT::FormatBool( m_out, "locked", true );
1424
1425 KICAD_FORMAT::FormatUuid( m_out, aGridItem->m_Uuid );
1427 m_out->Print( ")\n" );
1428}
1429
1430
1431void PCB_IO_KICAD_SEXPR::format( const FOOTPRINT* aFootprint ) const
1432{
1433 if( !( m_ctl & CTL_OMIT_INITIAL_COMMENTS ) )
1434 {
1435 const wxArrayString* initial_comments = aFootprint->GetInitialComments();
1436
1437 if( initial_comments )
1438 {
1439 for( unsigned i = 0; i < initial_comments->GetCount(); ++i )
1440 m_out->Print( "%s\n", TO_UTF8( (*initial_comments)[i] ) );
1441 }
1442 }
1443
1444 if( m_ctl & CTL_OMIT_LIBNAME )
1445 {
1446 m_out->Print( "(footprint %s",
1447 m_out->Quotes( aFootprint->GetFPID().GetLibItemName() ).c_str() );
1448 }
1449 else
1450 {
1451 m_out->Print( "(footprint %s",
1452 m_out->Quotes( aFootprint->GetFPID().Format() ).c_str() );
1453 }
1454
1456 {
1457 m_out->Print( "(version %d) (generator \"pcbnew\") (generator_version %s)", SEXPR_BOARD_FILE_VERSION,
1458 m_out->Quotew( GetMajorMinorVersion() ).c_str() );
1459 }
1460
1461 if( aFootprint->IsLocked() )
1462 KICAD_FORMAT::FormatBool( m_out, "locked", true );
1463
1464 if( aFootprint->IsPlaced() )
1465 KICAD_FORMAT::FormatBool( m_out, "placed", true );
1466
1467 formatLayer( aFootprint->GetLayer() );
1468
1469 if( !( m_ctl & CTL_OMIT_UUIDS ) )
1470 KICAD_FORMAT::FormatUuid( m_out, aFootprint->m_Uuid );
1471
1472 if( !( m_ctl & CTL_OMIT_AT ) )
1473 {
1474 m_out->Print( "(transform (translate %s) (rotate %s) (scale %s %s))",
1475 formatInternalUnits( aFootprint->GetPosition() ).c_str(),
1476 EDA_UNIT_UTILS::FormatAngle( aFootprint->GetOrientation() ).c_str(),
1477 FormatDouble2Str( aFootprint->GetTransform().GetScaleX() ).c_str(),
1478 FormatDouble2Str( aFootprint->GetTransform().GetScaleY() ).c_str() );
1479 }
1480
1481 if( !aFootprint->GetLibDescription().IsEmpty() )
1482 m_out->Print( "(descr %s)", m_out->Quotew( aFootprint->GetLibDescription() ).c_str() );
1483
1484 if( !aFootprint->GetKeywords().IsEmpty() )
1485 m_out->Print( "(tags %s)", m_out->Quotew( aFootprint->GetKeywords() ).c_str() );
1486
1487 for( const PCB_FIELD* field : aFootprint->GetFields() )
1488 {
1489 if( !field )
1490 continue;
1491
1492 m_out->Print( "(property %s %s",
1493 m_out->Quotew( field->GetUntranslatedName() ).c_str(),
1494 m_out->Quotew( field->GetText() ).c_str() );
1495
1496 format( field );
1497
1498 m_out->Print( ")" );
1499 }
1500
1501 if( const COMPONENT_CLASS* compClass = aFootprint->GetStaticComponentClass() )
1502 {
1503 if( !compClass->IsEmpty() )
1504 {
1505 m_out->Print( "(component_classes" );
1506
1507 for( const COMPONENT_CLASS* constituent : compClass->GetConstituentClasses() )
1508 m_out->Print( "(class %s)", m_out->Quotew( constituent->GetName() ).c_str() );
1509
1510 m_out->Print( ")" );
1511 }
1512 }
1513
1514 if( !aFootprint->GetFilters().empty() )
1515 {
1516 m_out->Print( "(property ki_fp_filters %s)",
1517 m_out->Quotew( aFootprint->GetFilters() ).c_str() );
1518 }
1519
1520 if( !( m_ctl & CTL_OMIT_PATH ) && !aFootprint->GetPath().empty() )
1521 m_out->Print( "(path %s)", m_out->Quotew( aFootprint->GetPath().AsString() ).c_str() );
1522
1523 if( !aFootprint->GetSheetname().empty() )
1524 m_out->Print( "(sheetname %s)", m_out->Quotew( aFootprint->GetSheetname() ).c_str() );
1525
1526 if( !aFootprint->GetSheetfile().empty() )
1527 m_out->Print( "(sheetfile %s)", m_out->Quotew( aFootprint->GetSheetfile() ).c_str() );
1528
1529 // Emit unit info for gate swapping metadata (flat pin list form)
1530 if( !aFootprint->GetUnitInfo().empty() )
1531 {
1532 m_out->Print( "(units" );
1533
1534 for( const FOOTPRINT::FP_UNIT_INFO& u : aFootprint->GetUnitInfo() )
1535 {
1536 m_out->Print( "(unit (name %s)", m_out->Quotew( u.m_unitName ).c_str() );
1537 m_out->Print( "(pins" );
1538
1539 for( const wxString& n : u.m_pins )
1540 m_out->Print( " %s", m_out->Quotew( n ).c_str() );
1541
1542 m_out->Print( ")" ); // </pins>
1543 m_out->Print( ")" ); // </unit>
1544 }
1545
1546 m_out->Print( ")" ); // </units>
1547 }
1548
1549 if( aFootprint->GetLocalSolderMaskMargin().has_value() )
1550 {
1551 m_out->Print( "(solder_mask_margin %s)",
1552 formatInternalUnits( aFootprint->GetLocalSolderMaskMargin().value() ).c_str() );
1553 }
1554
1555 if( aFootprint->GetLocalSolderPasteMargin().has_value() )
1556 {
1557 m_out->Print( "(solder_paste_margin %s)",
1558 formatInternalUnits( aFootprint->GetLocalSolderPasteMargin().value() ).c_str() );
1559 }
1560
1561 if( aFootprint->GetLocalSolderPasteMarginRatio().has_value() )
1562 {
1563 m_out->Print( "(solder_paste_margin_ratio %s)",
1564 FormatDouble2Str( aFootprint->GetLocalSolderPasteMarginRatio().value() ).c_str() );
1565 }
1566
1567 if( aFootprint->GetLocalClearance().has_value() )
1568 {
1569 m_out->Print( "(clearance %s)",
1570 formatInternalUnits( aFootprint->GetLocalClearance().value() ).c_str() );
1571 }
1572
1574 {
1575 m_out->Print( "(zone_connect %d)",
1576 static_cast<int>( aFootprint->GetLocalZoneConnection() ) );
1577 }
1578
1579 // Attributes
1580 if( aFootprint->GetAttributes()
1581 || aFootprint->AllowMissingCourtyard()
1582 || aFootprint->AllowSolderMaskBridges() )
1583 {
1584 m_out->Print( "(attr" );
1585
1586 if( aFootprint->GetAttributes() & FP_SMD )
1587 m_out->Print( " smd" );
1588
1589 if( aFootprint->GetAttributes() & FP_THROUGH_HOLE )
1590 m_out->Print( " through_hole" );
1591
1592 if( aFootprint->GetAttributes() & FP_BOARD_ONLY )
1593 m_out->Print( " board_only" );
1594
1595 if( aFootprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES )
1596 m_out->Print( " exclude_from_pos_files" );
1597
1598 if( aFootprint->GetAttributes() & FP_EXCLUDE_FROM_BOM )
1599 m_out->Print( " exclude_from_bom" );
1600
1601 if( aFootprint->GetAttributes() & FP_EXCLUDE_FROM_SIM )
1602 m_out->Print( " exclude_from_sim" );
1603
1604 if( aFootprint->AllowMissingCourtyard() )
1605 m_out->Print( " allow_missing_courtyard" );
1606
1607 if( aFootprint->GetAttributes() & FP_DNP )
1608 m_out->Print( " dnp" );
1609
1610 if( aFootprint->AllowSolderMaskBridges() )
1611 m_out->Print( " allow_soldermask_bridges" );
1612
1613 m_out->Print( ")" );
1614 }
1615
1616 // Expand inner layers is the default stackup mode
1618 {
1619 m_out->Print( "(stackup" );
1620
1621 const LSET& fpLset = aFootprint->GetStackupLayers();
1622 for( PCB_LAYER_ID layer : fpLset.Seq() )
1623 {
1624 wxString canonicalName( LSET::Name( layer ) );
1625 m_out->Print( "(layer %s)", m_out->Quotew( canonicalName ).c_str() );
1626 }
1627
1628 m_out->Print( ")" );
1629 }
1630
1631 if( aFootprint->GetPrivateLayers().any() )
1632 {
1633 m_out->Print( "(private_layers" );
1634
1635 for( PCB_LAYER_ID layer : aFootprint->GetPrivateLayers().Seq() )
1636 {
1637 wxString canonicalName( LSET::Name( layer ) );
1638 m_out->Print( " %s", m_out->Quotew( canonicalName ).c_str() );
1639 }
1640
1641 m_out->Print( ")" );
1642 }
1643
1644 if( aFootprint->IsNetTie() )
1645 {
1646 m_out->Print( "(net_tie_pad_groups" );
1647
1648 for( const wxString& group : aFootprint->GetNetTiePadGroups() )
1649 m_out->Print( " %s", m_out->Quotew( group ).c_str() );
1650
1651 m_out->Print( ")" );
1652 }
1653
1654 KICAD_FORMAT::FormatBool( m_out, "duplicate_pad_numbers_are_jumpers",
1655 aFootprint->GetDuplicatePadNumbersAreJumpers() );
1656
1657 const JUMPER_GROUP_SET& jumperGroups = aFootprint->JumperPadGroups();
1658
1659 if( !jumperGroups.IsEmpty() )
1660 {
1661 m_out->Print( "(jumper_pad_groups" );
1662
1663 for( const JUMPER_GROUP& group : jumperGroups.GetAll() )
1664 {
1665 m_out->Print( "(" );
1666
1667 for( const wxString& padName : group.GetNames() )
1668 m_out->Print( "%s ", m_out->Quotew( padName ).c_str() );
1669
1670 m_out->Print( ")" );
1671 }
1672
1673 m_out->Print( ")" );
1674 }
1675
1676 Format( &aFootprint->Reference() );
1677 Format( &aFootprint->Value() );
1678
1679 std::set<PAD*, FOOTPRINT::cmp_pads> sorted_pads( aFootprint->Pads().begin(),
1680 aFootprint->Pads().end() );
1681 std::set<BOARD_ITEM*, FOOTPRINT::cmp_drawings> sorted_drawings(
1682 aFootprint->GraphicalItems().begin(),
1683 aFootprint->GraphicalItems().end() );
1684 std::set<PCB_POINT*, PCB_POINT::cmp_points> sorted_points(
1685 aFootprint->Points().begin(),
1686 aFootprint->Points().end() );
1687 std::set<ZONE*, FOOTPRINT::cmp_zones> sorted_zones( aFootprint->Zones().begin(),
1688 aFootprint->Zones().end() );
1689 std::set<BOARD_ITEM*, PCB_GROUP::ptr_cmp> sorted_groups( aFootprint->Groups().begin(),
1690 aFootprint->Groups().end() );
1691 std::set<BOARD_ITEM*, PCB_GROUP::ptr_cmp> sorted_constraints( aFootprint->Constraints().begin(),
1692 aFootprint->Constraints().end() );
1693
1694 // Save drawing elements.
1695
1696 for( BOARD_ITEM* gr : sorted_drawings )
1697 Format( gr );
1698
1699 for( PCB_POINT* point : sorted_points )
1700 Format( point );
1701
1702 // Save pads.
1703 for( PAD* pad : sorted_pads )
1704 Format( pad );
1705
1706 // Save zones.
1707 for( BOARD_ITEM* zone : sorted_zones )
1708 Format( zone );
1709
1710 // Save groups.
1711 for( BOARD_ITEM* group : sorted_groups )
1712 Format( group );
1713
1714 // Save geometric constraints, after the items they reference.
1715 for( BOARD_ITEM* constraint : sorted_constraints )
1716 Format( constraint );
1717
1718 // Save variants.
1719 const bool baseDnp = aFootprint->IsDNP();
1720 const bool baseExcludedFromBOM = aFootprint->IsExcludedFromBOM();
1721 const bool baseExcludedFromSim = aFootprint->IsExcludedFromSim();
1722 const bool baseExcludedFromPosFiles = aFootprint->IsExcludedFromPosFiles();
1723
1724 for( const auto& [variantName, variant] : aFootprint->GetVariants() )
1725 {
1726 m_out->Print( "(variant (name %s)", m_out->Quotew( variantName ).c_str() );
1727
1728 if( variant.GetDNP() != baseDnp )
1729 KICAD_FORMAT::FormatBool( m_out, "dnp", variant.GetDNP() );
1730
1731 if( variant.GetExcludedFromBOM() != baseExcludedFromBOM )
1732 KICAD_FORMAT::FormatBool( m_out, "exclude_from_bom", variant.GetExcludedFromBOM() );
1733
1734 if( variant.GetExcludedFromSim() != baseExcludedFromSim )
1735 KICAD_FORMAT::FormatBool( m_out, "exclude_from_sim", variant.GetExcludedFromSim() );
1736
1737 if( variant.GetExcludedFromPosFiles() != baseExcludedFromPosFiles )
1738 {
1739 KICAD_FORMAT::FormatBool( m_out, "exclude_from_pos_files",
1740 variant.GetExcludedFromPosFiles() );
1741 }
1742
1743 for( const auto& [fieldName, fieldValue] : variant.GetFields() )
1744 {
1745 const PCB_FIELD* baseField = aFootprint->GetField( fieldName );
1746 const wxString baseValue = baseField ? baseField->GetText() : wxString();
1747
1748 if( fieldValue == baseValue )
1749 continue;
1750
1751 m_out->Print( "(field (name %s) (value %s))",
1752 m_out->Quotew( fieldName ).c_str(),
1753 m_out->Quotew( fieldValue ).c_str() );
1754 }
1755
1756 m_out->Print( ")" );
1757 }
1758
1759 KICAD_FORMAT::FormatBool( m_out, "embedded_fonts",
1760 aFootprint->GetEmbeddedFiles()->GetAreFontsEmbedded() );
1761
1762 if( !aFootprint->GetEmbeddedFiles()->IsEmpty() )
1763 aFootprint->WriteEmbeddedFiles( *m_out, !( m_ctl & CTL_FOR_BOARD ) );
1764
1765 // Save extruded 3D body info.
1766 if( const EXTRUDED_3D_BODY* body = aFootprint->GetExtrudedBody(); body && body->m_height > 0 )
1767 {
1768 m_out->Print( "(model" );
1769 m_out->Print( "(type extruded)" );
1770 KICAD_FORMAT::FormatBool( m_out, "hide", !body->m_show );
1771 m_out->Print( "(overall_height %s)", formatInternalUnits( body->m_height ).c_str() );
1772 m_out->Print( "(body_pcb_gap %s)", formatInternalUnits( body->m_standoff ).c_str() );
1773
1774 if( body->m_layer == UNSELECTED_LAYER )
1775 m_out->Print( "(layer pad_bbox)" );
1776 else if( body->m_layer != UNDEFINED_LAYER )
1777 m_out->Print( "(layer %s)", m_out->Quotew( LSET::Name( body->m_layer ) ).c_str() );
1778 else
1779 m_out->Print( "(layer auto)" );
1780
1781 {
1782 static const char* matNames[] = { "plastic", "matte", "metal", "copper" };
1783 m_out->Print( "(material %s)", matNames[static_cast<int>( body->m_material )] );
1784 }
1785
1786 if( body->m_color != KIGFX::COLOR4D::UNSPECIFIED )
1787 {
1788 m_out->Print( "(color %s %s %s %s)", FormatDouble2Str( body->m_color.r ).c_str(),
1789 FormatDouble2Str( body->m_color.g ).c_str(), FormatDouble2Str( body->m_color.b ).c_str(),
1790 FormatDouble2Str( body->m_color.a ).c_str() );
1791 }
1792 else
1793 {
1794 m_out->Print( "(color unspecified)" );
1795 }
1796
1797 m_out->Print( "(offset (xyz %s %s %s))", FormatDouble2Str( body->m_offset.x ).c_str(),
1798 FormatDouble2Str( body->m_offset.y ).c_str(), FormatDouble2Str( body->m_offset.z ).c_str() );
1799
1800 m_out->Print( "(scale (xyz %s %s %s))", FormatDouble2Str( body->m_scale.x ).c_str(),
1801 FormatDouble2Str( body->m_scale.y ).c_str(), FormatDouble2Str( body->m_scale.z ).c_str() );
1802
1803 m_out->Print( "(rotate (xyz %s %s %s))", FormatDouble2Str( body->m_rotation.x ).c_str(),
1804 FormatDouble2Str( body->m_rotation.y ).c_str(), FormatDouble2Str( body->m_rotation.z ).c_str() );
1805
1806 m_out->Print( ")" );
1807 }
1808
1809 // Save 3D info.
1810 auto bs3D = aFootprint->Models().begin();
1811 auto es3D = aFootprint->Models().end();
1812
1813 while( bs3D != es3D )
1814 {
1815 if( !bs3D->m_Filename.IsEmpty() )
1816 {
1817 m_out->Print( "(model %s", m_out->Quotew( bs3D->m_Filename ).c_str() );
1818
1819 if( !bs3D->m_Show )
1820 KICAD_FORMAT::FormatBool( m_out, "hide", !bs3D->m_Show );
1821
1822 if( bs3D->m_Opacity != 1.0 )
1823 m_out->Print( "%s", fmt::format("(opacity {:.4f})", bs3D->m_Opacity).c_str() );
1824
1825 m_out->Print( "(offset (xyz %s %s %s))",
1826 FormatDouble2Str( bs3D->m_Offset.x ).c_str(),
1827 FormatDouble2Str( bs3D->m_Offset.y ).c_str(),
1828 FormatDouble2Str( bs3D->m_Offset.z ).c_str() );
1829
1830 m_out->Print( "(scale (xyz %s %s %s))",
1831 FormatDouble2Str( bs3D->m_Scale.x ).c_str(),
1832 FormatDouble2Str( bs3D->m_Scale.y ).c_str(),
1833 FormatDouble2Str( bs3D->m_Scale.z ).c_str() );
1834
1835 m_out->Print( "(rotate (xyz %s %s %s))",
1836 FormatDouble2Str( bs3D->m_Rotation.x ).c_str(),
1837 FormatDouble2Str( bs3D->m_Rotation.y ).c_str(),
1838 FormatDouble2Str( bs3D->m_Rotation.z ).c_str() );
1839
1840 m_out->Print( ")" );
1841 }
1842
1843 ++bs3D;
1844 }
1845
1847 m_out->Print( ")" );
1848}
1849
1850
1851void PCB_IO_KICAD_SEXPR::formatLayers( LSET aLayerMask, bool aEnumerateLayers, bool aIsZone ) const
1852{
1853 static const LSET cu_all( LSET::AllCuMask() );
1854 static const LSET fr_bk( { B_Cu, F_Cu } );
1855 static const LSET adhes( { B_Adhes, F_Adhes } );
1856 static const LSET paste( { B_Paste, F_Paste } );
1857 static const LSET silks( { B_SilkS, F_SilkS } );
1858 static const LSET mask( { B_Mask, F_Mask } );
1859 static const LSET crt_yd( { B_CrtYd, F_CrtYd } );
1860 static const LSET fab( { B_Fab, F_Fab } );
1861
1862 LSET cu_board_mask = LSET::AllCuMask( m_board ? m_board->GetCopperLayerCount() : MAX_CU_LAYERS );
1863
1864 std::string output;
1865
1866 if( !aEnumerateLayers )
1867 {
1868 // If all copper layers present on the board are enabled, then output the wildcard
1869 if( ( aLayerMask & cu_board_mask ) == cu_board_mask )
1870 {
1871 output += ' ' + m_out->Quotew( "*.Cu" );
1872
1873 // Clear all copper bits because pads might have internal layers that aren't part of the
1874 // board enabled, and we don't want to output those in the layers listing if we already
1875 // output the wildcard.
1876 aLayerMask &= ~cu_all;
1877 }
1878 else if( ( aLayerMask & cu_board_mask ) == fr_bk )
1879 {
1880 if( aIsZone )
1881 output += ' ' + m_out->Quotew( "F&B.Cu" );
1882 else
1883 output += ' ' + m_out->Quotew( "*.Cu" );
1884
1885 aLayerMask &= ~fr_bk;
1886 }
1887
1888 if( ( aLayerMask & adhes ) == adhes )
1889 {
1890 output += ' ' + m_out->Quotew( "*.Adhes" );
1891 aLayerMask &= ~adhes;
1892 }
1893
1894 if( ( aLayerMask & paste ) == paste )
1895 {
1896 output += ' ' + m_out->Quotew( "*.Paste" );
1897 aLayerMask &= ~paste;
1898 }
1899
1900 if( ( aLayerMask & silks ) == silks )
1901 {
1902 output += ' ' + m_out->Quotew( "*.SilkS" );
1903 aLayerMask &= ~silks;
1904 }
1905
1906 if( ( aLayerMask & mask ) == mask )
1907 {
1908 output += ' ' + m_out->Quotew( "*.Mask" );
1909 aLayerMask &= ~mask;
1910 }
1911
1912 if( ( aLayerMask & crt_yd ) == crt_yd )
1913 {
1914 output += ' ' + m_out->Quotew( "*.CrtYd" );
1915 aLayerMask &= ~crt_yd;
1916 }
1917
1918 if( ( aLayerMask & fab ) == fab )
1919 {
1920 output += ' ' + m_out->Quotew( "*.Fab" );
1921 aLayerMask &= ~fab;
1922 }
1923 }
1924
1925 // output any individual layers not handled in wildcard combos above
1926 for( int layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
1927 {
1928 if( aLayerMask[layer] )
1929 output += ' ' + m_out->Quotew( LSET::Name( PCB_LAYER_ID( layer ) ) );
1930 }
1931
1932 m_out->Print( "(layers %s)", output.c_str() );
1933}
1934
1935
1936void PCB_IO_KICAD_SEXPR::format( const PAD* aPad ) const
1937{
1938 const BOARD* board = aPad->GetBoard();
1939 const FOOTPRINT* parentFP = aPad->GetParentFootprint();
1940
1941 auto shapeName =
1942 [&]( PCB_LAYER_ID aLayer )
1943 {
1944 switch( aPad->GetShape( aLayer ) )
1945 {
1946 case PAD_SHAPE::CIRCLE: return "circle";
1947 case PAD_SHAPE::RECTANGLE: return "rect";
1948 case PAD_SHAPE::OVAL: return "oval";
1949 case PAD_SHAPE::TRAPEZOID: return "trapezoid";
1951 case PAD_SHAPE::ROUNDRECT: return "roundrect";
1952 case PAD_SHAPE::CUSTOM: return "custom";
1953
1954 default:
1955 THROW_IO_ERRORF( _( "unknown pad type: %d" ), aPad->GetShape( aLayer ) );
1956 }
1957 };
1958
1959 const char* type;
1960
1961 switch( aPad->GetAttribute() )
1962 {
1963 case PAD_ATTRIB::PTH: type = "thru_hole"; break;
1964 case PAD_ATTRIB::SMD: type = "smd"; break;
1965 case PAD_ATTRIB::CONN: type = "connect"; break;
1966 case PAD_ATTRIB::NPTH: type = "np_thru_hole"; break;
1967
1968 default:
1969 THROW_IO_ERRORF( _( "unknown pad attribute: %d" ), aPad->GetAttribute() );
1970 }
1971
1972 const char* property = nullptr;
1973
1974 switch( aPad->GetProperty() )
1975 {
1976 case PAD_PROP::NONE: break; // could be "none"
1977 case PAD_PROP::BGA: property = "pad_prop_bga"; break;
1978 case PAD_PROP::FIDUCIAL_GLBL: property = "pad_prop_fiducial_glob"; break;
1979 case PAD_PROP::FIDUCIAL_LOCAL: property = "pad_prop_fiducial_loc"; break;
1980 case PAD_PROP::TESTPOINT: property = "pad_prop_testpoint"; break;
1981 case PAD_PROP::HEATSINK: property = "pad_prop_heatsink"; break;
1982 case PAD_PROP::CASTELLATED: property = "pad_prop_castellated"; break;
1983 case PAD_PROP::MECHANICAL: property = "pad_prop_mechanical"; break;
1984 case PAD_PROP::PRESSFIT: property = "pad_prop_pressfit"; break;
1985
1986 default:
1987 THROW_IO_ERRORF( _( "unknown pad property: %d" ), aPad->GetProperty() );
1988 }
1989
1990 const char* simElectricalType = nullptr;
1991
1992 switch( aPad->GetSimElectricalType() )
1993 {
1994 case PAD_SIM_ELECTRICAL_TYPE::SOURCE: simElectricalType = "source"; break;
1995 case PAD_SIM_ELECTRICAL_TYPE::SINK: simElectricalType = "sink"; break;
1996 default: simElectricalType = nullptr; break;
1997 }
1998
1999 m_out->Print( "(pad %s %s %s",
2000 m_out->Quotew( aPad->GetNumber() ).c_str(),
2001 type,
2002 shapeName( F_Cu ) );
2003
2004 m_out->Print( "(at %s %s)",
2005 formatInternalUnits( aPad->GetFPRelativePosition() ).c_str(),
2006 aPad->GetOrientation().IsZero() ? ""
2007 : EDA_UNIT_UTILS::FormatAngle( aPad->GetOrientation() ).c_str() );
2008
2009 // Write the stored library size directly: it is the footprint-frame value the parser
2010 // reads back, and avoids a bake/unbake that is not the inverse of GetSize() for a
2011 // pad rotated within the footprint.
2012 m_out->Print( "(size %s)", formatInternalUnits( aPad->Padstack().Size( F_Cu ) ).c_str() );
2013
2014 if( aPad->GetDelta( F_Cu ).x != 0 || aPad->GetDelta( F_Cu ).y != 0 )
2015 m_out->Print( "(rect_delta %s)", formatInternalUnits( aPad->GetDelta( F_Cu ) ).c_str() );
2016
2017 const VECTOR2I drill = aPad->GetDrillShape() == PAD_DRILL_SHAPE::CIRCLE
2018 ? unbakeSizeUniform( aPad->GetDrillSize(), parentFP )
2019 : unbakeSize( aPad->GetDrillSize(), parentFP );
2020 VECTOR2I shapeoffset = aPad->GetOffset( F_Cu );
2021 bool forceShapeOffsetOutput = false;
2022
2024 [&]( PCB_LAYER_ID layer )
2025 {
2026 if( aPad->GetOffset( layer ) != shapeoffset )
2027 forceShapeOffsetOutput = true;
2028 } );
2029
2030 if( drill.x > 0 || drill.y > 0 || shapeoffset.x != 0 || shapeoffset.y != 0 || forceShapeOffsetOutput )
2031 {
2032 m_out->Print( "(drill" );
2033
2034 if( aPad->GetDrillShape() == PAD_DRILL_SHAPE::OBLONG )
2035 m_out->Print( " oval" );
2036
2037 if( drill.x > 0 )
2038 m_out->Print( " %s", formatInternalUnits( drill.x ).c_str() );
2039
2040 if( drill.y > 0 && drill.x != drill.y )
2041 m_out->Print( " %s", formatInternalUnits( drill.y ).c_str() );
2042
2043 // NOTE: Shape offest is a property of the copper shape, not of the drill, but this was put
2044 // in the file format under the drill section. So, it is left here to minimize file format
2045 // changes, but note that the other padstack layers (if present) will have an offset stored
2046 // separately.
2047 if( shapeoffset.x != 0 || shapeoffset.y != 0 || forceShapeOffsetOutput )
2048 m_out->Print( "(offset %s)", formatInternalUnits( aPad->Padstack().Offset( F_Cu ) ).c_str() );
2049
2050 m_out->Print( ")" );
2051 }
2052
2053
2054 if( aPad->Padstack().SecondaryDrill().size.x > 0 )
2055 {
2056 m_out->Print( "(backdrill (size %s) (layers %s %s))",
2057 formatInternalUnits( aPad->Padstack().SecondaryDrill().size.x ).c_str(),
2058 m_out->Quotew( LSET::Name( aPad->Padstack().SecondaryDrill().start ) ).c_str(),
2059 m_out->Quotew( LSET::Name( aPad->Padstack().SecondaryDrill().end ) ).c_str() );
2060 }
2061
2062 if( aPad->Padstack().TertiaryDrill().size.x > 0 )
2063 {
2064 m_out->Print( "(tertiary_drill (size %s) (layers %s %s))",
2065 formatInternalUnits( aPad->Padstack().TertiaryDrill().size.x ).c_str(),
2066 m_out->Quotew( LSET::Name( aPad->Padstack().TertiaryDrill().start ) ).c_str(),
2067 m_out->Quotew( LSET::Name( aPad->Padstack().TertiaryDrill().end ) ).c_str() );
2068 }
2069
2070 auto formatPostMachining =
2071 [&]( const char* aName, const PADSTACK::POST_MACHINING_PROPS& aProps )
2072 {
2073 if( !aProps.mode.has_value() || aProps.mode == PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED )
2074 return;
2075
2076 m_out->Print( "(%s %s",
2077 aName,
2078 aProps.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ? "counterbore"
2079 : "countersink" );
2080
2081 if( aProps.size > 0 )
2082 m_out->Print( " (size %s)", formatInternalUnits( aProps.size ).c_str() );
2083
2084 if( aProps.depth > 0 )
2085 m_out->Print( " (depth %s)", formatInternalUnits( aProps.depth ).c_str() );
2086
2087 if( aProps.angle > 0 )
2088 m_out->Print( " (angle %s)", FormatDouble2Str( aProps.angle / 10.0 ).c_str() );
2089
2090 m_out->Print( ")" );
2091 };
2092
2093 formatPostMachining( "front_post_machining", aPad->Padstack().FrontPostMachining() );
2094 formatPostMachining( "back_post_machining", aPad->Padstack().BackPostMachining() );
2095
2096 // Add pad property, if exists.
2097 if( property )
2098 m_out->Print( "(property %s)", property );
2099
2100 if( simElectricalType )
2101 m_out->Print( "(sim_electrical_type %s)", simElectricalType );
2102
2103 formatLayers( aPad->GetLayerSet(), false /* enumerate layers */ );
2104
2105 if( aPad->GetAttribute() == PAD_ATTRIB::PTH )
2106 {
2107 KICAD_FORMAT::FormatBool( m_out, "remove_unused_layers", aPad->GetRemoveUnconnected() );
2108
2109 if( aPad->GetRemoveUnconnected() )
2110 {
2111 KICAD_FORMAT::FormatBool( m_out, "keep_end_layers", aPad->GetKeepTopBottom() );
2112
2113 if( board ) // Will be nullptr in footprint library
2114 {
2115 m_out->Print( "(zone_layer_connections" );
2116
2117 for( PCB_LAYER_ID layer : board->GetEnabledLayers().CuStack() )
2118 {
2119 if( aPad->GetZoneLayerOverride( layer ) == ZLO_FORCE_FLASHED )
2120 m_out->Print( " %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
2121 }
2122
2123 m_out->Print( ")" );
2124 }
2125 }
2126 }
2127
2128 auto formatCornerProperties =
2129 [&]( PCB_LAYER_ID aLayer )
2130 {
2131 // Output the radius ratio for rounded and chamfered rect pads
2132 if( aPad->GetShape( aLayer ) == PAD_SHAPE::ROUNDRECT
2133 || aPad->GetShape( aLayer ) == PAD_SHAPE::CHAMFERED_RECT)
2134 {
2135 m_out->Print( "(roundrect_rratio %s)",
2136 FormatDouble2Str( aPad->GetRoundRectRadiusRatio( aLayer ) ).c_str() );
2137 }
2138
2139 // Output the chamfer corners for chamfered rect pads
2140 if( aPad->GetShape( aLayer ) == PAD_SHAPE::CHAMFERED_RECT)
2141 {
2142 m_out->Print( "(chamfer_ratio %s)",
2143 FormatDouble2Str( aPad->GetChamferRectRatio( aLayer ) ).c_str() );
2144
2145 m_out->Print( "(chamfer" );
2146
2147 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_TOP_LEFT ) )
2148 m_out->Print( " top_left" );
2149
2150 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_TOP_RIGHT ) )
2151 m_out->Print( " top_right" );
2152
2153 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_BOTTOM_LEFT ) )
2154 m_out->Print( " bottom_left" );
2155
2156 if( ( aPad->GetChamferPositions( aLayer ) & RECT_CHAMFER_BOTTOM_RIGHT ) )
2157 m_out->Print( " bottom_right" );
2158
2159 m_out->Print( ")" );
2160 }
2161
2162 };
2163
2164 // For normal padstacks, this is the one and only set of properties. For complex ones, this
2165 // will represent the front layer properties, and other layers will be formatted below
2166 formatCornerProperties( F_Cu );
2167
2168 // Unconnected pad is default net so don't save it.
2169 if( !( m_ctl & CTL_OMIT_PAD_NETS ) && aPad->GetNetCode() > 0 )
2170 m_out->Print( "(net %s)", m_out->Quotew( aPad->GetNetname() ).c_str() );
2171
2172 // Pin functions and types are closely related to nets, so if CTL_OMIT_NETS is set, omit
2173 // them as well (for instance when saved from library editor).
2174 if( !( m_ctl & CTL_OMIT_PAD_NETS ) )
2175 {
2176 if( !aPad->GetPinFunction().IsEmpty() )
2177 m_out->Print( "(pinfunction %s)", m_out->Quotew( aPad->GetPinFunction() ).c_str() );
2178
2179 if( !aPad->GetPinType().IsEmpty() )
2180 m_out->Print( "(pintype %s)", m_out->Quotew( aPad->GetPinType() ).c_str() );
2181 }
2182
2183 if( aPad->GetPadToDieLength() != 0 )
2184 {
2185 m_out->Print( "(die_length %s)",
2186 formatInternalUnits( aPad->GetPadToDieLength() ).c_str() );
2187 }
2188
2189 if( aPad->GetPadToDieDelay() != 0 )
2190 {
2191 m_out->Print( "(die_delay %s)",
2193 }
2194
2195 if( aPad->GetLocalSolderMaskMargin().has_value() )
2196 {
2197 m_out->Print( "(solder_mask_margin %s)",
2198 formatInternalUnits( aPad->GetLocalSolderMaskMargin().value() ).c_str() );
2199 }
2200
2201 if( aPad->GetLocalSolderPasteMargin().has_value() )
2202 {
2203 m_out->Print( "(solder_paste_margin %s)",
2204 formatInternalUnits( aPad->GetLocalSolderPasteMargin().value() ).c_str() );
2205 }
2206
2207 if( aPad->GetLocalSolderPasteMarginRatio().has_value() )
2208 {
2209 m_out->Print( "(solder_paste_margin_ratio %s)",
2210 FormatDouble2Str( aPad->GetLocalSolderPasteMarginRatio().value() ).c_str() );
2211 }
2212
2213 if( aPad->GetLocalClearance().has_value() )
2214 {
2215 m_out->Print( "(clearance %s)",
2216 formatInternalUnits( aPad->GetLocalClearance().value() ).c_str() );
2217 }
2218
2220 {
2221 m_out->Print( "(zone_connect %d)",
2222 static_cast<int>( aPad->GetLocalZoneConnection() ) );
2223 }
2224
2225 if( aPad->GetLocalThermalSpokeWidthOverride().has_value() )
2226 {
2227 m_out->Print( "(thermal_bridge_width %s)",
2228 formatInternalUnits( aPad->GetLocalThermalSpokeWidthOverride().value() ).c_str() );
2229 }
2230
2232 {
2233 m_out->Print( "(thermal_bridge_angle %s)",
2235 }
2236
2237 if( aPad->GetLocalThermalGapOverride().has_value() )
2238 {
2239 m_out->Print( "(thermal_gap %s)",
2240 formatInternalUnits( aPad->GetLocalThermalGapOverride().value() ).c_str() );
2241 }
2242
2243 auto anchorShape =
2244 [&]( PCB_LAYER_ID aLayer )
2245 {
2246 switch( aPad->GetAnchorPadShape( aLayer ) )
2247 {
2248 case PAD_SHAPE::RECTANGLE: return "rect";
2249 default:
2250 case PAD_SHAPE::CIRCLE: return "circle";
2251 }
2252 };
2253
2254 auto formatPrimitives =
2255 [&]( PCB_LAYER_ID aLayer )
2256 {
2257 m_out->Print( "(primitives" );
2258
2259 // Output all basic shapes
2260 for( const std::shared_ptr<PCB_SHAPE>& primitive : aPad->GetPrimitives( aLayer ) )
2261 {
2262 const SHAPE_T libShape = primitive->GetLibraryShape();
2263
2264 switch( libShape )
2265 {
2266 case SHAPE_T::SEGMENT:
2267 if( primitive->IsProxyItem() )
2268 {
2269 m_out->Print( "(gr_vector (start %s) (end %s)",
2270 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2271 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2272 }
2273 else
2274 {
2275 m_out->Print( "(gr_line (start %s) (end %s)",
2276 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2277 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2278 }
2279 break;
2280
2281 case SHAPE_T::RECTANGLE:
2282 if( primitive->IsProxyItem() )
2283 {
2284 m_out->Print( "(gr_bbox (start %s) (end %s)",
2285 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2286 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2287 }
2288 else
2289 {
2290 m_out->Print( "(gr_rect (start %s) (end %s)",
2291 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2292 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2293
2294 if( primitive->GetCornerRadius() > 0 )
2295 {
2296 m_out->Print( " (radius %s)",
2297 formatInternalUnits( primitive->GetLibraryCornerRadius() ).c_str() );
2298 }
2299 }
2300 break;
2301
2302 case SHAPE_T::ARC:
2303 m_out->Print( "(gr_arc (start %s) (mid %s) (end %s)",
2304 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2305 formatInternalUnits( primitive->GetLibraryArcMid() ).c_str(),
2306 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2307 break;
2308
2309 case SHAPE_T::CIRCLE:
2310 m_out->Print( "(gr_circle (center %s) (end %s)",
2311 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2312 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2313 break;
2314
2315 case SHAPE_T::BEZIER:
2316 // Pad primitives are stored in raw library coordinates and read back
2317 // raw, so emit library coordinates like the other primitive types above.
2318 m_out->Print( "(gr_curve (pts (xy %s) (xy %s) (xy %s) (xy %s))",
2319 formatInternalUnits( primitive->GetLibraryStart() ).c_str(),
2320 formatInternalUnits( primitive->GetLibraryBezierC1() ).c_str(),
2321 formatInternalUnits( primitive->GetLibraryBezierC2() ).c_str(),
2322 formatInternalUnits( primitive->GetLibraryEnd() ).c_str() );
2323 break;
2324
2325 case SHAPE_T::POLY:
2326 if( primitive->IsPolyShapeValid() )
2327 {
2328 const SHAPE_POLY_SET poly = primitive->GetLibraryPolyShape();
2329 const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
2330
2331 m_out->Print( "(gr_poly" );
2332 formatPolyPts( outline );
2333 }
2334 break;
2335
2336 default:
2337 break;
2338 }
2339
2340 if( !primitive->IsProxyItem() )
2341 m_out->Print( "(width %s)", formatInternalUnits( primitive->GetWidth() ).c_str() );
2342
2343 // The filled flag represents if a solid fill is present on circles,
2344 // rectangles and polygons
2345 if( libShape == SHAPE_T::POLY || libShape == SHAPE_T::RECTANGLE || libShape == SHAPE_T::CIRCLE )
2346 {
2347 KICAD_FORMAT::FormatBool( m_out, "fill", primitive->IsSolidFill() );
2348 }
2349
2350 m_out->Print( ")" );
2351 }
2352
2353 m_out->Print( ")" ); // end of (primitives
2354 };
2355
2356 if( aPad->GetShape( F_Cu ) == PAD_SHAPE::CUSTOM )
2357 {
2358 m_out->Print( "(options" );
2359
2361 m_out->Print( "(clearance convexhull)" );
2362 else
2363 m_out->Print( "(clearance outline)" );
2364
2365 // Output the anchor pad shape (circle/rect)
2366 m_out->Print( "(anchor %s)", anchorShape( F_Cu ) );
2367
2368 m_out->Print( ")"); // end of (options ...
2369
2370 // Output graphic primitive of the pad shape
2371 formatPrimitives( F_Cu );
2372 }
2373
2376
2377 if( aPad->Padstack().FrontOuterLayers().has_solder_mask.has_value()
2378 || aPad->Padstack().BackOuterLayers().has_solder_mask.has_value() )
2379 {
2380 m_out->Print( 0, " (tenting " );
2385 m_out->Print( 0, ")" );
2386 }
2387
2389
2390 // TODO: Refactor so that we call formatPadLayer( ALL_LAYERS ) above instead of redundant code
2391 auto formatPadLayer =
2392 [&]( PCB_LAYER_ID aLayer )
2393 {
2394 const PADSTACK& padstack = aPad->Padstack();
2395
2396 m_out->Print( "(shape %s)", shapeName( aLayer ) );
2397
2398 m_out->Print( "(size %s)", formatInternalUnits( padstack.Size( aLayer ) ).c_str() );
2399
2400 const VECTOR2I& delta = aPad->GetDelta( aLayer );
2401
2402 if( delta.x != 0 || delta.y != 0 )
2403 m_out->Print( "(rect_delta %s)", formatInternalUnits( delta ).c_str() );
2404
2405 shapeoffset = aPad->GetOffset( aLayer );
2406
2407 if( shapeoffset.x != 0 || shapeoffset.y != 0 )
2408 m_out->Print( "(offset %s)", formatInternalUnits( padstack.Offset( aLayer ) ).c_str() );
2409
2410 formatCornerProperties( aLayer );
2411
2412 if( aPad->GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
2413 {
2414 m_out->Print( "(options" );
2415
2417 m_out->Print( "(clearance convexhull)" );
2418 else
2419 m_out->Print( "(clearance outline)" );
2420
2421 // Output the anchor pad shape (circle/rect)
2422 m_out->Print( "(anchor %s)", anchorShape( aLayer ) );
2423
2424 m_out->Print( ")" ); // end of (options ...
2425
2426 // Output graphic primitive of the pad shape
2427 formatPrimitives( aLayer );
2428 }
2429
2430 EDA_ANGLE defaultLayerAngle = ANGLE_90;
2431
2432 if( aPad->GetShape( aLayer ) == PAD_SHAPE::CIRCLE ||
2433 ( aPad->GetShape( aLayer ) == PAD_SHAPE::CUSTOM
2434 && aPad->GetAnchorPadShape( aLayer ) == PAD_SHAPE::CIRCLE ) )
2435 {
2436 defaultLayerAngle = ANGLE_45;
2437 }
2438
2439 EDA_ANGLE layerSpokeAngle = padstack.ThermalSpokeAngle( aLayer );
2440
2441 if( layerSpokeAngle != defaultLayerAngle )
2442 {
2443 m_out->Print( "(thermal_bridge_angle %s)",
2444 EDA_UNIT_UTILS::FormatAngle( layerSpokeAngle ).c_str() );
2445 }
2446
2447 if( padstack.ThermalGap( aLayer ).has_value() )
2448 {
2449 m_out->Print( "(thermal_gap %s)",
2450 formatInternalUnits( *padstack.ThermalGap( aLayer ) ).c_str() );
2451 }
2452
2453 if( padstack.ThermalSpokeWidth( aLayer ).has_value() )
2454 {
2455 m_out->Print( "(thermal_bridge_width %s)",
2456 formatInternalUnits( *padstack.ThermalSpokeWidth( aLayer ) ).c_str() );
2457 }
2458
2459 if( padstack.Clearance( aLayer ).has_value() )
2460 {
2461 m_out->Print( "(clearance %s)",
2462 formatInternalUnits( *padstack.Clearance( aLayer ) ).c_str() );
2463 }
2464
2465 if( padstack.ZoneConnection( aLayer ).has_value() )
2466 {
2467 m_out->Print( "(zone_connect %d)",
2468 static_cast<int>( *padstack.ZoneConnection( aLayer ) ) );
2469 }
2470 };
2471
2472
2473 if( aPad->Padstack().Mode() != PADSTACK::MODE::NORMAL )
2474 {
2476 {
2477 m_out->Print( "(padstack (mode front_inner_back)" );
2478
2479 m_out->Print( "(layer \"Inner\"" );
2480 formatPadLayer( PADSTACK::INNER_LAYERS );
2481 m_out->Print( ")" );
2482 m_out->Print( "(layer \"B.Cu\"" );
2483 formatPadLayer( B_Cu );
2484 m_out->Print( ")" );
2485 }
2486 else
2487 {
2488 m_out->Print( "(padstack (mode custom)" );
2489
2490 int layerCount = board ? board->GetCopperLayerCount() : MAX_CU_LAYERS;
2491
2492 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, layerCount ) )
2493 {
2494 if( layer == F_Cu )
2495 continue;
2496
2497 m_out->Print( "(layer %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
2498 formatPadLayer( layer );
2499 m_out->Print( ")" );
2500 }
2501 }
2502
2503 m_out->Print( ")" );
2504 }
2505
2507 m_out->Print( ")" );
2508}
2509
2510
2511void PCB_IO_KICAD_SEXPR::format( const PCB_BARCODE* aBarcode ) const
2512{
2513 wxCHECK_RET( aBarcode != nullptr && m_out != nullptr, "" );
2514
2515 m_out->Print( "(barcode" );
2516
2517 if( aBarcode->IsLocked() )
2518 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2519
2520 m_out->Print( "(at %s %s)",
2521 formatInternalUnits( aBarcode->GetPosition() ).c_str(),
2522 EDA_UNIT_UTILS::FormatAngle( aBarcode->GetAngle() ).c_str() );
2523
2524 formatLayer( aBarcode->GetLayer() );
2525
2526 m_out->Print( "(size %s %s)",
2527 formatInternalUnits( aBarcode->GetWidth() ).c_str(),
2528 formatInternalUnits( aBarcode->GetHeight() ).c_str() );
2529
2530 m_out->Print( "(text %s)", m_out->Quotew( aBarcode->GetText() ).c_str() );
2531
2532 m_out->Print( "(text_height %s)", formatInternalUnits( aBarcode->GetTextSize() ).c_str() );
2533
2534 const char* typeStr = "code39";
2535
2536 switch( aBarcode->GetKind() )
2537 {
2538 case BARCODE_T::CODE_39: typeStr = "code39"; break;
2539 case BARCODE_T::CODE_128: typeStr = "code128"; break;
2540 case BARCODE_T::DATA_MATRIX: typeStr = "datamatrix"; break;
2541 case BARCODE_T::QR_CODE: typeStr = "qr"; break;
2542 case BARCODE_T::MICRO_QR_CODE: typeStr = "microqr"; break;
2543 }
2544
2545 m_out->Print( "(type %s)", typeStr );
2546
2547 if( aBarcode->GetKind() == BARCODE_T::QR_CODE
2548 || aBarcode->GetKind() == BARCODE_T::MICRO_QR_CODE )
2549 {
2550 const char* eccStr = "L";
2551 switch( aBarcode->GetErrorCorrection() )
2552 {
2553 case BARCODE_ECC_T::L: eccStr = "L"; break;
2554 case BARCODE_ECC_T::M: eccStr = "M"; break;
2555 case BARCODE_ECC_T::Q: eccStr = "Q"; break;
2556 case BARCODE_ECC_T::H: eccStr = "H"; break;
2557 }
2558
2559 m_out->Print( "(ecc_level %s)", eccStr );
2560 }
2561
2562 KICAD_FORMAT::FormatBool( m_out, "hide", !aBarcode->GetShowText() );
2563 KICAD_FORMAT::FormatBool( m_out, "knockout", aBarcode->IsKnockout() );
2564
2565 if( aBarcode->GetMargin().x != 0 || aBarcode->GetMargin().y != 0 )
2566 {
2567 m_out->Print( "(margins %s %s)", formatInternalUnits( aBarcode->GetMargin().x ).c_str(),
2568 formatInternalUnits( aBarcode->GetMargin().y ).c_str() );
2569 }
2570
2572
2574 m_out->Print( ")" );
2575}
2576
2577
2578void PCB_IO_KICAD_SEXPR::format( const PCB_TEXT* aText ) const
2579{
2580 FOOTPRINT* parentFP = aText->GetParentFootprint();
2581 std::string prefix;
2582 std::string type;
2583 VECTOR2I pos = aText->GetTextPos();
2584 // Match the Type() dispatch in Format(); a field body is headerless, so it is only valid
2585 // inside the (property ...) its caller opened
2586 const PCB_FIELD* field = aText->Type() == PCB_FIELD_T ? static_cast<const PCB_FIELD*>( aText )
2587 : nullptr;
2588
2589 // Always format dimension text as gr_text
2590 if( dynamic_cast<const PCB_DIMENSION_BASE*>( aText ) )
2591 parentFP = nullptr;
2592
2593 if( parentFP )
2594 {
2595 prefix = "fp";
2596 type = "user";
2597
2598 pos = parentFP->GetTransform().InverseApply( pos );
2599 }
2600 else
2601 {
2602 prefix = "gr";
2603 }
2604
2605 if( !field )
2606 {
2607 m_out->Print( "(%s_text %s %s",
2608 prefix.c_str(),
2609 type.c_str(),
2610 m_out->Quotew( aText->GetText() ).c_str() );
2611
2612 if( aText->IsLocked() )
2613 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2614 }
2615
2616 m_out->Print( "(at %s %s)",
2617 formatInternalUnits( pos ).c_str(),
2618 EDA_UNIT_UTILS::FormatAngle( aText->GetTextAngle() ).c_str() );
2619
2620 if( parentFP && !aText->IsKeepUpright() )
2621 KICAD_FORMAT::FormatBool( m_out, "unlocked", true );
2622
2623 formatLayer( aText->GetLayer(), aText->IsKnockout() );
2624
2625 if( field && !field->IsVisible() )
2626 KICAD_FORMAT::FormatBool( m_out, "hide", true );
2627
2629
2630 // Currently, texts have no specific color and no hyperlink.
2631 // so ensure they are never written in kicad_pcb file
2632 int ctl_flags = CTL_OMIT_COLOR | CTL_OMIT_HYPERLINK;
2633
2634 if( parentFP )
2635 {
2636 EDA_TEXT* mut = const_cast<EDA_TEXT*>( static_cast<const EDA_TEXT*>( aText ) );
2637 const VECTOR2I savedSize = mut->GetTextSize();
2638 const int savedThickness = mut->GetTextThickness();
2639 const bool mutateThickness = !mut->GetAutoThickness();
2640
2641 mut->SetTextSize( unbakeSize( savedSize, parentFP ) );
2642
2643 if( mutateThickness )
2644 mut->SetTextThickness( unbakeLinear( savedThickness, parentFP ) );
2645
2646 aText->EDA_TEXT::Format( m_out, ctl_flags );
2647
2648 mut->SetTextSize( savedSize );
2649
2650 if( mutateThickness )
2651 mut->SetTextThickness( savedThickness );
2652 }
2653 else
2654 {
2655 aText->EDA_TEXT::Format( m_out, ctl_flags );
2656 }
2657
2658 if( aText->GetFont() && aText->GetFont()->IsOutline() )
2659 formatRenderCache( aText );
2660
2662
2663 if( !field )
2664 m_out->Print( ")" );
2665}
2666
2667
2668void PCB_IO_KICAD_SEXPR::format( const PCB_TEXTBOX* aTextBox ) const
2669{
2670 FOOTPRINT* parentFP = aTextBox->GetParentFootprint();
2671
2672 m_out->Print( "(%s %s",
2673 aTextBox->Type() == PCB_TABLECELL_T ? "table_cell"
2674 : parentFP ? "fp_text_box"
2675 : "gr_text_box",
2676 m_out->Quotew( aTextBox->GetText() ).c_str() );
2677
2678 if( aTextBox->IsLocked() )
2679 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2680
2681 // Use the lib-frame shape. The board-frame shape can flip to POLY for
2682 // rendering when the FP rotation is non-cardinal.
2683 SHAPE_T libShape = aTextBox->GetLibraryShape();
2684
2685 if( libShape == SHAPE_T::RECTANGLE )
2686 {
2687 m_out->Print( "(start %s) (end %s)", formatInternalUnits( aTextBox->GetLibraryStart() ).c_str(),
2688 formatInternalUnits( aTextBox->GetLibraryEnd() ).c_str() );
2689 }
2690 else if( libShape == SHAPE_T::POLY )
2691 {
2692 // Fall back to the runtime polygon if the lib copy was never seeded.
2693 const SHAPE_POLY_SET& libPoly = aTextBox->GetLibPoly();
2694 const bool haveLibPoly = libPoly.OutlineCount() > 0;
2695 const SHAPE_POLY_SET& poly = haveLibPoly ? libPoly : aTextBox->GetPolyShape();
2696
2697 if( poly.OutlineCount() > 0 )
2698 formatPolyPts( poly.Outline( 0 ), haveLibPoly ? nullptr : parentFP );
2699 }
2700 else
2701 {
2702 UNIMPLEMENTED_FOR( aTextBox->SHAPE_T_asString() );
2703 }
2704
2705 m_out->Print( "(margins %s %s %s %s)",
2706 formatInternalUnits( aTextBox->GetMarginLeft() ).c_str(),
2707 formatInternalUnits( aTextBox->GetMarginTop() ).c_str(),
2708 formatInternalUnits( aTextBox->GetMarginRight() ).c_str(),
2709 formatInternalUnits( aTextBox->GetMarginBottom() ).c_str() );
2710
2711 if( const PCB_TABLECELL* cell = dynamic_cast<const PCB_TABLECELL*>( aTextBox ) )
2712 m_out->Print( "(span %d %d)", cell->GetColSpan(), cell->GetRowSpan() );
2713
2714 EDA_ANGLE angle = aTextBox->GetTextAngle();
2715
2716 if( parentFP )
2717 {
2718 angle -= parentFP->GetOrientation();
2719 angle.Normalize720();
2720 }
2721
2722 if( !angle.IsZero() )
2723 m_out->Print( "(angle %s)", EDA_UNIT_UTILS::FormatAngle( angle ).c_str() );
2724
2725 formatLayer( aTextBox->GetLayer() );
2726
2728
2729 aTextBox->EDA_TEXT::Format( m_out, 0 );
2730
2731 if( aTextBox->Type() != PCB_TABLECELL_T )
2732 {
2733 KICAD_FORMAT::FormatBool( m_out, "border", aTextBox->IsBorderEnabled() );
2734 aTextBox->GetStroke().Format( m_out, pcbIUScale );
2735 }
2736
2737 KICAD_FORMAT::FormatBool( m_out, "knockout", aTextBox->IsKnockout() );
2738
2739 if( aTextBox->GetFont() && aTextBox->GetFont()->IsOutline() )
2740 formatRenderCache( aTextBox );
2741
2743 m_out->Print( ")" );
2744}
2745
2746
2748{
2749 // Carries geometry and cells only. Identity belongs to whatever encloses this, or a
2750 // drill chart would write its uuid and layer twice and the second copy would win on load
2751 m_out->Print( "(column_count %d)", aTable->GetColCount() );
2752
2753 m_out->Print( "(border" );
2754 KICAD_FORMAT::FormatBool( m_out, "external", aTable->StrokeExternal() );
2756
2757 if( aTable->StrokeExternal() || aTable->StrokeHeaderSeparator() )
2758 aTable->GetBorderStroke().Format( m_out, pcbIUScale );
2759
2760 m_out->Print( ")" ); // Close `border` token.
2761
2762 m_out->Print( "(separators" );
2763 KICAD_FORMAT::FormatBool( m_out, "rows", aTable->StrokeRows() );
2764 KICAD_FORMAT::FormatBool( m_out, "cols", aTable->StrokeColumns() );
2765
2766 if( aTable->StrokeRows() || aTable->StrokeColumns() )
2768
2769 m_out->Print( ")" ); // Close `separators` token.
2770
2771 m_out->Print( "(column_widths" );
2772
2773 for( int col = 0; col < aTable->GetColCount(); ++col )
2774 m_out->Print( " %s", formatInternalUnits( aTable->GetColWidth( col ) ).c_str() );
2775
2776 m_out->Print( ")" );
2777
2778 m_out->Print( "(row_heights" );
2779
2780 for( int row = 0; row < aTable->GetRowCount(); ++row )
2781 m_out->Print( " %s", formatInternalUnits( aTable->GetRowHeight( row ) ).c_str() );
2782
2783 m_out->Print( ")" );
2784
2785 m_out->Print( "(cells" );
2786
2787 for( PCB_TABLECELL* cell : aTable->GetCells() )
2788 format( static_cast<PCB_TEXTBOX*>( cell ) );
2789
2790 m_out->Print( ")" ); // Close `cells` token.
2791
2793}
2794
2795
2797{
2798 wxCHECK_RET( aMap != nullptr && m_out != nullptr, "" );
2799
2800 m_out->Print( "(drill_map " );
2801
2803
2804 if( aMap->IsLocked() )
2805 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2806
2807 formatLayer( aMap->GetLayer() );
2808
2809 // An offset applied to every mark, not a place of its own. Zero puts the marks on their
2810 // holes
2811 m_out->Print( "(offset %s)", formatInternalUnits( aMap->GetOffset() ).c_str() );
2812 m_out->Print( "(size %s)", formatInternalUnits( aMap->GetSymbolSize() ).c_str() );
2813
2814 if( aMap->GetAllSpans() )
2815 {
2816 m_out->Print( "(span all)" );
2817 }
2818 else
2819 {
2820 m_out->Print( "(span " );
2821 formatDrillSpan( m_out, aMap->GetSpan() );
2822 m_out->Print( ")" );
2823 }
2824
2825 KICAD_FORMAT::FormatBool( m_out, "outline_slots", aMap->GetOutlineSlots() );
2826 KICAD_FORMAT::FormatBool( m_out, "guide_cross", aMap->GetGuideCross() );
2828
2829 m_out->Print( ")" ); // Close `drill_map` token.
2830}
2831
2832
2834{
2835 wxCHECK_RET( aTable != nullptr && m_out != nullptr, "" );
2836
2837 switch( aTable->Type() )
2838 {
2839 case PCB_DRILL_CHART_T: m_out->Print( "(drill_chart " ); break;
2840
2841 default:
2842 wxFAIL_MSG( wxT( "Unhandled generated table type " ) + aTable->GetClass() );
2843 return;
2844 }
2845
2847
2848 if( aTable->IsLocked() )
2849 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2850
2851 formatLayer( aTable->GetLayer() );
2852
2853 // Anything the reader would arrive at on its own is left out. A table nobody has
2854 // reconfigured is a handful of bytes rather than a page of them
2855 const GENERATED_TABLE_SCHEMA& schema = aTable->Schema();
2856
2857 if( aTable->GetUnits() != schema.DefaultUnits() )
2858 m_out->Print( "(units %s)", GeneratedTableUnitsToken( aTable->GetUnits() ) );
2859
2860 if( aTable->GetPrecision() != schema.DefaultPrecision() )
2861 m_out->Print( "(precision %d)", aTable->GetPrecision() );
2862
2863 for( const GENERATED_TABLE_COLUMN& col : aTable->Columns() )
2864 {
2865 const GENERATED_TABLE_COLUMN_DEF* def = schema.Find( col.m_Id );
2866
2867 m_out->Print( "(column (id %s)", schema.Token( col.m_Id ) );
2868
2869 // Compared against the def's own literal, never DefaultColumn()'s translated text, so
2870 // a heading written under one UI language reads back unchanged under another
2871 if( !def || col.m_Heading != wxString( def->m_Heading ) )
2872 m_out->Print( "(name %s)", m_out->Quotew( col.m_Heading ).c_str() );
2873
2874 if( !def || col.m_Align != def->m_Align )
2875 m_out->Print( "(justify %s)", GeneratedTableAlignToken( col.m_Align ) );
2876
2877 if( col.m_Width > 0 )
2878 m_out->Print( "(width %s)", formatInternalUnits( col.m_Width ).c_str() );
2879
2880 m_out->Print( ")" );
2881 }
2882
2883 if( !aTable->RowKeys().empty() )
2884 {
2885 // What each row reports, so a rebuild after the board has changed can hand a row's
2886 // formatting to the row that still reports the same thing
2887 m_out->Print( "(row_keys" );
2888
2889 for( const auto& [row, key] : aTable->RowKeys() )
2890 m_out->Print( "(key %d %s)", row, m_out->Quotew( wxString::FromUTF8( key ) ).c_str() );
2891
2892 m_out->Print( ")" );
2893 }
2894
2896
2897 // The geometry and cells a generated table shares with any other table, written the same way
2898 formatTableData( aTable );
2899
2900 m_out->Print( ")" ); // Close the table's kind token
2901}
2902
2903
2905{
2906 switch( aTable->Type() )
2907 {
2908 case PCB_DRILL_CHART_T:
2909 {
2910 const PCB_DRILL_CHART* chart = static_cast<const PCB_DRILL_CHART*>( aTable );
2912 const DRILL_CHART_FILTER& filter = chart->Filter();
2913
2914 if( !( filter == DRILL_CHART_FILTER() ) )
2915 {
2916 m_out->Print( "(filter" );
2917 KICAD_FORMAT::FormatBool( m_out, "plated", filter.m_Plated );
2918 KICAD_FORMAT::FormatBool( m_out, "npth", filter.m_NonPlated );
2919 KICAD_FORMAT::FormatBool( m_out, "vias", filter.m_Vias );
2920 KICAD_FORMAT::FormatBool( m_out, "slots", filter.m_Slots );
2921 KICAD_FORMAT::FormatBool( m_out, "backdrill", filter.m_Backdrills );
2922 KICAD_FORMAT::FormatBool( m_out, "castellated", filter.m_Castellated );
2923 m_out->Print( ")" );
2924 }
2925
2926 if( chart->GetShowTotals() != defaults.GetShowTotals() )
2927 KICAD_FORMAT::FormatBool( m_out, "totals", chart->GetShowTotals() );
2928
2929 if( chart->GetSymbolColumn() >= 0 && !chart->RowShapes().empty() )
2930 {
2931 // Generated payload, like the cell text. A shape mark has no text of its own, so
2932 // without this the symbol column comes back blank
2933 m_out->Print( "(row_shapes (column %d)", chart->GetSymbolColumn() );
2934
2935 for( const auto& [row, shapeIndex] : chart->RowShapes() )
2936 m_out->Print( "(shape %d %d)", row, shapeIndex );
2937
2938 m_out->Print( ")" );
2939 }
2940
2941 break;
2942 }
2943
2944 default:
2945 break;
2946 }
2947}
2948
2949
2950void PCB_IO_KICAD_SEXPR::format( const PCB_TABLE* aTable ) const
2951{
2952 wxCHECK_RET( aTable != nullptr && m_out != nullptr, "" );
2953
2954 m_out->Print( "(table " );
2955
2957
2958 if( aTable->IsLocked() )
2959 KICAD_FORMAT::FormatBool( m_out, "locked", true );
2960
2961 formatLayer( aTable->GetLayer() );
2962
2963 formatTableData( aTable );
2964
2965 m_out->Print( ")" ); // Close `table` token.
2966}
2967
2968
2969void PCB_IO_KICAD_SEXPR::format( const PCB_GROUP* aGroup ) const
2970{
2971 wxArrayString memberIds;
2972
2973 // Validate member pointers against the board cache to avoid use-after-free on dangling
2974 // pointers (e.g. when a group held a reference to a deleted item). This validation only
2975 // applies when the group itself is part of m_board; for groups created off-board (e.g. a
2976 // DeepClone() used by the clipboard) the cache contains the originals, not our clones, so
2977 // skip the validation in that case and trust the member pointers.
2978 //
2979 // BOARD::IsItemIndexedById() probes the board's inverse pointer index without dereferencing
2980 // the candidate, so it is safe to call on a possibly-dangling member.
2981 bool validateAgainstBoard = m_board && m_board->IsItemIndexedById( aGroup );
2982
2983 memberIds.Alloc( aGroup->GetItems().size() );
2984
2985 for( EDA_ITEM* member : aGroup->GetItems() )
2986 {
2987 if( !validateAgainstBoard
2988 || m_board->IsItemIndexedById( static_cast<const BOARD_ITEM*>( member ) ) )
2989 {
2990 memberIds.Add( member->m_Uuid.AsString() );
2991 }
2992 }
2993
2994 if( memberIds.empty() )
2995 return;
2996
2997 m_out->Print( "(group %s", m_out->Quotew( aGroup->GetName() ).c_str() );
2998
3000
3001 if( aGroup->IsLocked() )
3002 KICAD_FORMAT::FormatBool( m_out, "locked", true );
3003
3004 if( aGroup->HasDesignBlockLink() )
3005 m_out->Print( "(lib_id \"%s\")", aGroup->GetDesignBlockLibId().Format().c_str() );
3006
3007 memberIds.Sort();
3008
3009 m_out->Print( "(members" );
3010
3011 for( const wxString& memberId : memberIds )
3012 m_out->Print( " %s", m_out->Quotew( memberId ).c_str() );
3013
3014 m_out->Print( ")" ); // Close `members` token.
3015
3017 m_out->Print( ")" ); // Close `group` token.
3018}
3019
3020
3021void PCB_IO_KICAD_SEXPR::format( const PCB_CONSTRAINT* aConstraint ) const
3022{
3023 const std::vector<CONSTRAINT_MEMBER>& members = aConstraint->GetMembers();
3024
3025 if( members.empty() )
3026 return;
3027
3028 // Members are KIID references (not pointers), so unlike format(PCB_GROUP*) there is no
3029 // use-after-free risk: every member is written verbatim, including one whose item was deleted,
3030 // so the constraint round-trips in its error state rather than silently losing the reference.
3031 m_out->Print( "(constraint (type %s)", ConstraintTypeToken( aConstraint->GetConstraintType() ) );
3032
3033 KICAD_FORMAT::FormatUuid( m_out, aConstraint->m_Uuid );
3034
3035 m_out->Print( "(members" );
3036
3037 for( const CONSTRAINT_MEMBER& member : members )
3038 {
3039 // Only VERTEX carries an ordinal others stay two-token
3040 if( member.m_anchor == CONSTRAINT_ANCHOR::VERTEX )
3041 {
3042 m_out->Print( "(member %s %s %d)", m_out->Quotew( member.m_item.AsString() ).c_str(),
3043 ConstraintAnchorToken( member.m_anchor ), member.m_index );
3044 }
3045 else
3046 {
3047 m_out->Print( "(member %s %s)", m_out->Quotew( member.m_item.AsString() ).c_str(),
3048 ConstraintAnchorToken( member.m_anchor ) );
3049 }
3050 }
3051
3052 m_out->Print( ")" ); // Close `members` token.
3053
3054 if( aConstraint->HasValue() )
3055 {
3056 // Length/radius values are stored in IU but written in mm like every other dimension;
3057 // angle values are written verbatim in degrees.
3058 double value = *aConstraint->GetValue();
3059
3060 if( ConstraintValueIsLength( aConstraint->GetConstraintType() ) )
3061 value /= pcbIUScale.IU_PER_MM;
3062
3063 m_out->Print( "(value %s)", FormatDouble2Str( value ).c_str() );
3064 }
3065
3066 if( !aConstraint->IsDriving() )
3067 KICAD_FORMAT::FormatBool( m_out, "driving", false );
3068
3070 m_out->Print( ")" ); // Close `constraint` token.
3071}
3072
3073
3074void PCB_IO_KICAD_SEXPR::format( const PCB_GENERATOR* aGenerator ) const
3075{
3076 // Some conditions appear to still be creating ghost tuning patterns. Don't save them.
3077 // A microvia stack with no members is the same kind of ghost: it reloads with no size, so
3078 // it can be neither seen nor picked, and with no net to rebuild its vias on.
3079 if( aGenerator->GetItems().empty()
3080 && ( aGenerator->GetGeneratorType() == wxT( "tuning_pattern" )
3081 || aGenerator->GetGeneratorType() == wxT( "via_stack" ) ) )
3082 {
3083 return;
3084 }
3085
3086 m_out->Print( "(generated" );
3087
3088 KICAD_FORMAT::FormatUuid( m_out, aGenerator->m_Uuid );
3089
3090 m_out->Print( "(type %s) (name %s) (layer %s)",
3091 TO_UTF8( aGenerator->GetGeneratorType() ),
3092 m_out->Quotew( aGenerator->GetName() ).c_str(),
3093 m_out->Quotew( LSET::Name( aGenerator->GetLayer() ) ).c_str() );
3094
3095 if( aGenerator->IsLocked() )
3096 KICAD_FORMAT::FormatBool( m_out, "locked", true );
3097
3098 for( const auto& [key, value] : aGenerator->GetProperties() )
3099 {
3100 if( value.CheckType<double>() || value.CheckType<int>() || value.CheckType<long>()
3101 || value.CheckType<long long>() )
3102 {
3103 double val;
3104
3105 if( !value.GetAs( &val ) )
3106 continue;
3107
3108 std::string buf = fmt::format( "{:.10g}", val );
3109
3110 // Don't quote numbers
3111 m_out->Print( "(%s %s)", key.c_str(), buf.c_str() );
3112 }
3113 else if( value.CheckType<bool>() )
3114 {
3115 bool val;
3116 value.GetAs( &val );
3117
3118 KICAD_FORMAT::FormatBool( m_out, key, val );
3119 }
3120 else if( value.CheckType<VECTOR2I>() )
3121 {
3122 VECTOR2I val;
3123 value.GetAs( &val );
3124
3125 m_out->Print( "(%s (xy %s))",
3126 key.c_str(),
3127 formatInternalUnits( val ).c_str() );
3128 }
3129 else if( value.CheckType<SHAPE_LINE_CHAIN>() )
3130 {
3131 SHAPE_LINE_CHAIN val;
3132 value.GetAs( &val );
3133
3134 m_out->Print( "(%s ", key.c_str() );
3135 formatPolyPts( val );
3136 m_out->Print( ")" );
3137 }
3138 else if( value.CheckType<std::vector<VECTOR2I>>() )
3139 {
3140 // Unitless integer pairs
3141 std::vector<VECTOR2I> val;
3142 value.GetAs( &val );
3143
3144 m_out->Print( "(%s (cells", key.c_str() );
3145
3146 for( const VECTOR2I& cell : val )
3147 m_out->Print( "(ij %d %d)", cell.x, cell.y );
3148
3149 m_out->Print( "))" );
3150 }
3151 else
3152 {
3153 wxString val;
3154
3155 if( value.CheckType<wxString>() )
3156 {
3157 value.GetAs( &val );
3158 }
3159 else if( value.CheckType<std::string>() )
3160 {
3161 std::string str;
3162 value.GetAs( &str );
3163
3164 val = wxString::FromUTF8( str );
3165 }
3166
3167 m_out->Print( "(%s %s)", key.c_str(), m_out->Quotew( val ).c_str() );
3168 }
3169 }
3170
3171 std::vector<std::pair<wxString, const BOARD_ITEM*>> templateItems = aGenerator->GetTemplateItems();
3172
3173 if( !templateItems.empty() )
3174 {
3175 m_out->Print( "(templates" );
3176
3177 for( const auto& [name, item] : templateItems )
3178 {
3179 if( !item )
3180 continue;
3181
3182 m_out->Print( "(template (name %s)", m_out->Quotew( name ).c_str() );
3183 Format( item );
3184 m_out->Print( ")" ); // Close `template` token.
3185 }
3186
3187 m_out->Print( ")" ); // Close `templates` token.
3188 }
3189
3190 wxArrayString memberIds;
3191
3192 for( EDA_ITEM* member : aGenerator->GetItems() )
3193 memberIds.Add( member->m_Uuid.AsString() );
3194
3195 memberIds.Sort();
3196
3197 m_out->Print( "(members" );
3198
3199 for( const wxString& memberId : memberIds )
3200 m_out->Print( " %s", m_out->Quotew( memberId ).c_str() );
3201
3202 m_out->Print( ")" ); // Close `members` token.
3203
3205 m_out->Print( ")" ); // Close `generated` token.
3206}
3207
3208
3209void PCB_IO_KICAD_SEXPR::format( const PCB_TRACK* aTrack ) const
3210{
3211 if( aTrack->Type() == PCB_VIA_T )
3212 {
3213 PCB_LAYER_ID layer1, layer2;
3214
3215 const PCB_VIA* via = static_cast<const PCB_VIA*>( aTrack );
3216 const BOARD* board = via->GetBoard();
3217
3218 wxCHECK_RET( board != nullptr, wxT( "Via has no parent." ) );
3219
3220 m_out->Print( "(via" );
3221
3222 via->LayerPair( &layer1, &layer2 );
3223
3224 switch( via->GetViaType() )
3225 {
3226 case VIATYPE::THROUGH: // Default shape not saved.
3227 break;
3228
3229 case VIATYPE::BLIND:
3230 m_out->Print( " blind " );
3231 break;
3232
3233 case VIATYPE::BURIED:
3234 m_out->Print( " buried " );
3235 break;
3236
3237 case VIATYPE::MICROVIA:
3238 m_out->Print( " micro " );
3239 break;
3240
3241 default:
3242 THROW_IO_ERRORF( _( "unknown via type %d" ), via->GetViaType() );
3243 }
3244
3245 m_out->Print( "(at %s) (size %s)",
3246 formatInternalUnits( aTrack->GetStart() ).c_str(),
3247 formatInternalUnits( via->GetWidth( F_Cu ) ).c_str() );
3248
3249 // Old boards were using UNDEFINED_DRILL_DIAMETER value in file for via drill when
3250 // via drill was the netclass value.
3251 // recent boards always set the via drill to the actual value, but now we need to
3252 // always store the drill value, because netclass value is not stored in the board file.
3253 // Otherwise the drill value of some (old) vias can be unknown
3254 if( via->GetDrill() != UNDEFINED_DRILL_DIAMETER )
3255 m_out->Print( "(drill %s)", formatInternalUnits( via->GetDrill() ).c_str() );
3256 else
3257 m_out->Print( "(drill %s)", formatInternalUnits( via->GetDrillValue() ).c_str() );
3258
3259 if( via->Padstack().SecondaryDrill().size.x > 0 )
3260 {
3261 m_out->Print( "(backdrill (size %s) (layers %s %s))",
3262 formatInternalUnits( via->Padstack().SecondaryDrill().size.x ).c_str(),
3263 m_out->Quotew( LSET::Name( via->Padstack().SecondaryDrill().start ) ).c_str(),
3264 m_out->Quotew( LSET::Name( via->Padstack().SecondaryDrill().end ) ).c_str() );
3265 }
3266
3267 if( via->Padstack().TertiaryDrill().size.x > 0 )
3268 {
3269 m_out->Print( "(tertiary_drill (size %s) (layers %s %s))",
3270 formatInternalUnits( via->Padstack().TertiaryDrill().size.x ).c_str(),
3271 m_out->Quotew( LSET::Name( via->Padstack().TertiaryDrill().start ) ).c_str(),
3272 m_out->Quotew( LSET::Name( via->Padstack().TertiaryDrill().end ) ).c_str() );
3273 }
3274
3275 auto formatPostMachining = [&]( const char* aName, const PADSTACK::POST_MACHINING_PROPS& aProps )
3276 {
3277 if( !aProps.mode.has_value() || aProps.mode == PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED )
3278 return;
3279
3280 m_out->Print( "(%s %s", aName,
3281 aProps.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ? "counterbore" : "countersink" );
3282
3283 if( aProps.size > 0 )
3284 m_out->Print( " (size %s)", formatInternalUnits( aProps.size ).c_str() );
3285
3286 if( aProps.depth > 0 )
3287 m_out->Print( " (depth %s)", formatInternalUnits( aProps.depth ).c_str() );
3288
3289 if( aProps.angle > 0 )
3290 m_out->Print( " (angle %s)", FormatDouble2Str( aProps.angle / 10.0 ).c_str() );
3291
3292 m_out->Print( ")" );
3293 };
3294
3295 formatPostMachining( "front_post_machining", via->Padstack().FrontPostMachining() );
3296 formatPostMachining( "back_post_machining", via->Padstack().BackPostMachining() );
3297
3298 m_out->Print( "(layers %s %s)",
3299 m_out->Quotew( LSET::Name( layer1 ) ).c_str(),
3300 m_out->Quotew( LSET::Name( layer2 ) ).c_str() );
3301
3302 switch( via->Padstack().UnconnectedLayerMode() )
3303 {
3305 KICAD_FORMAT::FormatBool( m_out, "remove_unused_layers", true );
3306 KICAD_FORMAT::FormatBool( m_out, "keep_end_layers", false );
3307 break;
3308
3310 KICAD_FORMAT::FormatBool( m_out, "remove_unused_layers", true );
3311 KICAD_FORMAT::FormatBool( m_out, "keep_end_layers", true );
3312 break;
3313
3315 KICAD_FORMAT::FormatBool( m_out, "start_end_only", true );
3316 break;
3317
3319 break;
3320 }
3321
3322 if( via->IsLocked() )
3323 KICAD_FORMAT::FormatBool( m_out, "locked", true );
3324
3325 if( via->GetIsFree() )
3326 KICAD_FORMAT::FormatBool( m_out, "free", true );
3327
3328 if( via->GetRemoveUnconnected() )
3329 {
3330 m_out->Print( "(zone_layer_connections" );
3331
3332 for( PCB_LAYER_ID layer : board->GetEnabledLayers().CuStack() )
3333 {
3334 if( via->GetZoneLayerOverride( layer ) == ZLO_FORCE_FLASHED )
3335 m_out->Print( " %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
3336 }
3337
3338 m_out->Print( ")" );
3339 }
3340
3341 const PADSTACK& padstack = via->Padstack();
3342
3343 if( padstack.FrontOuterLayers().has_solder_mask.has_value()
3344 || padstack.BackOuterLayers().has_solder_mask.has_value() )
3345 {
3346 m_out->Print( 0, " (tenting " );
3348 padstack.FrontOuterLayers().has_solder_mask );
3350 padstack.BackOuterLayers().has_solder_mask );
3351 m_out->Print( 0, ")" );
3352 }
3353
3354 if( padstack.Drill().is_capped.has_value() )
3355 KICAD_FORMAT::FormatOptBool( m_out, "capping", padstack.Drill().is_capped );
3356
3357 if( padstack.FrontOuterLayers().has_covering.has_value()
3358 || padstack.BackOuterLayers().has_covering.has_value() )
3359 {
3360 m_out->Print( 0, " (covering " );
3362 padstack.FrontOuterLayers().has_covering );
3364 padstack.BackOuterLayers().has_covering );
3365 m_out->Print( 0, ")" );
3366 }
3367
3368 if( padstack.FrontOuterLayers().has_plugging.has_value()
3369 || padstack.BackOuterLayers().has_plugging.has_value() )
3370 {
3371 m_out->Print( 0, " (plugging " );
3373 padstack.FrontOuterLayers().has_plugging );
3375 padstack.BackOuterLayers().has_plugging );
3376 m_out->Print( 0, ")" );
3377 }
3378
3379 if( padstack.Drill().is_filled.has_value() )
3380 KICAD_FORMAT::FormatOptBool( m_out, "filling", padstack.Drill().is_filled );
3381
3382 if( padstack.Mode() != PADSTACK::MODE::NORMAL )
3383 {
3384 m_out->Print( "(padstack" );
3385
3386 if( padstack.Mode() == PADSTACK::MODE::FRONT_INNER_BACK )
3387 {
3388 m_out->Print( "(mode front_inner_back)" );
3389
3390 m_out->Print( "(layer \"Inner\"" );
3391 m_out->Print( "(size %s)",
3392 formatInternalUnits( padstack.Size( PADSTACK::INNER_LAYERS ).x ).c_str() );
3393 m_out->Print( ")" );
3394 m_out->Print( "(layer \"B.Cu\"" );
3395 m_out->Print( "(size %s)",
3396 formatInternalUnits( padstack.Size( B_Cu ).x ).c_str() );
3397 m_out->Print( ")" );
3398 }
3399 else
3400 {
3401 m_out->Print( "(mode custom)" );
3402
3403 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, board->GetCopperLayerCount() ) )
3404 {
3405 if( layer == F_Cu )
3406 continue;
3407
3408 m_out->Print( "(layer %s", m_out->Quotew( LSET::Name( layer ) ).c_str() );
3409 m_out->Print( "(size %s)",
3410 formatInternalUnits( padstack.Size( layer ).x ).c_str() );
3411 m_out->Print( ")" );
3412 }
3413 }
3414
3415 m_out->Print( ")" );
3416 }
3417
3418 if( !isDefaultTeardropParameters( via->GetTeardropParams() ) )
3419 formatTeardropParameters( via->GetTeardropParams() );
3420 }
3421 else
3422 {
3423 if( aTrack->Type() == PCB_ARC_T )
3424 {
3425 const PCB_ARC* arc = static_cast<const PCB_ARC*>( aTrack );
3426
3427 m_out->Print( "(arc (start %s) (mid %s) (end %s) (width %s)",
3428 formatInternalUnits( arc->GetStart() ).c_str(),
3429 formatInternalUnits( arc->GetMid() ).c_str(),
3430 formatInternalUnits( arc->GetEnd() ).c_str(),
3431 formatInternalUnits( arc->GetWidth() ).c_str() );
3432 }
3433 else
3434 {
3435 m_out->Print( "(segment (start %s) (end %s) (width %s)",
3436 formatInternalUnits( aTrack->GetStart() ).c_str(),
3437 formatInternalUnits( aTrack->GetEnd() ).c_str(),
3438 formatInternalUnits( aTrack->GetWidth() ).c_str() );
3439 }
3440
3441 if( aTrack->IsLocked() )
3442 KICAD_FORMAT::FormatBool( m_out, "locked", true );
3443
3444 if( aTrack->GetLayerSet().count() > 1 )
3445 formatLayers( aTrack->GetLayerSet(), false /* enumerate layers */ );
3446 else
3447 formatLayer( aTrack->GetLayer() );
3448
3449 if( aTrack->HasSolderMask()
3450 && aTrack->GetLocalSolderMaskMargin().has_value()
3451 && IsExternalCopperLayer( aTrack->GetLayer() ) )
3452 {
3453 m_out->Print( "(solder_mask_margin %s)",
3454 formatInternalUnits( aTrack->GetLocalSolderMaskMargin().value() ).c_str() );
3455 }
3456 }
3457
3458 if( !( m_ctl & CTL_OMIT_PAD_NETS ) )
3459 m_out->Print( "(net %s)", m_out->Quotew( aTrack->GetNetname() ).c_str() );
3460
3463 m_out->Print( ")" );
3464}
3465
3466
3467void PCB_IO_KICAD_SEXPR::format( const ZONE* aZone ) const
3468{
3469 m_out->Print( "(zone" );
3470
3471 if( !( m_ctl & CTL_OMIT_PAD_NETS ) && aZone->IsOnCopperLayer() && !aZone->GetIsRuleArea()
3472 && aZone->GetNetCode() > 0 )
3473 {
3474 m_out->Print( "(net %s)", m_out->Quotew( aZone->GetNetname() ).c_str() );
3475 }
3476
3477 if( aZone->IsLocked() )
3478 KICAD_FORMAT::FormatBool( m_out, "locked", true );
3479
3480 // If a zone exists on multiple layers, format accordingly
3481 LSET layers = aZone->GetLayerSet();
3482
3483 if( aZone->GetBoard() )
3484 layers &= aZone->GetBoard()->GetEnabledLayers();
3485
3486 // Always enumerate every layer for a zone on a copper layer
3487 if( layers.count() > 1 )
3488 formatLayers( layers, aZone->IsOnCopperLayer(), true );
3489 else
3490 formatLayer( aZone->GetFirstLayer() );
3491
3492 if( !aZone->IsTeardropArea() )
3494
3495 if( !aZone->GetZoneName().empty() && !aZone->IsTeardropArea() )
3496 m_out->Print( "(name %s)", m_out->Quotew( aZone->GetZoneName() ).c_str() );
3497
3498 // Save the outline aux info
3499 std::string hatch;
3500
3501 switch( aZone->GetHatchStyle() )
3502 {
3503 default:
3504 case ZONE_BORDER_DISPLAY_STYLE::NO_HATCH: hatch = "none"; break;
3505 case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE: hatch = "edge"; break;
3506 case ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_FULL: hatch = "full"; break;
3507 }
3508
3509 m_out->Print( "(hatch %s %s)", hatch.c_str(),
3510 formatInternalUnits( aZone->GetBorderHatchPitch() ).c_str() );
3511
3512
3513
3514 if( aZone->GetAssignedPriority() > 0 )
3515 m_out->Print( "(priority %d)", aZone->GetAssignedPriority() );
3516
3517 // Add teardrop keywords in file: (attr (teardrop (type xxx))) where xxx is the teardrop type
3518 if( aZone->IsTeardropArea() )
3519 {
3520 m_out->Print( "(attr (teardrop (type %s)))",
3521 aZone->GetTeardropAreaType() == TEARDROP_TYPE::TD_VIAPAD ? "padvia"
3522 : "track_end" );
3523 }
3524
3525 m_out->Print( "(connect_pads" );
3526
3527 switch( aZone->GetPadConnection() )
3528 {
3529 default:
3530 case ZONE_CONNECTION::THERMAL: // Default option not saved or loaded.
3531 break;
3532
3534 m_out->Print( " thru_hole_only" );
3535 break;
3536
3538 m_out->Print( " yes" );
3539 break;
3540
3542 m_out->Print( " no" );
3543 break;
3544 }
3545
3546 m_out->Print( "(clearance %s)",
3547 formatInternalUnits( aZone->GetLocalClearance().value() ).c_str() );
3548
3549 m_out->Print( ")" );
3550
3551 m_out->Print( "(min_thickness %s)",
3552 formatInternalUnits( aZone->GetMinThickness() ).c_str() );
3553
3554 if( aZone->GetIsRuleArea() )
3555 {
3556 // Keepout settings
3557 m_out->Print( "(keepout (tracks %s) (vias %s) (pads %s) (copperpour %s) (footprints %s))",
3558 aZone->GetDoNotAllowTracks() ? "not_allowed" : "allowed",
3559 aZone->GetDoNotAllowVias() ? "not_allowed" : "allowed",
3560 aZone->GetDoNotAllowPads() ? "not_allowed" : "allowed",
3561 aZone->GetDoNotAllowZoneFills() ? "not_allowed" : "allowed",
3562 aZone->GetDoNotAllowFootprints() ? "not_allowed" : "allowed" );
3563
3564 // Multichannel settings
3565 m_out->Print( "(placement" );
3567
3568 switch( aZone->GetPlacementAreaSourceType() )
3569 {
3571 m_out->Print( "(sheetname %s)", m_out->Quotew( aZone->GetPlacementAreaSource() ).c_str() );
3572 break;
3574 m_out->Print( "(component_class %s)", m_out->Quotew( aZone->GetPlacementAreaSource() ).c_str() );
3575 break;
3577 m_out->Print( "(group %s)", m_out->Quotew( aZone->GetPlacementAreaSource() ).c_str() );
3578 break;
3579 // These are transitory and should not be saved
3581 break;
3582 }
3583
3584 m_out->Print( ")" );
3585 }
3586
3587 m_out->Print( "(fill" );
3588
3589 // Default is not filled.
3590 if( aZone->IsFilled() )
3591 m_out->Print( " yes" );
3592
3593 // Default is polygon filled.
3595 m_out->Print( "(mode hatch)" );
3596 else if( aZone->GetFillMode() == ZONE_FILL_MODE::COPPER_THIEVING )
3597 m_out->Print( "(mode thieving)" );
3598
3599 if( !aZone->IsTeardropArea() )
3600 {
3601 m_out->Print( "(thermal_gap %s) (thermal_bridge_width %s)",
3602 formatInternalUnits( aZone->GetThermalReliefGap() ).c_str(),
3603 formatInternalUnits( aZone->GetThermalReliefSpokeWidth() ).c_str() );
3604 }
3605
3607 {
3608 switch( aZone->GetCornerSmoothingType() )
3609 {
3611 m_out->Print( "(smoothing chamfer)" );
3612 break;
3613
3615 m_out->Print( "(smoothing fillet)" );
3616 break;
3617
3618 default:
3619 THROW_IO_ERRORF( _( "unknown zone corner smoothing type %d" ), aZone->GetCornerSmoothingType() );
3620 }
3621
3622 if( aZone->GetCornerRadius() != 0 )
3623 m_out->Print( "(radius %s)", formatInternalUnits( aZone->GetCornerRadius() ).c_str() );
3624 }
3625
3626 m_out->Print( "(island_removal_mode %d)",
3627 static_cast<int>( aZone->GetIslandRemovalMode() ) );
3628
3630 {
3631 m_out->Print( "(island_area_min %s)",
3632 formatInternalUnits( aZone->GetMinIslandArea() / pcbIUScale.IU_PER_MM ).c_str() );
3633 }
3634
3636 {
3637 m_out->Print( "(hatch_thickness %s) (hatch_gap %s) (hatch_orientation %s)",
3638 formatInternalUnits( aZone->GetHatchThickness() ).c_str(),
3639 formatInternalUnits( aZone->GetHatchGap() ).c_str(),
3640 FormatDouble2Str( aZone->GetHatchOrientation().AsDegrees() ).c_str() );
3641
3642 if( aZone->GetHatchSmoothingLevel() > 0 )
3643 {
3644 m_out->Print( "(hatch_smoothing_level %d) (hatch_smoothing_value %s)",
3645 aZone->GetHatchSmoothingLevel(),
3646 FormatDouble2Str( aZone->GetHatchSmoothingValue() ).c_str() );
3647 }
3648
3649 m_out->Print( "(hatch_border_algorithm %s) (hatch_min_hole_area %s)",
3650 aZone->GetHatchBorderAlgorithm() ? "hatch_thickness" : "min_thickness",
3651 FormatDouble2Str( aZone->GetHatchHoleMinArea() ).c_str() );
3652 }
3653 else if( aZone->GetFillMode() == ZONE_FILL_MODE::COPPER_THIEVING )
3654 {
3655 const THIEVING_SETTINGS& thieving = aZone->GetThievingSettings();
3656 const char* patternStr = "dots";
3657
3658 switch( thieving.pattern )
3659 {
3660 case THIEVING_PATTERN::SQUARES: patternStr = "squares"; break;
3661 case THIEVING_PATTERN::HATCH: patternStr = "hatch"; break;
3663 default: patternStr = "dots"; break;
3664 }
3665
3666 m_out->Print( "(thieving (type %s) (size %s) (gap %s) (width %s) "
3667 "(stagger %s) (orientation %s))",
3668 patternStr,
3669 formatInternalUnits( thieving.element_size ).c_str(),
3670 formatInternalUnits( thieving.gap ).c_str(),
3671 formatInternalUnits( thieving.line_width ).c_str(),
3672 thieving.stagger ? "yes" : "no",
3673 FormatDouble2Str( thieving.orientation.GetAngle().AsDegrees() ).c_str() );
3674 }
3675
3676 m_out->Print( ")" );
3677
3678 for( const auto& [layer, properties] : aZone->LayerProperties() )
3679 {
3680 format( properties, 0, layer );
3681 }
3682
3683 if( aZone->GetNumCorners() )
3684 {
3685 SHAPE_POLY_SET::POLYGON poly = aZone->Outline()->Polygon(0);
3686
3687 for( const SHAPE_LINE_CHAIN& chain : poly )
3688 {
3689 if( chain.PointCount() == 0 )
3690 continue;
3691
3692 m_out->Print( "(polygon" );
3694 m_out->Print( ")" );
3695 }
3696 }
3697
3698 // Save the PolysList (filled areas)
3699 for( PCB_LAYER_ID layer : aZone->GetLayerSet().Seq() )
3700 {
3701 const std::shared_ptr<SHAPE_POLY_SET>& fv = aZone->GetFilledPolysList( layer );
3702
3703 for( int ii = 0; ii < fv->OutlineCount(); ++ii )
3704 {
3705 m_out->Print( "(filled_polygon" );
3706 m_out->Print( "(layer %s)", m_out->Quotew( LSET::Name( layer ) ).c_str() );
3707
3708 if( aZone->IsIsland( layer, ii ) )
3709 KICAD_FORMAT::FormatBool( m_out, "island", true );
3710
3711 const SHAPE_LINE_CHAIN& chain = fv->COutline( ii );
3712
3714 m_out->Print( ")" );
3715 }
3716 }
3717
3719 m_out->Print( ")" );
3720}
3721
3722
3723void PCB_IO_KICAD_SEXPR::format( const ZONE_LAYER_PROPERTIES& aZoneLayerProperties, int aNestLevel,
3724 PCB_LAYER_ID aLayer ) const
3725{
3726 // Do not store the layer properties if no value is actually set.
3727 if( !aZoneLayerProperties.hatching_offset.has_value() )
3728 return;
3729
3730 m_out->Print( aNestLevel, "(property\n" );
3731 m_out->Print( aNestLevel, "(layer %s)\n", m_out->Quotew( LSET::Name( aLayer ) ).c_str() );
3732
3733 if( aZoneLayerProperties.hatching_offset.has_value() )
3734 {
3735 m_out->Print( aNestLevel, "(hatch_position (xy %s))",
3736 formatInternalUnits( aZoneLayerProperties.hatching_offset.value() ).c_str() );
3737 }
3738
3739 m_out->Print( aNestLevel, ")\n" );
3740}
3741
3742
3744 PCB_IO( wxS( "KiCad" ) ),
3745 m_cache( nullptr ),
3746 m_ctl( aControlFlags )
3747{
3748 init( nullptr );
3749 m_out = &m_sf;
3750}
3751
3752
3757
3758
3759void PCB_IO_KICAD_SEXPR::loadBoard( const wxString& aFileName, BOARD& aBoard, bool aIsNewLoad,
3760 const std::map<std::string, UTF8>* aProperties, PROJECT* aProject )
3761{
3762 FILE_LINE_READER reader( aFileName );
3763
3764 unsigned lineCount = 0;
3765
3766 // Collect the font substitution warnings (RAII - automatically reset on scope exit)
3768
3769 if( m_progressReporter )
3770 {
3771 m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
3772
3773 if( !m_progressReporter->KeepRefreshing() )
3775
3776 while( reader.ReadLine() )
3777 lineCount++;
3778
3779 reader.Rewind();
3780 }
3781
3782 DoLoad( reader, aBoard, aIsNewLoad, aProperties, m_progressReporter, lineCount );
3783}
3784
3785
3786void PCB_IO_KICAD_SEXPR::DoLoad( LINE_READER& aReader, BOARD& aBoard, bool aIsNewLoad,
3787 const std::map<std::string, UTF8>* aProperties, PROGRESS_REPORTER* aProgressReporter,
3788 unsigned aLineCount )
3789{
3790 init( aProperties );
3791
3792 bool preserveDestinationStackup =
3793 aProperties && aProperties->contains( PCB_IO_LOAD_PROPERTIES::APPEND_PRESERVE_DESTINATION_STACKUP );
3794
3795 PCB_IO_KICAD_SEXPR_PARSER parser( &aReader, &aBoard, m_queryUserCallback, aProgressReporter, aLineCount,
3796 preserveDestinationStackup, !aIsNewLoad );
3797
3799
3800 std::set<BOARD_ITEM*> itemsBefore;
3801 std::set<NETINFO_ITEM*> netsBefore;
3802
3803 if( !aIsNewLoad )
3804 {
3805 for( BOARD_ITEM* item : aBoard.GetItemSet() )
3806 itemsBefore.insert( item );
3807
3808 for( NETINFO_ITEM* net : aBoard.GetNetInfo() )
3809 netsBefore.insert( net );
3810 }
3811
3812 auto revertPartialAppend = [&]()
3813 {
3814 if( aIsNewLoad )
3815 return;
3816
3817 std::vector<BOARD_ITEM*> addedItems;
3818
3819 for( BOARD_ITEM* item : aBoard.GetItemSet() )
3820 {
3821 if( !itemsBefore.contains( item ) )
3822 addedItems.push_back( item );
3823 }
3824
3825 // Remove everything before deleting anything, group member back pointers
3826 // must be unlinked while their groups are still alive
3827 for( BOARD_ITEM* item : addedItems )
3828 aBoard.Remove( item );
3829
3830 for( BOARD_ITEM* item : addedItems )
3831 delete item;
3832
3833 std::vector<NETINFO_ITEM*> addedNets;
3834
3835 for( NETINFO_ITEM* net : aBoard.GetNetInfo() )
3836 {
3837 if( !netsBefore.contains( net ) )
3838 addedNets.push_back( net );
3839 }
3840
3841 for( NETINFO_ITEM* net : addedNets )
3842 {
3843 aBoard.Remove( net );
3844 delete net;
3845 }
3846 };
3847
3848 BOARD* board = nullptr;
3849
3850 try
3851 {
3852 board = dynamic_cast<BOARD*>( parser.Parse() );
3853 }
3854 catch( const FUTURE_FORMAT_ERROR& )
3855 {
3856 revertPartialAppend();
3857
3858 // Don't wrap a FUTURE_FORMAT_ERROR in another
3859 throw;
3860 }
3861 catch( const PARSE_ERROR& parse_error )
3862 {
3863 revertPartialAppend();
3864
3865 if( parser.IsTooRecent() )
3866 throw FUTURE_FORMAT_ERROR( parse_error, parser.GetRequiredVersion() );
3867 else
3868 throw;
3869 }
3870 catch( ... )
3871 {
3872 revertPartialAppend();
3873 throw;
3874 }
3875
3876 if( board != &aBoard )
3877 {
3878 // The parser loaded something that was valid, but wasn't a board.
3879 THROW_PARSE_ERROR( _( "This file does not contain a PCB." ), parser.CurSource(),
3880 parser.CurLine(), parser.CurLineNumber(), parser.CurOffset() );
3881 }
3882
3883 // Report any non-fatal parse warnings to the load info reporter
3884 for( const wxString& warning : parser.GetParseWarnings() )
3886}
3887
3888
3889void PCB_IO_KICAD_SEXPR::init( const std::map<std::string, UTF8>* aProperties )
3890{
3891 m_board = nullptr;
3892 m_reader = nullptr;
3893 m_props = aProperties;
3894}
3895
3896
3897void PCB_IO_KICAD_SEXPR::validateCache( const wxString& aLibraryPath, bool checkModified )
3898{
3899 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
3900 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
3901
3902 if( !m_cache || !m_cache->IsPath( aLibraryPath ) || ( checkModified && m_cache->IsModified() ) )
3903 {
3904 // a spectacular episode in memory management:
3905 delete m_cache;
3906 m_cache = new FP_CACHE( this, aLibraryPath );
3907 m_cache->Load();
3908 }
3909}
3910
3911
3912void PCB_IO_KICAD_SEXPR::FootprintEnumerate( wxArrayString& aFootprintNames,
3913 const wxString& aLibPath, bool aBestEfforts,
3914 const std::map<std::string, UTF8>* aProperties )
3915{
3916 wxDir dir( aLibPath );
3917 wxString errorMsg;
3918
3919 init( aProperties );
3920
3921 try
3922 {
3923 validateCache( aLibPath );
3924
3925 if( !dir.IsOpened() )
3926 THROW_IO_ERRORF( _( "Footprint library '%s' not found." ), aLibPath );
3927 }
3928 catch( const IO_ERROR& ioe )
3929 {
3930 errorMsg = ioe.What();
3931 }
3932
3933 // Some of the files may have been parsed correctly so we want to add the valid files to
3934 // the library.
3935
3936 for( const auto& footprint : m_cache->GetFootprints() )
3937 aFootprintNames.Add( footprint.first );
3938
3939 if( !errorMsg.IsEmpty() && !aBestEfforts )
3940 THROW_IO_ERROR( errorMsg );
3941}
3942
3943
3944const FOOTPRINT* PCB_IO_KICAD_SEXPR::getFootprint( const wxString& aLibraryPath,
3945 const wxString& aFootprintName,
3946 const std::map<std::string, UTF8>* aProperties,
3947 bool checkModified )
3948{
3949 init( aProperties );
3950
3951 try
3952 {
3953 validateCache( aLibraryPath, checkModified );
3954 }
3955 catch( const IO_ERROR& )
3956 {
3957 // do nothing with the error
3958 }
3959
3960 auto it = m_cache->GetFootprints().find( aFootprintName );
3961
3962 if( it == m_cache->GetFootprints().end() )
3963 return nullptr;
3964
3965 return it->second->GetFootprint().get();
3966}
3967
3968
3969const FOOTPRINT* PCB_IO_KICAD_SEXPR::GetEnumeratedFootprint( const wxString& aLibraryPath,
3970 const wxString& aFootprintName,
3971 const std::map<std::string, UTF8>* aProperties )
3972{
3973 return getFootprint( aLibraryPath, aFootprintName, aProperties, false );
3974}
3975
3976
3977bool PCB_IO_KICAD_SEXPR::FootprintExists( const wxString& aLibraryPath,
3978 const wxString& aFootprintName,
3979 const std::map<std::string, UTF8>* aProperties )
3980{
3981 // Note: checking the cache sounds like a good idea, but won't catch files which differ
3982 // only in case.
3983 //
3984 // Since this goes out to the native filesystem, we get platform differences (ie: MSW's
3985 // case-insensitive filesystem) handled "for free".
3986 // Warning: footprint names frequently contain a point. So be careful when initializing
3987 // wxFileName, and use a CTOR with extension specified
3988 wxFileName footprintFile( aLibraryPath, aFootprintName, FILEEXT::KiCadFootprintFileExtension );
3989
3990 return footprintFile.Exists();
3991}
3992
3993
3994std::unique_ptr<FOOTPRINT> PCB_IO_KICAD_SEXPR::ImportFootprint( const wxString& aFootprintPath,
3995 wxString& aFootprintNameOut,
3996 const std::map<std::string, UTF8>* aProperties )
3997{
3998 wxString fcontents;
3999 wxFFile f( aFootprintPath );
4000
4001 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
4002 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
4003
4004 if( !f.IsOpened() )
4005 return nullptr;
4006
4007 f.ReadAll( &fcontents );
4008
4009 aFootprintNameOut = wxFileName( aFootprintPath ).GetName();
4010
4011 return std::unique_ptr<FOOTPRINT>( dynamic_cast<FOOTPRINT*>( Parse( fcontents ) ) );
4012}
4013
4014
4015std::unique_ptr<FOOTPRINT> PCB_IO_KICAD_SEXPR::FootprintLoad( const wxString& aLibraryPath,
4016 const wxString& aFootprintName, bool aKeepUUID,
4017 const std::map<std::string, UTF8>* aProperties )
4018{
4019 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
4020 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
4021
4022 const FOOTPRINT* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
4023
4024 if( footprint )
4025 {
4026 std::unique_ptr<FOOTPRINT> copy;
4027
4028 if( aKeepUUID )
4029 copy.reset( static_cast<FOOTPRINT*>( footprint->Clone() ) );
4030 else
4031 copy.reset( static_cast<FOOTPRINT*>( footprint->Duplicate( IGNORE_PARENT_GROUP ) ) );
4032
4033 copy->SetParent( nullptr );
4034 return copy;
4035 }
4036
4037 return nullptr;
4038}
4039
4040
4041void PCB_IO_KICAD_SEXPR::FootprintSave( const wxString& aLibraryPath, const FOOTPRINT* aFootprint,
4042 const std::map<std::string, UTF8>* aProperties )
4043{
4044 init( aProperties );
4045
4046 // In this public PLUGIN API function, we can safely assume it was
4047 // called for saving into a library path.
4049
4050 // Support saving to a single-file path like "/tmp/foo.kicad_mod" by treating the directory
4051 // as the library path and the file base-name as the footprint name.
4052 wxString libPath = aLibraryPath;
4053 wxString singleFileBaseName; // without extension
4054 bool saveSingleFile = false;
4055
4056 {
4057 wxFileName asFile( aLibraryPath );
4058
4059 if( asFile.GetExt() == FILEEXT::KiCadFootprintFileExtension )
4060 {
4061 saveSingleFile = true;
4062 libPath = asFile.GetPath();
4063 singleFileBaseName = asFile.GetName();
4064 }
4065 }
4066
4067 validateCache( libPath, !aProperties || !aProperties->contains( "skip_cache_validation" ) );
4068
4069 if( !m_cache->IsWritable() )
4070 {
4071 if( !m_cache->Exists() )
4072 {
4073 const wxString msg = wxString::Format( _( "Library '%s' does not exist.\n"
4074 "Would you like to create it?"),
4075 libPath );
4076
4077 if( !Pgm().IsGUI() || wxMessageBox( msg, _( "Library Not Found" ), wxYES_NO | wxICON_QUESTION ) != wxYES )
4078 return;
4079
4080 // Save throws its own IO_ERROR on failure, so no need to recreate here
4081 m_cache->Save( nullptr );
4082 }
4083 else
4084 {
4085 THROW_IO_ERRORF( _( "Library '%s' is read only." ), libPath );
4086 }
4087 }
4088
4089 // The map key used by the cache and the on-disk filename base.
4090 wxString footprintName = saveSingleFile ? singleFileBaseName
4091 : aFootprint->GetFPID().GetUniStringLibItemName();
4092
4093 wxString fpName = saveSingleFile ? singleFileBaseName
4094 : aFootprint->GetFPID().GetUniStringLibItemName();
4095 ReplaceIllegalFileNameChars( fpName, '_' );
4096
4097 // Quietly overwrite footprint and delete footprint file from path for any by same name.
4098 wxFileName fn( libPath, fpName, FILEEXT::KiCadFootprintFileExtension );
4099
4100 // Write through symlinks, don't replace them
4102
4103 if( !fn.IsOk() )
4104 THROW_IO_ERRORF( _( "Footprint file name '%s' is not valid." ), fn.GetFullPath() );
4105
4106 if( fn.FileExists() && !fn.IsFileWritable() )
4107 THROW_IO_ERRORF( _( "Insufficient permissions to delete '%s'." ), fn.GetFullPath() );
4108
4109 wxString fullPath = fn.GetFullPath();
4110 wxString fullName = fn.GetFullName();
4111 auto it = m_cache->GetFootprints().find( footprintName );
4112
4113 if( it != m_cache->GetFootprints().end() )
4114 {
4115 // Save() below writes atomically via sibling temp + rename, so no pre-delete.
4116 wxLogTrace( traceKicadPcbPlugin, wxT( "Replacing footprint file '%s'." ), fullPath );
4117 m_cache->GetFootprints().erase( footprintName );
4118 }
4119
4120 // I need my own copy for the cache
4121 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aFootprint->Clone() );
4122
4123 // It's orientation should be zero and it should be on the front layer.
4124 footprint->SetOrientation( ANGLE_0 );
4125
4126 if( footprint->GetLayer() != F_Cu )
4127 {
4128 PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() );
4129
4130 if( cfg )
4131 footprint->Flip( footprint->GetPosition(), cfg->m_FlipDirection );
4132 else
4133 footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
4134 }
4135
4136 // Detach it from the board and its group
4137 footprint->SetParent( nullptr );
4138 footprint->SetParentGroup( nullptr );
4139
4140 // Now that the clone is detached from its parent board, any m_netinfo pointers its
4141 // descendants still carry reference NETINFO_ITEMs owned by that board and may dangle.
4142 // Force them all to the board-independent ORPHANED singleton before serialization.
4143 footprint->ClearAllNets();
4144
4145 wxLogTrace( traceKicadPcbPlugin, wxT( "Creating s-expr footprint file '%s'." ), fullPath );
4146 m_cache->GetFootprints().insert( footprintName,
4147 new FP_CACHE_ENTRY( footprint,
4148 WX_FILENAME( fn.GetPath(), fullName ) ) );
4149 m_cache->Save( footprint );
4150}
4151
4152
4153void PCB_IO_KICAD_SEXPR::FootprintDelete( const wxString& aLibraryPath,
4154 const wxString& aFootprintName,
4155 const std::map<std::string, UTF8>* aProperties )
4156{
4157 init( aProperties );
4158
4159 validateCache( aLibraryPath );
4160
4161 if( !m_cache->IsWritable() )
4162 THROW_IO_ERRORF( _( "Library '%s' is read only." ), aLibraryPath.GetData() );
4163
4164 m_cache->Remove( aFootprintName );
4165}
4166
4167
4168void PCB_IO_KICAD_SEXPR::ClearCachedFootprints( const wxString& aLibraryPath )
4169{
4170 if( m_cache && m_cache->IsPath( aLibraryPath ) )
4171 {
4172 delete m_cache;
4173 m_cache = nullptr;
4174 }
4175}
4176
4177
4178long long PCB_IO_KICAD_SEXPR::GetLibraryTimestamp( const wxString& aLibraryPath ) const
4179{
4180 return FP_CACHE::GetTimestamp( aLibraryPath );
4181}
4182
4183
4184void PCB_IO_KICAD_SEXPR::CreateLibrary( const wxString& aLibraryPath,
4185 const std::map<std::string, UTF8>* aProperties )
4186{
4187 if( wxDir::Exists( aLibraryPath ) )
4188 THROW_IO_ERRORF( _( "Cannot overwrite library path '%s'." ), aLibraryPath.GetData() );
4189
4190 init( aProperties );
4191
4192 delete m_cache;
4193 m_cache = new FP_CACHE( this, aLibraryPath );
4194 m_cache->Save();
4195}
4196
4197
4198bool PCB_IO_KICAD_SEXPR::DeleteLibrary( const wxString& aLibraryPath,
4199 const std::map<std::string, UTF8>* aProperties )
4200{
4201 wxFileName fn;
4202 fn.SetPath( aLibraryPath );
4203
4204 // Return if there is no library path to delete.
4205 if( !fn.DirExists() )
4206 return false;
4207
4208 if( !fn.IsDirWritable() )
4209 THROW_IO_ERRORF( _( "Insufficient permissions to delete folder '%s'." ), aLibraryPath.GetData() );
4210
4211 wxDir dir( aLibraryPath );
4212
4213 if( dir.HasSubDirs() )
4214 THROW_IO_ERRORF( _( "Library folder '%s' has unexpected sub-folders." ), aLibraryPath.GetData() );
4215
4216 // All the footprint files must be deleted before the directory can be deleted.
4217 if( dir.HasFiles() )
4218 {
4219 unsigned i;
4220 wxFileName tmp;
4221 wxArrayString files;
4222
4223 CollectFilesLoopSafe( aLibraryPath, files );
4224
4225 for( i = 0; i < files.GetCount(); i++ )
4226 {
4227 tmp = files[i];
4228
4229 if( tmp.GetExt() != FILEEXT::KiCadFootprintFileExtension )
4230 {
4231 THROW_IO_ERRORF( _( "Unexpected file '%s' found in library path '%s'." ),
4232 files[i].GetData(),
4233 aLibraryPath.GetData() );
4234 }
4235 }
4236
4237 for( i = 0; i < files.GetCount(); i++ )
4238 wxRemoveFile( files[i] );
4239 }
4240
4241 wxLogTrace( traceKicadPcbPlugin, wxT( "Removing footprint library '%s'." ),
4242 aLibraryPath.GetData() );
4243
4244 // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
4245 // we don't want that. we want bare metal portability with no UI here.
4246 if( !wxRmdir( aLibraryPath ) )
4247 THROW_IO_ERRORF( _( "Footprint library '%s' cannot be deleted." ), aLibraryPath.GetData() );
4248
4249 // For some reason removing a directory in Windows is not immediately updated. This delay
4250 // prevents an error when attempting to immediately recreate the same directory when over
4251 // writing an existing library.
4252#ifdef __WINDOWS__
4253 wxMilliSleep( 250L );
4254#endif
4255
4256 if( m_cache && !m_cache->IsPath( aLibraryPath ) )
4257 {
4258 delete m_cache;
4259 m_cache = nullptr;
4260 }
4261
4262 return true;
4263}
4264
4265
4266bool PCB_IO_KICAD_SEXPR::IsLibraryWritable( const wxString& aLibraryPath )
4267{
4268 init( nullptr );
4269
4270 validateCache( aLibraryPath );
4271
4272 return m_cache->IsWritable();
4273}
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
@ LT_FRONT
Definition board.h:249
@ LT_BACK
Definition board.h:250
@ ZLO_FORCE_FLASHED
Definition board_item.h:74
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
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:64
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
DRILL_SYMBOL_PROFILE & GetDrillSymbolProfile()
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 bool IsKnockout() const
Definition board_item.h:422
bool IsLocked() const override
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
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:410
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1215
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition board.cpp:3841
const std::vector< wxString > & GetVariantNames() const
Definition board.h:532
const GENERATORS & Generators() const
Definition board.h:484
const PCB_POINTS & Points() const
Definition board.h:498
const PAGE_INFO & GetPageSettings() const
Definition board.h:1018
const ZONES & Zones() const
Definition board.h:468
const GROUPS & Groups() const
The groups must maintain the following invariants.
Definition board.h:517
LAYER_T GetLayerType(PCB_LAYER_ID aLayer) const
Return the type of the copper layer given by aLayer.
Definition board.cpp:993
TITLE_BLOCK & GetTitleBlock()
Definition board.h:1024
int GetCopperLayerCount() const
Definition board.cpp:1129
const std::map< wxString, wxString > & GetProperties() const
Definition board.h:525
const FOOTPRINTS & Footprints() const
Definition board.h:464
const BOARD_ITEM_SET GetItemSet()
Collect every owned item (tracks, zones, generators, footprints, drawings, markers,...
Definition board.cpp:4393
const TRACKS & Tracks() const
Definition board.h:462
const CONSTRAINTS & Constraints() const
Geometric constraints (#2329) owned by this board.
Definition board.h:521
wxString GetVariantDescription(const wxString &aVariantName) const
Definition board.cpp:3321
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition board.h:1021
bool LegacyTeardrops() const
Definition board.h:1651
wxString GroupsSanityCheck(bool repair=false)
Consistency check of internal m_groups structure.
Definition board.cpp:4097
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1292
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1176
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition board.cpp:1686
const DRAWINGS & Drawings() const
Definition board.h:466
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.
The column set and formatting a new chart starts from.
static DRILL_CHART_TEMPLATE MakeDefault()
Grouping rules and symbol assignments, shared by reference so a chart and its map can never disagree ...
const wxString & GetName() const
const std::map< std::string, DRILL_SYMBOL_ASSIGNMENT > & Assignments() const
const std::set< DRILL_GROUP_KEY > & GroupKeys() const
DRILL_MARK_POLICY GetMarkPolicy() const
double AsDegrees() const
Definition eda_angle.h:115
bool IsZero() const
Definition eda_angle.h:135
EDA_ANGLE Normalize720()
Definition eda_angle.h:286
const LIB_ID & GetDesignBlockLibId() const
Definition eda_group.h:102
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:78
wxString GetName() const
Definition eda_group.h:75
bool HasDesignBlockLink() const
Definition eda_group.h:99
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
virtual void SetParentGroup(EDA_GROUP *aGroup)
Definition eda_item.h:115
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:153
EDA_ANGLE GetAngle() const
Definition eda_angle.h:533
const VECTOR2I & GetBezierC2() const
Definition eda_shape.h:374
FILL_T GetFillMode() const
Definition eda_shape.h:148
SHAPE_POLY_SET & GetPolyShape()
SHAPE_T GetShape() const
Definition eda_shape.h:175
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:331
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:281
const LINE_ENDING & GetStartEnding() const
Definition eda_shape.h:177
wxString SHAPE_T_asString() const
const VECTOR2I & GetBezierC1() const
Definition eda_shape.h:371
const LINE_ENDING & GetEndEnding() const
Definition eda_shape.h:183
int GetCornerRadius() const
bool IsPolyShapeValid() const
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
virtual VECTOR2I GetTextSize() const
Definition eda_text.h:301
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:512
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:118
bool IsKeepUpright() const
Definition eda_text.h:245
virtual bool IsVisible() const
Definition eda_text.h:226
KIFONT::FONT * GetFont() const
Definition eda_text.h:286
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:683
virtual EDA_ANGLE GetDrawRotation() const
Definition eda_text.h:425
virtual wxString GetShownText(RESOLUTION_CONTEXT aContext, int aDepth=0) const
Return the string actually shown after processing of the base text.
Definition eda_text.h:128
bool GetAutoThickness() const
Definition eda_text.h:170
virtual void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:250
virtual int GetTextThickness() const
Definition eda_text.h:159
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, std::shared_ptr< EMBEDDED_FILE > > & EmbeddedFileMap() const
Provide an iterable view of the file collection.
bool GetAreFontsEmbedded() const
A LINE_READER that reads from an open file.
Definition richio.h:157
void Rewind()
Rewind the file and resets the line number back to zero.
Definition richio.h:206
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition richio.cpp:202
RAII class to set and restore the fontconfig reporter.
Definition reporter.h:385
bool GetDuplicatePadNumbersAreJumpers() const
Definition footprint.h:1221
const CASE_INSENSITIVE_MAP< FOOTPRINT_VARIANT > & GetVariants() const
Get all variants.
Definition footprint.h:1105
bool AllowSolderMaskBridges() const
Definition footprint.h:560
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:475
wxString GetLibDescription() const
Definition footprint.h:491
ZONE_CONNECTION GetLocalZoneConnection() const
Definition footprint.h:533
bool IsDNP() const
Definition footprint.h:1041
EDA_ANGLE GetOrientation() const
Definition footprint.h:439
ZONES & Zones()
Definition footprint.h:411
PCB_POINTS & Points()
Definition footprint.h:420
bool IsExcludedFromBOM() const
Definition footprint.h:1023
void SetOrientation(const EDA_ANGLE &aNewAngle)
const TRANSFORM_TRS & GetTransform() const
Definition footprint.h:452
wxString GetSheetname() const
Definition footprint.h:511
const std::vector< FP_UNIT_INFO > & GetUnitInfo() const
Definition footprint.h:1003
const EXTRUDED_3D_BODY * GetExtrudedBody() const
Definition footprint.h:429
std::optional< int > GetLocalSolderPasteMargin() const
Definition footprint.h:526
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
PCB_FIELD & Value()
read/write accessors:
Definition footprint.h:925
std::optional< int > GetLocalClearance() const
Definition footprint.h:520
CONSTRAINTS & Constraints()
Definition footprint.h:417
PCB_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this footprint.
std::deque< PAD * > & Pads()
Definition footprint.h:405
int GetAttributes() const
Definition footprint.h:551
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:450
LSET GetPrivateLayers() const
Definition footprint.h:345
bool AllowMissingCourtyard() const
Definition footprint.h:557
wxString GetSheetfile() const
Definition footprint.h:514
const std::vector< wxString > & GetNetTiePadGroups() const
Definition footprint.h:609
const LIB_ID & GetFPID() const
Definition footprint.h:474
bool IsExcludedFromPosFiles() const
Definition footprint.h:1014
bool IsExcludedFromSim() const
Definition footprint.h:1032
const LSET & GetStackupLayers() const
Definition footprint.h:549
PCB_FIELD & Reference()
Definition footprint.h:926
JUMPER_GROUP_SET & JumperPadGroups()
Each jumper pad group is a set of pad numbers that should be treated as internally connected.
Definition footprint.h:1228
void ClearAllNets()
Clear (i.e.
bool IsNetTie() const
Definition footprint.h:567
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition footprint.h:529
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
GROUPS & Groups()
Definition footprint.h:414
wxString GetFilters() const
Definition footprint.h:517
const wxArrayString * GetInitialComments() const
Return the initial comments block or NULL if none, without transfer of ownership.
Definition footprint.h:1346
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:425
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:497
std::optional< int > GetLocalSolderMaskMargin() const
Definition footprint.h:523
wxString GetKeywords() const
Definition footprint.h:494
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition footprint.h:1375
FOOTPRINT_STACKUP GetStackupMode() const
Definition footprint.h:542
bool IsPlaced() const
Definition footprint.h:698
VECTOR2I GetPosition() const override
Definition footprint.h:436
DRAWINGS & GraphicalItems()
Definition footprint.h:408
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)
The columns one generator (the drill chart, a stackup table, ...) can produce, and the units and prec...
const char * Token(int aId) const
The file token for a known id, or a stable placeholder for one the schema does not recognize,...
const GENERATED_TABLE_COLUMN_DEF * Find(int aId) const
GENERATED_TABLE_UNITS DefaultUnits() const
PROGRESS_REPORTER * m_progressReporter
Progress reporter to track the progress of the operation, may be nullptr.
Definition io_base.h:241
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()
Represents an (ordered) list of JUMPER_GROUP objects.
const std::vector< JUMPER_GROUP > & GetAll() const
bool IsEmpty() const
Represents a group of jumper pins or pads, keyed by name.
APP_SETTINGS_BASE * KifaceSettings() const
Definition kiface_base.h:92
virtual bool IsOutline() const
Definition font.h:102
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:400
virtual void SetLineWidth(float aLineWidth)
Set the line width.
virtual wxString GetClass() const =0
Return the class name.
wxString AsString() const
Definition kiid.cpp:423
LAYER_MAPPING_HANDLER m_layer_mapping_handler
Callback to get layer mapping.
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
UTF8 Format() const
Definition lib_id.cpp:132
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition lib_id.h:108
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
void Format(OUTPUTFORMATTER *aOut, const EDA_IU_SCALE &aIuScale, const char *aToken) const
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition richio.h:65
static LOAD_INFO_REPORTER & GetInstance()
Definition reporter.cpp:351
REPORTER & Report(const wxString &aMsg, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition reporter.cpp:336
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:604
LSEQ CuStack() const
Return a sequence of copper layers in starting from the front/top and extending to the back/bottom.
Definition lset.cpp:259
LSEQ TechAndUserUIOrder() const
Return the technical and user layers in the order shown in layer widget.
Definition lset.cpp:272
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:309
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:595
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:184
Handle the data for a net.
Definition netinfo.h:50
const wxString & GetNetname() const
Definition netinfo.h:110
An interface used to output 8 bit text in a convenient way.
Definition richio.h:294
std::string Quotew(const wxString &aWrapee) const
Definition richio.cpp:505
int PRINTF_FUNC_N Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition richio.cpp:432
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
Definition padstack.h:156
std::optional< int > & Clearance(PCB_LAYER_ID aLayer=F_Cu)
Definition padstack.cpp:999
MASK_LAYER_PROPS & FrontOuterLayers()
Definition padstack.h:381
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 DefaultThermalSpokeAngleForShape(PCB_LAYER_ID aLayer=F_Cu) const
VECTOR2I & Offset(PCB_LAYER_ID aLayer)
Definition padstack.cpp:898
EDA_ANGLE ThermalSpokeAngle(PCB_LAYER_ID aLayer=F_Cu) const
POST_MACHINING_PROPS & FrontPostMachining()
Definition padstack.h:367
std::optional< int > & ThermalGap(PCB_LAYER_ID aLayer=F_Cu)
DRILL_PROPS & TertiaryDrill()
Definition padstack.h:364
DRILL_PROPS & Drill()
Definition padstack.h:358
const VECTOR2I & Size(PCB_LAYER_ID aLayer) const
Definition padstack.cpp:868
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:170
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
Definition padstack.h:171
DRILL_PROPS & SecondaryDrill()
Definition padstack.h:361
POST_MACHINING_PROPS & BackPostMachining()
Definition padstack.h:370
MODE Mode() const
Definition padstack.h:346
MASK_LAYER_PROPS & BackOuterLayers()
Definition padstack.h:384
static constexpr PCB_LAYER_ID INNER_LAYERS
! The layer identifier to use for "inner layers" on top/inner/bottom padstacks
Definition padstack.h:182
std::optional< ZONE_CONNECTION > & ZoneConnection(PCB_LAYER_ID aLayer=F_Cu)
Definition pad.h:61
PAD_PROP GetProperty() const
Definition pad.h:563
bool GetRemoveUnconnected() const
Definition pad.h:874
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition pad.h:557
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:375
const ZONE_LAYER_OVERRIDE & GetZoneLayerOverride(PCB_LAYER_ID aLayer) const
Definition pad.cpp:517
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition pad.h:600
const wxString & GetPinType() const
Definition pad.h:160
PAD_ATTRIB GetAttribute() const
Definition pad.h:560
const wxString & GetPinFunction() const
Definition pad.h:154
const wxString & GetNumber() const
Definition pad.h:143
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition pad.h:307
EDA_ANGLE GetThermalSpokeAngle() const
Definition pad.h:757
VECTOR2I GetOffset(PCB_LAYER_ID aLayer) const
Definition pad.cpp:838
VECTOR2I GetDrillSize() const
Definition pad.h:320
double GetRoundRectRadiusRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:811
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:205
bool GetKeepTopBottom() const
Definition pad.h:889
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:331
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.cpp:1757
PAD_DRILL_SHAPE GetDrillShape() const
Definition pad.h:434
int GetChamferPositions(PCB_LAYER_ID aLayer) const
Definition pad.h:852
std::optional< int > GetLocalSolderPasteMargin() const
Definition pad.h:593
PAD_SIM_ELECTRICAL_TYPE GetSimElectricalType() const
Definition pad.h:573
std::optional< int > GetLocalSolderMaskMargin() const
Definition pad.h:586
double GetChamferRectRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:834
std::optional< int > GetLocalThermalSpokeWidthOverride() const
Definition pad.h:741
ZONE_CONNECTION GetLocalZoneConnection() const
Definition pad.h:611
CUSTOM_SHAPE_ZONE_MODE GetCustomShapeInZoneOpt() const
Definition pad.h:227
int GetLocalThermalGapOverride(wxString *aSource) const
Definition pad.cpp:2195
PAD_SHAPE GetAnchorPadShape(PCB_LAYER_ID aLayer) const
Definition pad.h:219
int GetPadToDieLength() const
Definition pad.h:578
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:295
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).
A geometric constraint between board items (issue #2329).
const std::vector< CONSTRAINT_MEMBER > & GetMembers() const
std::optional< double > GetValue() const
bool IsDriving() const
A driving constraint forces its value; a reference (non-driving) one only measures it.
PCB_CONSTRAINT_TYPE GetConstraintType() const
bool HasValue() const
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
DIM_ARROW_DIRECTION GetArrowDirection() const
virtual VECTOR2I GetEnd() const
bool GetSuppressZeroes() const
int GetExtensionOffset() const
virtual VECTOR2I GetStart() const
The dimension's origin is the first feature point for the dimension.
int GetArrowLength() const
bool GetOverrideTextEnabled() 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
A drill chart placed on the board, kept in step with the holes.
const std::map< int, int > & RowShapes() const
Curated shape index for each generated row, by table row.
bool GetShowTotals() const
DRILL_CHART_FILTER & Filter()
int GetSymbolColumn() const
Table column the symbol is drawn in, or -1 when the chart has no symbol column.
Turns on drill symbols at the holes, for one layer.
bool GetAllSpans() const
int GetSymbolSize() const
bool GetGuideCross() const
bool GetOutlineSlots() const
const DRILL_SPAN & GetSpan() const
const VECTOR2I & GetOffset() const
A table whose cell text is derived from the board and rebuilt whenever it goes stale.
const std::map< int, std::string > & RowKeys() const
What each generated row reports on, by table row.
std::vector< GENERATED_TABLE_COLUMN > & Columns()
virtual const GENERATED_TABLE_SCHEMA & Schema() const =0
GENERATED_TABLE_UNITS GetUnits() const
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
virtual std::vector< std::pair< wxString, const BOARD_ITEM * > > GetTemplateItems() const
Named template items used by the generator.
virtual const STRING_ANY_MAP GetProperties() const
virtual wxString GetGeneratorType() const
EDA_ANGLE GetPhiExtent() const
VECTOR2I GetSpacing() const
VECTOR2I GetExtent() const
unsigned GetAssignedPriority() const
unsigned GetTickInterval() const
VECTOR2I GetPosition() const override
int GetRadiusSpacing() const
EDA_ANGLE GetOrientation() const
EDA_ANGLE GetPhiSpacing() const
PCB_GRID_AFFECTS & Affects()
int GetRadiusExtent() const
PCB_GRID_TYPE GetGridItemType() const
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
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.
void SetLayerMappingHandler(LAYER_MAPPING_HANDLER aHandler)
Handler to remap an appended board's layers onto the destination board, used on mismatch.
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 PCB_IO derivation for saving and loading Pcbnew s-expression formatted files.
bool CanReadBoard(const wxString &aFileName) const override
void loadBoard(const wxString &aFileName, BOARD &aBoard, bool aIsNewLoad, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr) override
Parse aFileName into aBoard.
void formatProperties(const BOARD *aBoard) const
formats the Nets and Netclasses
void FootprintDelete(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Delete aFootprintName from the library at aLibraryPath.
void formatTableData(const PCB_TABLE *aTable) const
Geometry and cells with no identity, so an enclosing form owns uuid/layer/locked.
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 i...
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 formatDrillSymbolProfile(const BOARD_DESIGN_SETTINGS &aSettings) const
void formatPolyPts(const SHAPE_LINE_CHAIN &outline, const FOOTPRINT *aParentFP=nullptr) const
std::unique_ptr< 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.
FP_CACHE * m_cache
Footprint library cache.
void DoLoad(LINE_READER &aReader, BOARD &aBoard, bool aIsNewLoad, const std::map< std::string, UTF8 > *aProperties, PROGRESS_REPORTER *aProgressReporter, unsigned aLineCount)
void formatBoardLayers(const BOARD *aBoard) const
formats the board layer information
std::unique_ptr< 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...
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 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 format(const BOARD *aBoard) const
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 formatGeneratedTableExtras(const PCB_GENERATED_TABLE *aTable) const
What only one kind of generated table carries, written after the shared settings.
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:393
virtual bool CanReadBoard(const wxString &aFileName) const
Definition pcb_io.cpp:40
PCB_IO(const wxString &aName)
Definition pcb_io.h:372
const std::map< std::string, UTF8 > * m_props
Properties passed via Save() or Load(), no ownership, may be NULL.
Definition pcb_io.h:396
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:39
int GetSize() const
Definition pcb_point.h:69
VECTOR2I GetLibraryPosition() const
Definition pcb_point.h:61
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()
EDA_ANGLE GetLibraryEllipseEndAngle() const
Definition pcb_shape.h:239
std::optional< int > GetLocalSolderMaskMargin() const
Definition pcb_shape.h:347
bool HasSolderMask() const
Definition pcb_shape.h:344
int GetLibraryEllipseMinorRadius() const
Definition pcb_shape.h:236
EDA_ANGLE GetLibraryEllipseStartAngle() const
Definition pcb_shape.h:238
int GetLibraryEllipseMajorRadius() const
Definition pcb_shape.h:235
EDA_ANGLE GetLibraryEllipseRotation() const
Definition pcb_shape.h:237
SHAPE_T GetLibraryShape() const
Definition pcb_shape.h:231
VECTOR2I GetLibraryEllipseCenter() const
Definition pcb_shape.h:234
VECTOR2I GetLibraryEnd() const
Definition pcb_shape.h:230
const SHAPE_POLY_SET & GetLibPoly() const
Definition pcb_shape.h:287
VECTOR2I GetLibraryStart() const
Definition pcb_shape.h:229
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
STROKE_PARAMS GetStroke() const override
VECTOR2I GetLibraryArcMid() const
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition pcb_shape.h:68
int GetLibraryCornerRadius() const
Definition pcb_shape.h:232
bool StrokeRows() const
Definition pcb_table.h:110
int GetRowCount() const
Definition pcb_table.h:128
bool StrokeHeaderSeparator() const
Definition pcb_table.h:68
bool StrokeColumns() const
Definition pcb_table.h:107
bool StrokeExternal() const
Definition pcb_table.h:65
std::vector< PCB_TABLECELL * > GetCells() const
Definition pcb_table.h:165
int GetColCount() const
Definition pcb_table.h:126
const STROKE_PARAMS & GetSeparatorsStroke() const
Definition pcb_table.h:89
const STROKE_PARAMS & GetBorderStroke() const
Definition pcb_table.h:71
int GetColWidth(int aCol) const
Definition pcb_table.h:137
virtual wxString GetClass() const override
Return the class name.
Definition pcb_table.h:54
int GetRowHeight(int aRow) const
Definition pcb_table.h:147
int GetShape() const
Definition pcb_target.h:54
int GetWidth() const
Definition pcb_target.h:60
int GetSize() const
Definition pcb_target.h:57
VECTOR2I GetPosition() const override
Definition pcb_target.h:51
bool IsBorderEnabled() const
Tests whether the border is disabled, as configured by the stroke.
int GetMarginBottom() const
EDA_ANGLE GetTextAngle() const override
int GetMarginLeft() const
int GetMarginRight() const
int GetMarginTop() const
EDA_ANGLE GetTextAngle() const override
Definition pcb_text.cpp:560
VECTOR2I GetTextPos() const override
Definition pcb_text.cpp:461
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:122
std::optional< int > GetLocalSolderMaskMargin() const
Definition pcb_track.h:125
const VECTOR2I & GetStart() const
Definition pcb_track.h:98
const VECTOR2I & GetEnd() const
Definition pcb_track.h:95
virtual int GetWidth() const
Definition pcb_track.h:92
bool Finish() override
Runs prettification over the buffered bytes, writes them to the sibling temp file,...
Definition richio.cpp:710
A progress reporter interface for use in multi-threaded environments.
Container for project specific data.
Definition project.h:63
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:116
const VECTOR2I & GetP1() const
Definition shape_arc.h:115
const VECTOR2I & GetP0() const
Definition shape_arc.h:114
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.
int OutlineCount() const
Return the number of outlines in the set.
Is a LINE_READER that reads from a multiline 8 bit wide std::string.
Definition richio.h:225
Simple container to manage line stroke parameters.
int GetWidth() const
void SetWidth(int aWidth)
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.
VECTOR2I InverseApply(const VECTOR2I &aPoint) const
double GetScaleX() const
double GetScaleY() const
const char * c_str() const
Definition utf8.h:104
A wrapper around a wxFileName which is much more performant with a subset of the API.
Definition wx_filename.h:46
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:70
int GetHatchBorderAlgorithm() const
Definition zone.h:343
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:832
std::optional< int > GetLocalClearance() const override
Definition zone.cpp:1062
const THIEVING_SETTINGS & GetThievingSettings() const
Definition zone.h:351
bool GetDoNotAllowVias() const
Definition zone.h:843
ZONE_LAYER_PROPERTIES & LayerProperties(PCB_LAYER_ID aLayer)
Definition zone.h:146
wxString GetPlacementAreaSource() const
Definition zone.h:837
std::shared_ptr< SHAPE_POLY_SET > GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition zone.h:699
bool GetDoNotAllowPads() const
Definition zone.h:845
PLACEMENT_SOURCE_T GetPlacementAreaSourceType() const
Definition zone.h:839
bool GetDoNotAllowTracks() const
Definition zone.h:844
bool IsFilled() const
Definition zone.h:306
ISLAND_REMOVAL_MODE GetIslandRemovalMode() const
Definition zone.h:854
SHAPE_POLY_SET * Outline()
Definition zone.h:421
bool IsIsland(PCB_LAYER_ID aLayer, int aPolyIdx) const
Check if a given filled polygon is an insulated island.
Definition zone.cpp:1708
long long int GetMinIslandArea() const
Definition zone.h:857
const wxString & GetZoneName() const
Definition zone.h:160
int GetMinThickness() const
Definition zone.h:315
ZONE_SETTINGS::CORNER_SMOOTHING GetCornerSmoothingType() const
Definition zone.h:771
ZONE_CONNECTION GetPadConnection() const
Definition zone.h:312
int GetHatchThickness() const
Definition zone.h:325
double GetHatchHoleMinArea() const
Definition zone.h:340
bool GetPlacementAreaEnabled() const
Definition zone.h:834
bool IsTeardropArea() const
Definition zone.h:807
int GetThermalReliefSpokeWidth() const
Definition zone.h:259
int GetBorderHatchPitch() const
HatchBorder related methods.
Definition zone.h:867
ZONE_BORDER_DISPLAY_STYLE GetHatchStyle() const
Definition zone.h:687
EDA_ANGLE GetHatchOrientation() const
Definition zone.h:331
bool GetDoNotAllowFootprints() const
Definition zone.h:846
ZONE_FILL_MODE GetFillMode() const
Definition zone.h:238
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:133
int GetHatchGap() const
Definition zone.h:328
TEARDROP_TYPE GetTeardropAreaType() const
Definition zone.h:818
double GetHatchSmoothingValue() const
Definition zone.h:337
bool GetDoNotAllowZoneFills() const
Definition zone.h:842
int GetHatchSmoothingLevel() const
Definition zone.h:334
unsigned int GetCornerRadius() const
Definition zone.h:775
bool IsOnCopperLayer() const override
Definition zone.cpp:615
PCB_LAYER_ID GetFirstLayer() const
Definition zone.cpp:595
int GetThermalReliefGap() const
Definition zone.h:248
unsigned GetAssignedPriority() const
Definition zone.h:122
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition zone.h:617
@ FOR_CANVAS
Definition common.h:90
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
const char * DrillGroupKeyToken(DRILL_GROUP_KEY aKey)
Stable file tokens, deliberately independent of enum ordering so a later insertion into the enum cann...
const char * DrillMarkModeToken(DRILL_MARK_MODE aMode)
const char * DrillMarkPolicyToken(DRILL_MARK_POLICY aPolicy)
DRILL_GROUP_KEY
Which properties split holes into separate chart rows and symbols.
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:450
static constexpr EDA_ANGLE ANGLE_45
Definition eda_angle.h:449
@ REVERSE_HATCH
Definition eda_fill.h:35
@ HATCH
Definition eda_fill.h:34
@ FILLED_SHAPE
Fill with object color.
Definition eda_fill.h:31
@ CROSS_HATCH
Definition eda_fill.h:36
#define IGNORE_PARENT_GROUP
Definition eda_item.h:55
SHAPE_T
Definition eda_shape.h:54
@ ELLIPSE
Definition eda_shape.h:62
@ SEGMENT
Definition eda_shape.h:56
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
@ ELLIPSE_ARC
Definition eda_shape.h:63
EDA_DATA_TYPE
The type of unit.
Definition eda_units.h:34
@ FP_SMD
Definition footprint.h:87
@ FP_DNP
Definition footprint.h:92
@ FP_EXCLUDE_FROM_POS_FILES
Definition footprint.h:88
@ FP_BOARD_ONLY
Definition footprint.h:90
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:89
@ FP_EXCLUDE_FROM_SIM
Definition footprint.h:93
@ FP_THROUGH_HOLE
Definition footprint.h:86
@ EXPAND_INNER_LAYERS
The 'normal' stackup handling, where there is a single inner layer (In1) and rule areas using it expa...
Definition footprint.h:161
const char * GeneratedTableUnitsToken(GENERATED_TABLE_UNITS aUnits)
Stable file tokens, independent of enum ordering so inserting a value later cannot change what an exi...
const char * GeneratedTableAlignToken(GENERATED_TABLE_ALIGN aAlign)
void CollectFilesLoopSafe(const wxString &aRoot, wxArrayString &aFiles, const wxString &aFileSpec, int aFlags)
Recursively collect every file under aRoot, deduplicating subdirectories by their resolved path.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_IO_ERRORF(msg,...)
#define THROW_IO_CANCELLED()
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
static const std::string KiCadFootprintFileExtension
const wxChar *const traceKicadPcbPlugin
Flag to enable KiCad PCB plugin debug output.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition idf_helpers.h:37
#define MAX_CU_LAYERS
Definition layer_ids.h:172
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:703
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition layer_ids.h:714
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ B_Adhes
Definition layer_ids.h:99
@ F_Paste
Definition layer_ids.h:100
@ F_Adhes
Definition layer_ids.h:98
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ B_Paste
Definition layer_ids.h:101
@ UNSELECTED_LAYER
Definition layer_ids.h:58
@ F_Fab
Definition layer_ids.h:115
@ F_SilkS
Definition layer_ids.h:96
@ B_CrtYd
Definition layer_ids.h:111
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ User_1
Definition layer_ids.h:120
@ B_SilkS
Definition layer_ids.h:97
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:167
@ F_Cu
Definition layer_ids.h:60
@ B_Fab
Definition layer_ids.h:114
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:92
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:25
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 FormatCustomProperties(OUTPUTFORMATTER *aOut, const EDA_ITEM &aItem)
Writes the item's custom properties as a series of (custom_property "key" "value")
void FormatOptBool(OUTPUTFORMATTER *aOut, const wxString &aKey, std::optional< bool > aValue)
Writes an optional boolean to the formatter.
void FormatUuid(OUTPUTFORMATTER *aOut, const KIID &aUuid)
void FormatStreamData(OUTPUTFORMATTER &aOut, const wxStreamBuffer &aStream)
Write binary data to the formatter as base 64 encoded string.
void FormatBool(OUTPUTFORMATTER *aOut, const wxString &aKey, bool aValue)
Writes a boolean to the formatter, in the style (aKey [yes|no])
long long TimestampDir(const wxString &aDirPath, const wxString &aFilespec)
Computes a hash of modification times and sizes for files matching a pattern.
Definition unix/io.cpp:123
constexpr char APPEND_PRESERVE_DESTINATION_STACKUP[]
Definition pcb_io.h:45
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:98
@ PTH
Plated through hole pad.
Definition padstack.h:97
@ CONN
Like smd, does not appear on the solder paste layer (default) Note: also has a special attribute in G...
Definition padstack.h:99
@ CHAMFERED_RECT
Definition padstack.h:59
@ ROUNDRECT
Definition padstack.h:56
@ TRAPEZOID
Definition padstack.h:55
@ RECTANGLE
Definition padstack.h:53
@ FIDUCIAL_LOCAL
a fiducial (usually a smd) local to the parent footprint
Definition padstack.h:117
@ FIDUCIAL_GLBL
a fiducial (usually a smd) for the full board
Definition padstack.h:116
@ MECHANICAL
a pad used for mechanical support
Definition padstack.h:121
@ PRESSFIT
a PTH with a hole diameter with tight tolerances for press fit pin
Definition padstack.h:122
@ HEATSINK
a pad used as heat sink, usually in SMD footprints
Definition padstack.h:119
@ NONE
no special fabrication property
Definition padstack.h:114
@ TESTPOINT
a test point pad
Definition padstack.h:118
@ CASTELLATED
a pad with a castellated through hole
Definition padstack.h:120
@ BGA
Smd pad, used in BGA footprints.
Definition padstack.h:115
BARCODE class definition.
bool ConstraintValueIsLength(PCB_CONSTRAINT_TYPE aType)
True if this type's value is a length in IU (serialized in mm); false for an angle in degrees.
const char * ConstraintTypeToken(PCB_CONSTRAINT_TYPE aType)
Stable file-format token for a constraint type (e.g. "parallel"). Used by serialization.
const char * ConstraintAnchorToken(CONSTRAINT_ANCHOR aAnchor)
Stable file-format token for a member anchor (e.g. "start").
@ VERTEX
An indexed rectangle corner or polygon outline vertex; pairs with CONSTRAINT_MEMBER::m_index.
Class to handle a set of BOARD_ITEMs.
static void formatDrillSpan(OUTPUTFORMATTER *aOut, const DRILL_SPAN &aSpan)
Layers plus the flags that distinguish a backdrill or non-plated span from the primary one that share...
bool isDefaultTeardropParameters(const TEARDROP_PARAMETERS &tdParams)
static VECTOR2I unbakeSize(const VECTOR2I &aSize, const FOOTPRINT *aParentFP)
std::string formatInternalUnits(const int aValue, const EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
static int unbakeLinear(int aValue, const FOOTPRINT *aParentFP)
static VECTOR2I unbakeSizeUniform(const VECTOR2I &aSize, const FOOTPRINT *aParentFP)
#define SEXPR_BOARD_FILE_VERSION
Current s-expression file format version. 2 was the last legacy format version.
#define CTL_FOR_BOARD
The zero arg constructor when PCB_PLUGIN is used for PLUGIN::Load() and PLUGIN::Save()ing a BOARD fil...
#define CTL_FOR_LIBRARY
Format output for a footprint library instead of clipboard or BOARD.
Pcbnew s-expression file format parser definition.
#define UNDEFINED_DRILL_DIAMETER
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
@ RPT_SEVERITY_WARNING
std::string FormatDouble2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 This function is intended in...
bool ReplaceIllegalFileNameChars(std::string &aName, int aReplaceChar)
Checks aName for illegal file name characters.
int ValueStringCompare(const wxString &strFWord, const wxString &strSWord)
Compare strings like the strcmp function but handle numbers and modifiers within the string text corr...
One participant in a constraint: a referenced board item plus the feature of that item that participa...
Which holes a chart reports on.
PCB_LAYER_ID DrillEndLayer() const
Definition drill_span.h:88
PCB_LAYER_ID DrillStartLayer() const
Definition drill_span.h:83
bool m_IsNonPlatedFile
Definition drill_span.h:131
bool m_IsBackdrill
Definition drill_span.h:130
Variant of PARSE_ERROR indicating that a syntax or related error was likely caused by a file generate...
The column a schema knows how to build, independent of whether any particular table shows it.
A column as a generated table actually carries it: what a row's cell in that column holds and how it ...
PCB_LAYER_ID start
Definition padstack.h:275
PCB_LAYER_ID end
Definition padstack.h:276
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
Definition padstack.h:273
std::optional< bool > is_capped
True if the drill hole should be capped.
Definition padstack.h:279
std::optional< bool > is_filled
True if the drill hole should be filled completely.
Definition padstack.h:278
std::optional< bool > has_covering
True if the pad on this side should have covering.
Definition padstack.h:261
std::optional< bool > has_solder_mask
True if this outer layer has mask (is not tented)
Definition padstack.h:259
std::optional< bool > has_plugging
True if the drill hole should be plugged on this side.
Definition padstack.h:262
A filename or source description, a problem input line, a line number, a byte offset,...
bool routing
Used by the router as a local routing frame.
bool cursor
Replace the display grid for cursor snapping inside coverage.
bool placement
Used by edit/move tools for placement snap.
Parameters that drive copper-thieving fill generation.
THIEVING_PATTERN pattern
EDA_ORIENTATION orientation
std::optional< VECTOR2I > hatching_offset
VECTOR2I center
const SHAPE_LINE_CHAIN chain
int delta
wxLogTrace helper definitions.
#define kv
@ PCB_T
Definition typeinfo.h:74
@ PCB_CONSTRAINT_T
a geometric constraint between board items
Definition typeinfo.h:237
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:98
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:95
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:83
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_DRILL_MAP_T
class PCB_DRILL_MAP, drill symbols drawn at the holes
Definition typeinfo.h:240
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:96
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:103
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:85
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:100
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:81
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:82
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:93
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:99
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:87
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:78
@ PCB_GRID_ITEM_T
a subgrid placed on a board
Definition typeinfo.h:238
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:94
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:86
@ PCB_POINT_T
class PCB_POINT, a 0-dimensional point
Definition typeinfo.h:105
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:97
@ PCB_DRILL_CHART_T
class PCB_DRILL_CHART, a live drill chart derived from PCB_TABLE
Definition typeinfo.h:239
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
Definition of file extensions used in Kicad.
@ THERMAL
Use thermal relief for pads.
Definition zones.h:46
@ THT_THERMAL
Thermal relief only for THT pads.
Definition zones.h:48
@ NONE
Pads are not covered.
Definition zones.h:45
@ FULL
pads are covered by copper
Definition zones.h:47