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