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