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