KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_io_geda.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 Wayne Stambaugh <[email protected]>
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 * This file contains file format knowledge derived from the gEDA/pcb project:
25 *
26 * gEDA/gaf - Copyright (C) 1998-2010 Ales Hvezda
27 * Copyright (C) 1998-2016 gEDA Contributors
28 * Lepton EDA - Copyright (C) 2017-2024 Lepton EDA Contributors
29 *
30 * Both projects are licensed under the GNU General Public License v2 or later.
31 * See https://github.com/lepton-eda/lepton-eda and
32 * https://github.com/rlutz/geda-gaf
33 */
34
40
41#include <kiplatform/io.h>
43#include <string_utils.h>
44#include <trace_helpers.h>
45#include <math/util.h> // for KiROUND
46
47#include <board.h>
49#include <font/fontconfig.h>
50#include <footprint.h>
51#include <gestfich.h>
52#include <netinfo.h>
53#include <pad.h>
54#include <macros.h>
55#include <pcb_text.h>
56#include <pcb_track.h>
57#include <pcb_shape.h>
58#include <reporter.h>
59#include <zone.h>
60#include <wx_filename.h>
61
62#include <wx/dir.h>
63#include <wx/log.h>
64#include <wx/filename.h>
65#include <wx/txtstrm.h>
66#include <wx/wfstream.h>
67#include <boost/ptr_container/ptr_map.hpp>
68#include <filter_reader.h>
69
70
71static inline long parseInt( const wxString& aValue, double aScalar )
72{
73 double value = std::numeric_limits<double>::max();
74
75 /*
76 * In 2011 gEDA/pcb introduced values with units, like "10mm" or "200mil".
77 * Unit-less values are still centimils (100000 units per inch), like with
78 * the previous format.
79 *
80 * Distinction between the even older format (mils, 1000 units per inch)
81 * and the pre-2011 format is done in ::parseFOOTPRINT already; the
82 * distinction is by whether an object definition opens with '(' or '['.
83 * All values with explicit unit open with a '[' so there's no need to
84 * consider this distinction when parsing them.
85 *
86 * The solution here is to watch for a unit and, if present, convert the
87 * value to centimils. All unit-less values are read unaltered. This way
88 * the code below can continue to consider all read values to be in mils or
89 * centimils. It also matches the strategy gEDA/pcb uses for backwards
90 * compatibility with its own layouts.
91 *
92 * Fortunately gEDA/pcb allows only units 'mil' and 'mm' in files, see
93 * definition of ALLOW_READABLE in gEDA/pcb's pcb_printf.h. So we don't
94 * have to test for all 11 units gEDA/pcb allows in user dialogs.
95 */
96 if( aValue.EndsWith( wxT( "mm" ) ) )
97 {
98 aScalar *= 100000.0 / 25.4;
99 }
100 else if( aValue.EndsWith( wxT( "mil" ) ) )
101 {
102 aScalar *= 100.;
103 }
104
105 // This conversion reports failure on strings as simple as "1000", still
106 // it returns the right result in &value. Thus, ignore the return value.
107 aValue.ToCDouble(&value);
108
109 if( value == std::numeric_limits<double>::max() ) // conversion really failed
110 {
111 THROW_IO_ERROR( wxString::Format( _( "Cannot convert '%s' to an integer." ),
112 aValue.GetData() ) );
113 }
114
115 return KiROUND( value * aScalar );
116}
117
118
119#define TEXT_DEFAULT_SIZE ( 40 * pcbIUScale.IU_PER_MILS )
120#define OLD_GPCB_UNIT_CONV pcbIUScale.IU_PER_MILS
121#define NEW_GPCB_UNIT_CONV ( 0.01 * pcbIUScale.IU_PER_MILS )
122
123
133{
134public:
135 GPCB_FPL_CACHE_ENTRY( FOOTPRINT* aFootprint, const WX_FILENAME& aFileName ) :
136 m_filename( aFileName ),
137 m_footprint( aFootprint )
138 { }
139
141 std::unique_ptr<FOOTPRINT>& GetFootprint() { return m_footprint; }
142
143private:
145 std::unique_ptr<FOOTPRINT> m_footprint;
146};
147
148
150{
151public:
152 GPCB_FPL_CACHE( PCB_IO_GEDA* aOwner, const wxString& aLibraryPath );
153
154 wxString GetPath() const { return m_lib_path.GetPath(); }
155 bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); }
156 boost::ptr_map<std::string, GPCB_FPL_CACHE_ENTRY>& GetFootprints() { return m_footprints; }
157
158 // Most all functions in this class throw IO_ERROR exceptions. There are no
159 // error codes nor user interface calls from here, nor in any PLUGIN.
160 // Catch these exceptions higher up please.
161
163
164 void Load();
165
166 void Remove( const wxString& aFootprintName );
167
174 static long long GetTimestamp( const wxString& aLibPath );
175
179 bool IsModified();
180
181private:
182 FOOTPRINT* parseFOOTPRINT( LINE_READER* aLineReader );
183
194 bool testFlags( const wxString& aFlag, long aMask, const wxChar* aName );
195
211 void parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader );
212
214 wxFileName m_lib_path;
215
216 boost::ptr_map<std::string, GPCB_FPL_CACHE_ENTRY> m_footprints;
218
223};
224
225
226GPCB_FPL_CACHE::GPCB_FPL_CACHE( PCB_IO_GEDA* aOwner, const wxString& aLibraryPath )
227{
228 m_owner = aOwner;
229 m_lib_path.SetPath( aLibraryPath );
231 m_cache_dirty = true;
232}
233
234
236{
237 m_cache_dirty = false;
239
240 // Note: like our .pretty footprint libraries, the gpcb footprint libraries are folders,
241 // and the footprints are the .fp files inside this folder.
242
243 wxDir dir( m_lib_path.GetPath() );
244
245 if( !dir.IsOpened() )
246 {
247 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' not found." ),
248 m_lib_path.GetPath().GetData() ) );
249 }
250
251 wxString fullName;
252 wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::GedaPcbFootprintLibFileExtension );
253
254 // wxFileName construction is egregiously slow. Construct it once and just swap out
255 // the filename thereafter.
256 WX_FILENAME fn( m_lib_path.GetPath(), wxT( "dummyName" ) );
257
258 if( !dir.GetFirst( &fullName, fileSpec ) )
259 return;
260
261 wxString cacheErrorMsg;
262
263 do
264 {
265 fn.SetFullName( fullName );
266
267 // Queue I/O errors so only files that fail to parse don't get loaded.
268 try
269 {
270 // reader now owns fp, will close on exception or return
271 FILE_LINE_READER reader( fn.GetFullPath() );
272 std::string name = TO_UTF8( fn.GetName() );
273 FOOTPRINT* footprint = parseFOOTPRINT( &reader );
274
275 // The footprint name is the file name without the extension.
276 footprint->SetFPID( LIB_ID( wxEmptyString, fn.GetName() ) );
277 m_footprints.insert( name, new GPCB_FPL_CACHE_ENTRY( footprint, fn ) );
278 }
279 catch( const IO_ERROR& ioe )
280 {
281 if( !cacheErrorMsg.IsEmpty() )
282 cacheErrorMsg += wxT( "\n\n" );
283
284 cacheErrorMsg += ioe.What();
285 }
286 } while( dir.GetNext( &fullName ) );
287
288 if( !cacheErrorMsg.IsEmpty() )
289 THROW_IO_ERROR( cacheErrorMsg );
290}
291
292
293void GPCB_FPL_CACHE::Remove( const wxString& aFootprintName )
294{
295 std::string footprintName = TO_UTF8( aFootprintName );
296
297 auto it = m_footprints.find( footprintName );
298
299 if( it == m_footprints.end() )
300 {
301 THROW_IO_ERROR( wxString::Format( _( "Library '%s' has no footprint '%s'." ),
302 m_lib_path.GetPath().GetData(),
303 aFootprintName.GetData() ) );
304 }
305
306 // Remove the footprint from the cache and delete the footprint file from the library.
307 wxString fullPath = it->second->GetFileName().GetFullPath();
308 m_footprints.erase( footprintName );
309 wxRemoveFile( fullPath );
310}
311
312
319
320
321long long GPCB_FPL_CACHE::GetTimestamp( const wxString& aLibPath )
322{
323 wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::GedaPcbFootprintLibFileExtension );
324
325 return KIPLATFORM::IO::TimestampDir( aLibPath, fileSpec );
326}
327
328
330{
331 int paramCnt;
332
333 // GPCB unit = 0.01 mils and Pcbnew 0.1.
334 double conv_unit = NEW_GPCB_UNIT_CONV;
335 wxString msg;
336 wxArrayString parameters;
337 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( nullptr );
338
339 if( aLineReader->ReadLine() == nullptr )
340 {
341 msg = aLineReader->GetSource() + wxT( ": empty file" );
342 THROW_IO_ERROR( msg );
343 }
344
345 parameters.Clear();
346 parseParameters( parameters, aLineReader );
347 paramCnt = parameters.GetCount();
348
349 /* From the Geda PCB documentation, valid Element definitions:
350 * Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags]
351 * Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags)
352 * Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags)
353 * Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags)
354 * Element ("Desc" "Name" TX TY TDir TScale TNFlags)
355 */
356
357 if( parameters[0].CmpNoCase( wxT( "Element" ) ) != 0 )
358 {
359 msg.Printf( _( "Unknown token '%s'" ), parameters[0] );
360 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
361 aLineReader->LineNumber(), 0 );
362 }
363
364 if( paramCnt < 10 || paramCnt > 14 )
365 {
366 msg.Printf( _( "Element token contains %d parameters." ), paramCnt );
367 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
368 aLineReader->LineNumber(), 0 );
369 }
370
371 // Test symbol after "Element": if [ units = 0.01 mils, and if ( units = 1 mil
372 if( parameters[1] == wxT( "(" ) )
373 conv_unit = OLD_GPCB_UNIT_CONV;
374
375 if( paramCnt > 10 )
376 {
377 footprint->SetLibDescription( parameters[3] );
378 footprint->SetReference( parameters[4] );
379 }
380 else
381 {
382 footprint->SetLibDescription( parameters[2] );
383 footprint->SetReference( parameters[3] );
384 }
385
386 // Read value
387 if( paramCnt > 10 )
388 footprint->SetValue( parameters[5] );
389
390 // With gEDA/pcb, value is meaningful after instantiation, only, so it's
391 // often empty in bare footprints.
392 if( footprint->Value().GetText().IsEmpty() )
393 footprint->Value().SetText( wxT( "VAL**" ) );
394
395 if( footprint->Reference().GetText().IsEmpty() )
396 footprint->Reference().SetText( wxT( "REF**" ) );
397
398 while( aLineReader->ReadLine() )
399 {
400 parameters.Clear();
401 parseParameters( parameters, aLineReader );
402
403 if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
404 continue;
405
406 if( parameters[0] == wxT( ")" ) )
407 break;
408
409 paramCnt = parameters.GetCount();
410
411 // Test units value for a string line param (more than 3 parameters : ident [ xx ] )
412 if( paramCnt > 3 )
413 {
414 if( parameters[1] == wxT( "(" ) )
415 conv_unit = OLD_GPCB_UNIT_CONV;
416 else
417 conv_unit = NEW_GPCB_UNIT_CONV;
418 }
419
420 wxLogTrace( traceGedaPcbPlugin, wxT( "%s parameter count = %d." ),
421 parameters[0], paramCnt );
422
423 // Parse a line with format: ElementLine [X1 Y1 X2 Y2 Thickness]
424 if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 )
425 {
426 if( paramCnt != 8 )
427 {
428 msg.Printf( wxT( "ElementLine token contains %d parameters." ), paramCnt );
429 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
430 aLineReader->LineNumber(), 0 );
431 }
432
433 PCB_SHAPE* shape = new PCB_SHAPE( footprint.get(), SHAPE_T::SEGMENT );
434 shape->SetLayer( F_SilkS );
435 shape->SetStart( VECTOR2I( static_cast<int>( parseInt( parameters[2], conv_unit ) ),
436 static_cast<int>( parseInt( parameters[3], conv_unit ) ) ) );
437 shape->SetEnd( VECTOR2I( static_cast<int>( parseInt( parameters[4], conv_unit ) ),
438 static_cast<int>( parseInt( parameters[5], conv_unit ) ) ) );
439 shape->SetStroke( STROKE_PARAMS( static_cast<int>( parseInt( parameters[6], conv_unit ) ),
441
442 shape->Rotate( { 0, 0 }, footprint->GetOrientation() );
443 shape->Move( footprint->GetPosition() );
444
445 footprint->Add( shape );
446 continue;
447 }
448
449 // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
450 if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
451 {
452 if( paramCnt != 10 )
453 {
454 msg.Printf( wxT( "ElementArc token contains %d parameters." ), paramCnt );
455 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
456 aLineReader->LineNumber(), 0 );
457 }
458
459 // Pcbnew does know ellipse so we must have Width = Height
460 PCB_SHAPE* shape = new PCB_SHAPE( footprint.get(), SHAPE_T::ARC );
461 shape->SetLayer( F_SilkS );
462 footprint->Add( shape );
463
464 // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses
465 int radius = static_cast<int>( ( parseInt( parameters[4], conv_unit ) +
466 parseInt( parameters[5], conv_unit ) ) / 2 );
467
468 VECTOR2I centre( static_cast<int>( parseInt( parameters[2], conv_unit ) ),
469 static_cast<int>( parseInt( parameters[3], conv_unit ) ) );
470
471 // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
472 EDA_ANGLE start_angle( static_cast<int>( parseInt( parameters[6], -10.0 ) ),
474 start_angle += ANGLE_180;
475
476 // Pcbnew delta angle direction is the opposite of Geda PCB delta angles.
477 EDA_ANGLE sweep_angle( static_cast<int>( parseInt( parameters[7], -10.0 ) ),
479
480 // Geda PCB does not support circles.
481 if( sweep_angle == -ANGLE_360 )
482 {
483 shape->SetShape( SHAPE_T::CIRCLE );
484 shape->SetCenter( centre );
485 shape->SetEnd( centre + VECTOR2I( radius, 0 ) );
486 }
487 else
488 {
489 // Calculate start point coordinate of arc
490 VECTOR2I arcStart( radius, 0 );
491 RotatePoint( arcStart, -start_angle );
492 shape->SetCenter( centre );
493 shape->SetStart( arcStart + centre );
494
495 // Angle value is clockwise in gpcb and Pcbnew.
496 shape->SetArcAngleAndEnd( sweep_angle, true );
497 }
498
499 shape->SetStroke( STROKE_PARAMS( static_cast<int>( parseInt( parameters[8], conv_unit ) ),
501
502 shape->Rotate( { 0, 0 }, footprint->GetOrientation() );
503 shape->Move( footprint->GetPosition() );
504 continue;
505 }
506
507 // Parse a Pad with no hole with format:
508 // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
509 // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
510 // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
511 // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
512 if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
513 {
514 if( paramCnt < 10 || paramCnt > 13 )
515 {
516 msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt );
517 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
518 aLineReader->LineNumber(), 0 );
519 }
520
521 std::unique_ptr<PAD> pad = std::make_unique<PAD>( footprint.get() );
522
523 static const LSET pad_front( { F_Cu, F_Mask, F_Paste } );
524 static const LSET pad_back( { B_Cu, B_Mask, B_Paste } );
525
527 pad->SetAttribute( PAD_ATTRIB::SMD );
528 pad->SetLayerSet( pad_front );
529
530 if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
531 pad->SetLayerSet( pad_back );
532
533 // Set the pad name:
534 // Pcbnew pad name is used for electrical connection calculations.
535 // Accordingly it should be mapped to gEDA's pin/pad number,
536 // which is used for the same purpose.
537 // gEDA also features a pin/pad "name", which is an arbitrary string
538 // and set to the pin name of the netlist on instantiation. Many gEDA
539 // bare footprints use identical strings for name and number, so this
540 // can be a bit confusing.
541 pad->SetNumber( parameters[paramCnt-3] );
542
543 int x1 = static_cast<int>( parseInt( parameters[2], conv_unit ) );
544 int x2 = static_cast<int>( parseInt( parameters[4], conv_unit ) );
545 int y1 = static_cast<int>( parseInt( parameters[3], conv_unit ) );
546 int y2 = static_cast<int>( parseInt( parameters[5], conv_unit ) );
547 int width = static_cast<int>( parseInt( parameters[6], conv_unit ) );
548 VECTOR2I delta( x2 - x1, y2 - y1 );
549 double angle = atan2( (double)delta.y, (double)delta.x );
550
551 // Get the pad clearance and the solder mask clearance.
552 if( paramCnt == 13 )
553 {
554 int clearance = static_cast<int>( parseInt( parameters[7], conv_unit ) );
555 // One of gEDA's oddities is that clearance between pad and polygon
556 // is given as the gap on both sides of the pad together, so for
557 // KiCad it has to halfed.
558 pad->SetLocalClearance( clearance / 2 );
559
560 // In GEDA, the mask value is the size of the hole in this
561 // solder mask. In Pcbnew, it is a margin, therefore the distance
562 // between the copper and the mask
563 int maskMargin = static_cast<int>( parseInt( parameters[8], conv_unit ) );
564 maskMargin = ( maskMargin - width ) / 2;
565 pad->SetLocalSolderMaskMargin( maskMargin );
566 }
567
568 // Negate angle (due to Y reversed axis)
569 EDA_ANGLE orient( -angle, RADIANS_T );
570 pad->SetOrientation( orient );
571
572 VECTOR2I padPos( ( x1 + x2 ) / 2, ( y1 + y2 ) / 2 );
573
574 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( delta.EuclideanNorm() + width, width ) );
575
576 padPos += footprint->GetPosition();
577 pad->SetPosition( padPos );
578
579 if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
580 {
581 if( pad->GetSize( PADSTACK::ALL_LAYERS ).x == pad->GetSize( PADSTACK::ALL_LAYERS ).y )
583 else
585 }
586
587 if( pad->GetSizeX() > 0 && pad->GetSizeY() > 0 )
588 {
589 footprint->Add( pad.release() );
590 }
591 else
592 {
593 wxLogError( _( "Invalid zero-sized pad ignored in\nfile: %s" ),
594 aLineReader->GetSource() );
595 }
596
597 continue;
598 }
599
600 // Parse a Pin with through hole with format:
601 // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
602 // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
603 // Pin (aX aY Thickness Drill "Name" "Number" NFlags)
604 // Pin (aX aY Thickness Drill "Name" NFlags)
605 // Pin (aX aY Thickness "Name" NFlags)
606 if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
607 {
608 if( paramCnt < 8 || paramCnt > 12 )
609 {
610 msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt );
611 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
612 aLineReader->LineNumber(), 0 );
613 }
614
615 PAD* pad = new PAD( footprint.get() );
616
618
619 static const LSET pad_set = LSET::AllCuMask() | LSET( { F_SilkS, F_Mask, B_Mask } );
620
621 pad->SetLayerSet( pad_set );
622
623 if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
625
626 // Set the pad name:
627 // Pcbnew pad name is used for electrical connection calculations.
628 // Accordingly it should be mapped to gEDA's pin/pad number,
629 // which is used for the same purpose.
630 pad->SetNumber( parameters[paramCnt-3] );
631
632 VECTOR2I padPos( static_cast<int>( parseInt( parameters[2], conv_unit ) ),
633 static_cast<int>( parseInt( parameters[3], conv_unit ) ) );
634
635 int padSize = static_cast<int>( parseInt( parameters[4], conv_unit ) );
636
637 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( padSize, padSize ) );
638
639 int drillSize = 0;
640
641 // Get the pad clearance, solder mask clearance, and drill size.
642 if( paramCnt == 12 )
643 {
644 int clearance = static_cast<int>( parseInt( parameters[5], conv_unit ) );
645 // One of gEDA's oddities is that clearance between pad and polygon
646 // is given as the gap on both sides of the pad together, so for
647 // KiCad it has to halfed.
648 pad->SetLocalClearance( clearance / 2 );
649
650 // In GEDA, the mask value is the size of the hole in this
651 // solder mask. In Pcbnew, it is a margin, therefore the distance
652 // between the copper and the mask
653 int maskMargin = static_cast<int>( parseInt( parameters[6], conv_unit ) );
654 maskMargin = ( maskMargin - padSize ) / 2;
655 pad->SetLocalSolderMaskMargin( maskMargin );
656
657 drillSize = static_cast<int>( parseInt( parameters[7], conv_unit ) );
658 }
659 else
660 {
661 drillSize = static_cast<int>( parseInt( parameters[5], conv_unit ) );
662 }
663
664 pad->SetDrillSize( VECTOR2I( drillSize, drillSize ) );
665
666 padPos += footprint->GetPosition();
667 pad->SetPosition( padPos );
668
669 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE
670 && pad->GetSize( PADSTACK::ALL_LAYERS ).x != pad->GetSize( PADSTACK::ALL_LAYERS ).y )
671 {
673 }
674
675 footprint->Add( pad );
676 continue;
677 }
678 }
679
680 footprint->AutoPositionFields();
681
682 return footprint.release();
683}
684
685
686void GPCB_FPL_CACHE::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
687{
688 char key;
689 wxString tmp;
690 char* line = aLineReader->Line();
691
692 // Last line already ready in main parser loop.
693 while( *line != 0 )
694 {
695 key = *line;
696 line++;
697
698 switch( key )
699 {
700 case '[':
701 case '(':
702 if( !tmp.IsEmpty() )
703 {
704 aParameterList.Add( tmp );
705 tmp.Clear();
706 }
707
708 tmp.Append( key );
709 aParameterList.Add( tmp );
710 tmp.Clear();
711
712 // Opening delimiter "(" after Element statement. Any other occurrence is part
713 // of a keyword definition.
714 if( aParameterList.GetCount() == 1 )
715 {
716 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
717 return;
718 }
719
720 break;
721
722 case ']':
723 case ')':
724 if( !tmp.IsEmpty() )
725 {
726 aParameterList.Add( tmp );
727 tmp.Clear();
728 }
729
730 tmp.Append( key );
731 aParameterList.Add( tmp );
732 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
733 return;
734
735 case '\n':
736 case '\r':
737 // Element descriptions can span multiple lines.
738 line = aLineReader->ReadLine();
740
741 case '\t':
742 case ' ':
743 if( !tmp.IsEmpty() )
744 {
745 aParameterList.Add( tmp );
746 tmp.Clear();
747 }
748
749 break;
750
751 case '"':
752 // Handle empty quotes.
753 if( *line == '"' )
754 {
755 line++;
756 tmp.Clear();
757 aParameterList.Add( wxEmptyString );
758 break;
759 }
760
761 while( *line != 0 )
762 {
763 key = *line;
764 line++;
765
766 if( key == '"' )
767 {
768 aParameterList.Add( tmp );
769 tmp.Clear();
770 break;
771 }
772 else
773 {
774 tmp.Append( key );
775 }
776 }
777
778 break;
779
780 case '#':
781 line = aLineReader->ReadLine();
782
783 if( !line )
784 return;
785
786 break;
787
788 default:
789 tmp.Append( key );
790 break;
791 }
792 }
793}
794
795
796bool GPCB_FPL_CACHE::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
797{
798 wxString number;
799
800 if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
801 {
802 long lflags;
803
804 if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
805 return true;
806 }
807 else if( aFlag.Contains( aName ) )
808 {
809 return true;
810 }
811
812 return false;
813}
814
815
816PCB_IO_GEDA::PCB_IO_GEDA() : PCB_IO( wxS( "gEDA PCB" ) ),
817 m_cache( nullptr ),
818 m_ctl( 0 ),
820{
821 m_reader = nullptr;
822 init( nullptr );
823}
824
825
826PCB_IO_GEDA::PCB_IO_GEDA( int aControlFlags ) : PCB_IO( wxS( "gEDA PCB" ) ),
827 m_cache( nullptr ),
828 m_ctl( aControlFlags ),
830{
831 m_reader = nullptr;
832 init( nullptr );
833}
834
835
837{
838 for( FOOTPRINT* fp : m_cachedFootprints )
839 delete fp;
840
841 delete m_cache;
842}
843
844
845void PCB_IO_GEDA::init( const std::map<std::string, UTF8>* aProperties )
846{
847 m_props = aProperties;
848}
849
850
851void PCB_IO_GEDA::validateCache( const wxString& aLibraryPath, bool checkModified )
852{
853 if( !m_cache || ( checkModified && m_cache->IsModified() ) )
854 {
855 // a spectacular episode in memory management:
856 delete m_cache;
857 m_cache = new GPCB_FPL_CACHE( this, aLibraryPath );
858 m_cache->Load();
859 }
860}
861
862
863FOOTPRINT* PCB_IO_GEDA::ImportFootprint( const wxString& aFootprintPath,
864 wxString& aFootprintNameOut,
865 const std::map<std::string, UTF8>* aProperties )
866{
867 wxFileName fn( aFootprintPath );
868
869 FILE_LINE_READER freader( aFootprintPath );
870 WHITESPACE_FILTER_READER reader( freader );
871
872 reader.ReadLine();
873 char* line = reader.Line();
874
875 if( !line )
876 return nullptr;
877
878 if( strncasecmp( line, "Element", strlen( "Element" ) ) != 0 )
879 return nullptr;
880
881 aFootprintNameOut = fn.GetName();
882
883 return FootprintLoad( fn.GetPath(), aFootprintNameOut );
884}
885
886
887void PCB_IO_GEDA::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
888 bool aBestEfforts, const std::map<std::string, UTF8>* aProperties )
889{
890 wxDir dir( aLibraryPath );
891 wxString errorMsg;
892
893 if( !dir.IsOpened() )
894 {
895 if( aBestEfforts )
896 return;
897 else
898 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' not found." ), aLibraryPath ) );
899 }
900
901 init( aProperties );
902
903 try
904 {
905 validateCache( aLibraryPath );
906 }
907 catch( const IO_ERROR& ioe )
908 {
909 errorMsg = ioe.What();
910 }
911
912 // Some of the files may have been parsed correctly so we want to add the valid files to
913 // the library.
914
915 for( const auto& footprint : m_cache->GetFootprints() )
916 aFootprintNames.Add( From_UTF8( footprint.first.c_str() ) );
917
918 if( !errorMsg.IsEmpty() && !aBestEfforts )
919 THROW_IO_ERROR( errorMsg );
920}
921
922
923const FOOTPRINT* PCB_IO_GEDA::getFootprint( const wxString& aLibraryPath,
924 const wxString& aFootprintName,
925 const std::map<std::string, UTF8>* aProperties,
926 bool checkModified )
927{
928 init( aProperties );
929
930 validateCache( aLibraryPath, checkModified );
931
932 auto it = m_cache->GetFootprints().find( TO_UTF8( aFootprintName ) );
933
934 if( it == m_cache->GetFootprints().end() )
935 return nullptr;
936
937 return it->second->GetFootprint().get();
938}
939
940
941FOOTPRINT* PCB_IO_GEDA::FootprintLoad( const wxString& aLibraryPath,
942 const wxString& aFootprintName,
943 bool aKeepUUID,
944 const std::map<std::string, UTF8>* aProperties )
945{
946 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
947 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
948
949 const FOOTPRINT* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
950
951 if( footprint )
952 {
954 copy->SetParent( nullptr );
955 return copy;
956 }
957
958 return nullptr;
959}
960
961
962void PCB_IO_GEDA::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
963 const std::map<std::string, UTF8>* aProperties )
964{
965 init( aProperties );
966
967 validateCache( aLibraryPath );
968
969 if( !m_cache->IsWritable() )
970 {
971 THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only." ),
972 aLibraryPath.GetData() ) );
973 }
974
975 m_cache->Remove( aFootprintName );
976}
977
978
979bool PCB_IO_GEDA::DeleteLibrary( const wxString& aLibraryPath, const std::map<std::string, UTF8>* aProperties )
980{
981 wxFileName fn;
982 fn.SetPath( aLibraryPath );
983
984 // Return if there is no library path to delete.
985 if( !fn.DirExists() )
986 return false;
987
988 if( !fn.IsDirWritable() )
989 {
990 THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete folder '%s'." ),
991 aLibraryPath.GetData() ) );
992 }
993
994 wxDir dir( aLibraryPath );
995
996 if( dir.HasSubDirs() )
997 {
998 THROW_IO_ERROR( wxString::Format( _( "Library folder '%s' has unexpected sub-folders." ),
999 aLibraryPath.GetData() ) );
1000 }
1001
1002 // All the footprint files must be deleted before the directory can be deleted.
1003 if( dir.HasFiles() )
1004 {
1005 unsigned i;
1006 wxFileName tmp;
1007 wxArrayString files;
1008
1009 CollectFilesLoopSafe( aLibraryPath, files );
1010
1011 for( i = 0; i < files.GetCount(); i++ )
1012 {
1013 tmp = files[i];
1014
1015 if( tmp.GetExt() != FILEEXT::KiCadFootprintFileExtension )
1016 {
1017 THROW_IO_ERROR( wxString::Format( _( "Unexpected file '%s' found in library '%s'." ),
1018 files[i].GetData(),
1019 aLibraryPath.GetData() ) );
1020 }
1021 }
1022
1023 for( i = 0; i < files.GetCount(); i++ )
1024 {
1025 wxRemoveFile( files[i] );
1026 }
1027 }
1028
1029 wxLogTrace( traceGedaPcbPlugin, wxT( "Removing footprint library '%s'" ),
1030 aLibraryPath.GetData() );
1031
1032 // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
1033 // we don't want that. we want bare metal portability with no UI here.
1034 if( !wxRmdir( aLibraryPath ) )
1035 {
1036 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' cannot be deleted." ),
1037 aLibraryPath.GetData() ) );
1038 }
1039
1040 // For some reason removing a directory in Windows is not immediately updated. This delay
1041 // prevents an error when attempting to immediately recreate the same directory when over
1042 // writing an existing library.
1043#ifdef __WINDOWS__
1044 wxMilliSleep( 250L );
1045#endif
1046
1047 if( m_cache && m_cache->GetPath() == aLibraryPath )
1048 {
1049 delete m_cache;
1050 m_cache = nullptr;
1051 }
1052
1053 return true;
1054}
1055
1056
1057long long PCB_IO_GEDA::GetLibraryTimestamp( const wxString& aLibraryPath ) const
1058{
1059 return GPCB_FPL_CACHE::GetTimestamp( aLibraryPath );
1060}
1061
1062
1063bool PCB_IO_GEDA::IsLibraryWritable( const wxString& aLibraryPath )
1064{
1065 init( nullptr );
1066
1067 validateCache( aLibraryPath );
1068
1069 return m_cache->IsWritable();
1070}
1071
1072
1073// =====================================================================
1074// Board-level import
1075// =====================================================================
1076
1077
1078void PCB_IO_GEDA::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
1079{
1080 char key;
1081 wxString tmp;
1082 char* line = aLineReader->Line();
1083
1084 while( *line != 0 )
1085 {
1086 key = *line;
1087 line++;
1088
1089 switch( key )
1090 {
1091 case '[':
1092 case '(':
1093 if( !tmp.IsEmpty() )
1094 {
1095 aParameterList.Add( tmp );
1096 tmp.Clear();
1097 }
1098
1099 tmp.Append( key );
1100 aParameterList.Add( tmp );
1101 tmp.Clear();
1102
1103 if( aParameterList.GetCount() == 1 )
1104 {
1105 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
1106 return;
1107 }
1108
1109 break;
1110
1111 case ']':
1112 case ')':
1113 if( !tmp.IsEmpty() )
1114 {
1115 aParameterList.Add( tmp );
1116 tmp.Clear();
1117 }
1118
1119 tmp.Append( key );
1120 aParameterList.Add( tmp );
1121 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
1122 return;
1123
1124 case '\n':
1125 case '\r':
1126 line = aLineReader->ReadLine();
1127
1128 if( !line )
1129 return;
1130
1132
1133 case '\t':
1134 case ' ':
1135 if( !tmp.IsEmpty() )
1136 {
1137 aParameterList.Add( tmp );
1138 tmp.Clear();
1139 }
1140
1141 break;
1142
1143 case '"':
1144 if( *line == '"' )
1145 {
1146 line++;
1147 tmp.Clear();
1148 aParameterList.Add( wxEmptyString );
1149 break;
1150 }
1151
1152 while( *line != 0 )
1153 {
1154 key = *line;
1155 line++;
1156
1157 if( key == '"' )
1158 {
1159 aParameterList.Add( tmp );
1160 tmp.Clear();
1161 break;
1162 }
1163 else
1164 {
1165 tmp.Append( key );
1166 }
1167 }
1168
1169 break;
1170
1171 case '#':
1172 line = aLineReader->ReadLine();
1173
1174 if( !line )
1175 return;
1176
1177 break;
1178
1179 default:
1180 tmp.Append( key );
1181 break;
1182 }
1183 }
1184}
1185
1186
1187bool PCB_IO_GEDA::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
1188{
1189 wxString number;
1190
1191 if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
1192 {
1193 long lflags;
1194
1195 if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
1196 return true;
1197 }
1198 else if( aFlag.Contains( aName ) )
1199 {
1200 return true;
1201 }
1202
1203 return false;
1204}
1205
1206
1207bool PCB_IO_GEDA::CanReadBoard( const wxString& aFileName ) const
1208{
1209 if( !PCB_IO::CanReadBoard( aFileName ) )
1210 return false;
1211
1212 wxFileInputStream input( aFileName );
1213
1214 if( !input.IsOk() )
1215 return false;
1216
1217 wxTextInputStream text( input );
1218
1219 for( int i = 0; i < 20; i++ )
1220 {
1221 if( input.Eof() )
1222 return false;
1223
1224 wxString line = text.ReadLine();
1225
1226 if( line.Contains( wxS( "PCB[" ) ) || line.Contains( wxS( "PCB(" ) ) )
1227 return true;
1228 }
1229
1230 return false;
1231}
1232
1233
1234PCB_LAYER_ID PCB_IO_GEDA::mapLayer( int aGedaLayer, const wxString& aLayerName ) const
1235{
1236 wxString name = aLayerName.Lower();
1237
1238 if( name.Contains( wxT( "outline" ) ) || name.Contains( wxT( "route" ) ) )
1239 return Edge_Cuts;
1240
1241 if( name.Contains( wxT( "silk" ) ) )
1242 {
1243 if( name.Contains( wxT( "solder" ) ) || name.Contains( wxT( "bottom" ) ) )
1244 return B_SilkS;
1245
1246 return F_SilkS;
1247 }
1248
1249 if( name.Contains( wxT( "mask" ) ) )
1250 {
1251 if( name.Contains( wxT( "solder" ) ) || name.Contains( wxT( "bottom" ) ) )
1252 return B_Mask;
1253
1254 return F_Mask;
1255 }
1256
1257 if( name.Contains( wxT( "paste" ) ) )
1258 {
1259 if( name.Contains( wxT( "solder" ) ) || name.Contains( wxT( "bottom" ) ) )
1260 return B_Paste;
1261
1262 return F_Paste;
1263 }
1264
1265 if( name.Contains( wxT( "fab" ) ) )
1266 return F_Fab;
1267
1268 // Copper layers: gEDA uses 1-based numbering. 1 = component/top, 2 = solder/bottom.
1269 if( name.Contains( wxT( "component" ) ) || name.Contains( wxT( "top" ) )
1270 || ( aGedaLayer == 1 && !name.Contains( wxT( "solder" ) ) ) )
1271 {
1272 return F_Cu;
1273 }
1274
1275 if( name.Contains( wxT( "solder" ) ) || name.Contains( wxT( "bottom" ) )
1276 || aGedaLayer == 2 )
1277 {
1278 return B_Cu;
1279 }
1280
1281 // Inner copper layers (gEDA layer numbers 3+)
1282 if( aGedaLayer >= 3 && aGedaLayer <= 16 )
1283 {
1284 int innerIdx = aGedaLayer - 3;
1285 PCB_LAYER_ID innerLayers[] = { In1_Cu, In2_Cu, In3_Cu, In4_Cu, In5_Cu, In6_Cu,
1287 In13_Cu, In14_Cu };
1288
1289 if( innerIdx < 14 )
1290 return innerLayers[innerIdx];
1291 }
1292
1293 return F_Cu;
1294}
1295
1296
1297void PCB_IO_GEDA::parseVia( wxArrayString& aParameters, double aConvUnit )
1298{
1299 // Via[X Y Thickness Clearance Mask Drill "Name" SFlags]
1300 int paramCnt = aParameters.GetCount();
1301
1302 if( paramCnt < 10 )
1303 {
1304 THROW_IO_ERROR( wxString::Format(
1305 _( "Via token contains %d parameters, expected at least 10." ), paramCnt ) );
1306 }
1307
1308 PCB_VIA* via = new PCB_VIA( m_board );
1309
1310 int x = static_cast<int>( parseInt( aParameters[2], aConvUnit ) );
1311 int y = static_cast<int>( parseInt( aParameters[3], aConvUnit ) );
1312 int thickness = static_cast<int>( parseInt( aParameters[4], aConvUnit ) );
1313 int drill = static_cast<int>( parseInt( aParameters[7], aConvUnit ) );
1314
1315 via->SetPosition( VECTOR2I( x, y ) );
1316 via->SetWidth( PADSTACK::ALL_LAYERS, thickness );
1317 via->SetDrill( drill );
1318 via->SetViaType( VIATYPE::THROUGH );
1319 via->SetLayerPair( F_Cu, B_Cu );
1320 via->SetNet( NETINFO_LIST::OrphanedItem() );
1321
1322 m_board->Add( via, ADD_MODE::APPEND );
1323}
1324
1325
1326FOOTPRINT* PCB_IO_GEDA::parseElement( wxArrayString& aParameters, LINE_READER* aLineReader,
1327 double aConvUnit )
1328{
1329 int paramCnt = aParameters.GetCount();
1330 double conv_unit = aConvUnit;
1331 wxString msg;
1332
1333 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
1334
1335 if( paramCnt < 10 || paramCnt > 14 )
1336 {
1337 msg.Printf( _( "Element token contains %d parameters." ), paramCnt );
1338 THROW_IO_ERROR( msg );
1339 }
1340
1341 // The long form has SFlags, Desc, Name, Value, MX, MY, TX, TY, TDir, TScale, TSFlags
1342 // paramCnt == 14: Element [ SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags ]
1343 // paramCnt == 12: Element ( NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags )
1344 int descIdx, nameIdx, valueIdx, mxIdx;
1345
1346 if( paramCnt > 10 )
1347 {
1348 descIdx = 3;
1349 nameIdx = 4;
1350 valueIdx = 5;
1351 mxIdx = 6;
1352 }
1353 else
1354 {
1355 descIdx = 2;
1356 nameIdx = 3;
1357 valueIdx = -1;
1358 mxIdx = -1;
1359 }
1360
1361 footprint->SetLibDescription( aParameters[descIdx] );
1362 footprint->SetReference( aParameters[nameIdx] );
1363
1364 if( valueIdx > 0 )
1365 footprint->SetValue( aParameters[valueIdx] );
1366
1367 if( footprint->Value().GetText().IsEmpty() )
1368 footprint->Value().SetText( wxT( "VAL**" ) );
1369
1370 if( footprint->Reference().GetText().IsEmpty() )
1371 footprint->Reference().SetText( wxT( "REF**" ) );
1372
1373 // Set footprint position from MX, MY (absolute board coordinates)
1374 if( mxIdx > 0 && paramCnt > 12 )
1375 {
1376 int mx = static_cast<int>( parseInt( aParameters[mxIdx], conv_unit ) );
1377 int my = static_cast<int>( parseInt( aParameters[mxIdx + 1], conv_unit ) );
1378 footprint->SetPosition( VECTOR2I( mx, my ) );
1379 }
1380
1381 wxArrayString parameters;
1382
1383 while( aLineReader->ReadLine() )
1384 {
1385 parameters.Clear();
1386 parseParameters( parameters, aLineReader );
1387
1388 if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
1389 continue;
1390
1391 if( parameters[0] == wxT( ")" ) )
1392 break;
1393
1394 paramCnt = parameters.GetCount();
1395
1396 if( paramCnt > 3 )
1397 {
1398 if( parameters[1] == wxT( "(" ) )
1399 conv_unit = OLD_GPCB_UNIT_CONV;
1400 else
1401 conv_unit = NEW_GPCB_UNIT_CONV;
1402 }
1403
1404 // ElementLine [X1 Y1 X2 Y2 Thickness]
1405 if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 )
1406 {
1407 if( paramCnt != 8 )
1408 continue;
1409
1410 PCB_SHAPE* shape = new PCB_SHAPE( footprint.get(), SHAPE_T::SEGMENT );
1411 shape->SetLayer( F_SilkS );
1412 shape->SetStart( VECTOR2I( static_cast<int>( parseInt( parameters[2], conv_unit ) ),
1413 static_cast<int>( parseInt( parameters[3], conv_unit ) ) ) );
1414 shape->SetEnd( VECTOR2I( static_cast<int>( parseInt( parameters[4], conv_unit ) ),
1415 static_cast<int>( parseInt( parameters[5], conv_unit ) ) ) );
1416 shape->SetStroke( STROKE_PARAMS( static_cast<int>( parseInt( parameters[6], conv_unit ) ),
1418
1419 shape->Rotate( { 0, 0 }, footprint->GetOrientation() );
1420 shape->Move( footprint->GetPosition() );
1421
1422 footprint->Add( shape );
1423 continue;
1424 }
1425
1426 // ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
1427 if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
1428 {
1429 if( paramCnt != 10 )
1430 continue;
1431
1432 PCB_SHAPE* shape = new PCB_SHAPE( footprint.get(), SHAPE_T::ARC );
1433 shape->SetLayer( F_SilkS );
1434 footprint->Add( shape );
1435
1436 int radius = static_cast<int>( ( parseInt( parameters[4], conv_unit )
1437 + parseInt( parameters[5], conv_unit ) ) / 2 );
1438
1439 VECTOR2I centre( static_cast<int>( parseInt( parameters[2], conv_unit ) ),
1440 static_cast<int>( parseInt( parameters[3], conv_unit ) ) );
1441
1442 EDA_ANGLE start_angle( static_cast<int>( parseInt( parameters[6], -10.0 ) ),
1444 start_angle += ANGLE_180;
1445
1446 EDA_ANGLE sweep_angle( static_cast<int>( parseInt( parameters[7], -10.0 ) ),
1448
1449 if( sweep_angle == -ANGLE_360 )
1450 {
1451 shape->SetShape( SHAPE_T::CIRCLE );
1452 shape->SetCenter( centre );
1453 shape->SetEnd( centre + VECTOR2I( radius, 0 ) );
1454 }
1455 else
1456 {
1457 VECTOR2I arcStart( radius, 0 );
1458 RotatePoint( arcStart, -start_angle );
1459 shape->SetCenter( centre );
1460 shape->SetStart( arcStart + centre );
1461 shape->SetArcAngleAndEnd( sweep_angle, true );
1462 }
1463
1464 shape->SetStroke( STROKE_PARAMS( static_cast<int>( parseInt( parameters[8], conv_unit ) ),
1466
1467 shape->Rotate( { 0, 0 }, footprint->GetOrientation() );
1468 shape->Move( footprint->GetPosition() );
1469 continue;
1470 }
1471
1472 // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
1473 if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
1474 {
1475 if( paramCnt < 10 || paramCnt > 13 )
1476 continue;
1477
1478 std::unique_ptr<PAD> pad = std::make_unique<PAD>( footprint.get() );
1479
1480 static const LSET pad_front( { F_Cu, F_Mask, F_Paste } );
1481 static const LSET pad_back( { B_Cu, B_Mask, B_Paste } );
1482
1484 pad->SetAttribute( PAD_ATTRIB::SMD );
1485 pad->SetLayerSet( pad_front );
1486
1487 if( testFlags( parameters[paramCnt - 2], 0x0080, wxT( "onsolder" ) ) )
1488 pad->SetLayerSet( pad_back );
1489
1490 pad->SetNumber( parameters[paramCnt - 3] );
1491
1492 int x1 = static_cast<int>( parseInt( parameters[2], conv_unit ) );
1493 int x2 = static_cast<int>( parseInt( parameters[4], conv_unit ) );
1494 int y1 = static_cast<int>( parseInt( parameters[3], conv_unit ) );
1495 int y2 = static_cast<int>( parseInt( parameters[5], conv_unit ) );
1496 int width = static_cast<int>( parseInt( parameters[6], conv_unit ) );
1497 VECTOR2I delta( x2 - x1, y2 - y1 );
1498 double angle = atan2( (double) delta.y, (double) delta.x );
1499
1500 if( paramCnt == 13 )
1501 {
1502 int clearance = static_cast<int>( parseInt( parameters[7], conv_unit ) );
1503 pad->SetLocalClearance( clearance / 2 );
1504
1505 int maskMargin = static_cast<int>( parseInt( parameters[8], conv_unit ) );
1506 maskMargin = ( maskMargin - width ) / 2;
1507 pad->SetLocalSolderMaskMargin( maskMargin );
1508 }
1509
1510 EDA_ANGLE orient( -angle, RADIANS_T );
1511 pad->SetOrientation( orient );
1512
1513 VECTOR2I padPos( ( x1 + x2 ) / 2, ( y1 + y2 ) / 2 );
1514
1515 pad->SetSize( PADSTACK::ALL_LAYERS,
1516 VECTOR2I( delta.EuclideanNorm() + width, width ) );
1517
1518 padPos += footprint->GetPosition();
1519 pad->SetPosition( padPos );
1520
1521 if( !testFlags( parameters[paramCnt - 2], 0x0100, wxT( "square" ) ) )
1522 {
1523 if( pad->GetSize( PADSTACK::ALL_LAYERS ).x
1524 == pad->GetSize( PADSTACK::ALL_LAYERS ).y )
1525 {
1527 }
1528 else
1529 {
1531 }
1532 }
1533
1534 if( pad->GetSizeX() > 0 && pad->GetSizeY() > 0 )
1535 footprint->Add( pad.release() );
1536
1537 continue;
1538 }
1539
1540 // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
1541 if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
1542 {
1543 if( paramCnt < 8 || paramCnt > 12 )
1544 continue;
1545
1546 PAD* pad = new PAD( footprint.get() );
1547
1549
1550 static const LSET pad_set = LSET::AllCuMask()
1551 | LSET( { F_SilkS, F_Mask, B_Mask } );
1552
1553 pad->SetLayerSet( pad_set );
1554
1555 if( testFlags( parameters[paramCnt - 2], 0x0100, wxT( "square" ) ) )
1557
1558 pad->SetNumber( parameters[paramCnt - 3] );
1559
1560 VECTOR2I padPos( static_cast<int>( parseInt( parameters[2], conv_unit ) ),
1561 static_cast<int>( parseInt( parameters[3], conv_unit ) ) );
1562
1563 int padSize = static_cast<int>( parseInt( parameters[4], conv_unit ) );
1564 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( padSize, padSize ) );
1565
1566 int drillSize = 0;
1567
1568 if( paramCnt == 12 )
1569 {
1570 int clearance = static_cast<int>( parseInt( parameters[5], conv_unit ) );
1571 pad->SetLocalClearance( clearance / 2 );
1572
1573 int maskMargin = static_cast<int>( parseInt( parameters[6], conv_unit ) );
1574 maskMargin = ( maskMargin - padSize ) / 2;
1575 pad->SetLocalSolderMaskMargin( maskMargin );
1576
1577 drillSize = static_cast<int>( parseInt( parameters[7], conv_unit ) );
1578 }
1579 else
1580 {
1581 drillSize = static_cast<int>( parseInt( parameters[5], conv_unit ) );
1582 }
1583
1584 pad->SetDrillSize( VECTOR2I( drillSize, drillSize ) );
1585
1586 padPos += footprint->GetPosition();
1587 pad->SetPosition( padPos );
1588
1589 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE
1590 && pad->GetSize( PADSTACK::ALL_LAYERS ).x
1591 != pad->GetSize( PADSTACK::ALL_LAYERS ).y )
1592 {
1594 }
1595
1596 footprint->Add( pad );
1597 continue;
1598 }
1599 }
1600
1601 // Handle the onsolder element flag to flip bottom-side components.
1602 // In the long form, SFlags is at index 2; in the short form, NFlags is at index 2.
1603 wxString elementFlags = aParameters[2];
1604
1605 if( elementFlags.Contains( wxT( "onsolder" ) ) )
1606 footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
1607
1608 footprint->AutoPositionFields();
1609
1610 return footprint.release();
1611}
1612
1613
1614void PCB_IO_GEDA::parseLayer( wxArrayString& aParameters, LINE_READER* aLineReader,
1615 double aConvUnit )
1616{
1617 // Layer(N "name") ( ... objects ... )
1618 // In new format: Layer[N "name"]
1619 int paramCnt = aParameters.GetCount();
1620
1621 if( paramCnt < 4 )
1622 return;
1623
1624 long layerNum = 0;
1625 aParameters[2].ToLong( &layerNum );
1626
1627 wxString layerName;
1628
1629 if( paramCnt > 4 )
1630 layerName = aParameters[3];
1631
1632 PCB_LAYER_ID kicadLayer = mapLayer( (int) layerNum, layerName );
1633
1634 bool isCopperLayer = IsCopperLayer( kicadLayer );
1635
1636 if( isCopperLayer )
1637 {
1638 // gEDA layer numbers are 1-based (1=component, 2=solder, 3+=inner)
1639 int layerCount = static_cast<int>( layerNum );
1640
1641 if( layerCount > m_numCopperLayers )
1642 m_numCopperLayers = layerCount;
1643 }
1644
1645 wxArrayString parameters;
1646 double conv_unit = aConvUnit;
1647
1648 while( aLineReader->ReadLine() )
1649 {
1650 parameters.Clear();
1651 parseParameters( parameters, aLineReader );
1652
1653 if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
1654 continue;
1655
1656 if( parameters[0] == wxT( ")" ) )
1657 break;
1658
1659 paramCnt = parameters.GetCount();
1660
1661 if( paramCnt > 3 )
1662 {
1663 if( parameters[1] == wxT( "(" ) )
1664 conv_unit = OLD_GPCB_UNIT_CONV;
1665 else
1666 conv_unit = NEW_GPCB_UNIT_CONV;
1667 }
1668
1669 // Line[X1 Y1 X2 Y2 Thickness Clearance SFlags]
1670 if( parameters[0].CmpNoCase( wxT( "Line" ) ) == 0 )
1671 {
1672 if( paramCnt < 9 )
1673 continue;
1674
1675 int x1 = static_cast<int>( parseInt( parameters[2], conv_unit ) );
1676 int y1 = static_cast<int>( parseInt( parameters[3], conv_unit ) );
1677 int x2 = static_cast<int>( parseInt( parameters[4], conv_unit ) );
1678 int y2 = static_cast<int>( parseInt( parameters[5], conv_unit ) );
1679 int thickness = static_cast<int>( parseInt( parameters[6], conv_unit ) );
1680
1681 if( isCopperLayer )
1682 {
1683 PCB_TRACK* track = new PCB_TRACK( m_board );
1684 track->SetStart( VECTOR2I( x1, y1 ) );
1685 track->SetEnd( VECTOR2I( x2, y2 ) );
1686 track->SetWidth( thickness );
1687 track->SetLayer( kicadLayer );
1689 m_board->Add( track, ADD_MODE::APPEND );
1690 }
1691 else
1692 {
1694 shape->SetStart( VECTOR2I( x1, y1 ) );
1695 shape->SetEnd( VECTOR2I( x2, y2 ) );
1696 shape->SetStroke( STROKE_PARAMS( thickness, LINE_STYLE::SOLID ) );
1697 shape->SetLayer( kicadLayer );
1698 m_board->Add( shape, ADD_MODE::APPEND );
1699 }
1700
1701 continue;
1702 }
1703
1704 // Arc[X Y Width Height Thickness Clearance StartAngle DeltaAngle SFlags]
1705 if( parameters[0].CmpNoCase( wxT( "Arc" ) ) == 0 )
1706 {
1707 if( paramCnt < 11 )
1708 continue;
1709
1710 int cx = static_cast<int>( parseInt( parameters[2], conv_unit ) );
1711 int cy = static_cast<int>( parseInt( parameters[3], conv_unit ) );
1712 int arcWidth = static_cast<int>( parseInt( parameters[4], conv_unit ) );
1713 int arcHeight = static_cast<int>( parseInt( parameters[5], conv_unit ) );
1714 int thickness = static_cast<int>( parseInt( parameters[6], conv_unit ) );
1715 int radius = ( arcWidth + arcHeight ) / 2;
1716
1717 VECTOR2I centre( cx, cy );
1718
1719 EDA_ANGLE start_angle( static_cast<int>( parseInt( parameters[8], -10.0 ) ),
1721 start_angle += ANGLE_180;
1722
1723 EDA_ANGLE sweep_angle( static_cast<int>( parseInt( parameters[9], -10.0 ) ),
1725
1726 if( isCopperLayer )
1727 {
1728 PCB_ARC* arc = new PCB_ARC( m_board );
1729 arc->SetLayer( kicadLayer );
1730 arc->SetWidth( thickness );
1732
1733 VECTOR2I arcStart( radius, 0 );
1734 RotatePoint( arcStart, -start_angle );
1735 arc->SetStart( arcStart + centre );
1736
1737 VECTOR2I arcMid( radius, 0 );
1738 RotatePoint( arcMid, -start_angle - sweep_angle / 2 );
1739 arc->SetMid( arcMid + centre );
1740
1741 VECTOR2I arcEnd( radius, 0 );
1742 RotatePoint( arcEnd, -start_angle - sweep_angle );
1743 arc->SetEnd( arcEnd + centre );
1744
1745 m_board->Add( arc, ADD_MODE::APPEND );
1746 }
1747 else
1748 {
1749 PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::ARC );
1750 shape->SetLayer( kicadLayer );
1751
1752 if( sweep_angle == -ANGLE_360 )
1753 {
1754 shape->SetShape( SHAPE_T::CIRCLE );
1755 shape->SetCenter( centre );
1756 shape->SetEnd( centre + VECTOR2I( radius, 0 ) );
1757 }
1758 else
1759 {
1760 VECTOR2I arcStart( radius, 0 );
1761 RotatePoint( arcStart, -start_angle );
1762 shape->SetCenter( centre );
1763 shape->SetStart( arcStart + centre );
1764 shape->SetArcAngleAndEnd( sweep_angle, true );
1765 }
1766
1767 shape->SetStroke( STROKE_PARAMS( thickness, LINE_STYLE::SOLID ) );
1768 m_board->Add( shape, ADD_MODE::APPEND );
1769 }
1770
1771 continue;
1772 }
1773
1774 // Polygon(SFlags) ( [X Y] [X Y] ... )
1775 if( parameters[0].CmpNoCase( wxT( "Polygon" ) ) == 0 )
1776 {
1777 ZONE* zone = new ZONE( m_board );
1778 zone->SetLayer( kicadLayer );
1780 zone->SetLocalClearance( 0 );
1781 zone->SetAssignedPriority( 0 );
1782
1783 const int outlineIdx = -1;
1784 bool parsingPoints = false;
1785
1786 while( aLineReader->ReadLine() )
1787 {
1788 wxArrayString polyParams;
1789 parseParameters( polyParams, aLineReader );
1790
1791 if( polyParams.IsEmpty() )
1792 continue;
1793
1794 if( polyParams[0] == wxT( ")" ) )
1795 break;
1796
1797 if( polyParams[0] == wxT( "(" ) )
1798 {
1799 parsingPoints = true;
1800 continue;
1801 }
1802
1803 if( !parsingPoints )
1804 continue;
1805
1806 // Parse coordinate pairs [X Y]
1807 for( size_t i = 0; i < polyParams.GetCount(); i++ )
1808 {
1809 if( polyParams[i] == wxT( "[" ) && i + 2 < polyParams.GetCount() )
1810 {
1811 int px = static_cast<int>( parseInt( polyParams[i + 1], conv_unit ) );
1812 int py = static_cast<int>( parseInt( polyParams[i + 2], conv_unit ) );
1813 zone->AppendCorner( VECTOR2I( px, py ), outlineIdx );
1814 i += 3; // skip past X, Y, ]
1815 }
1816 }
1817 }
1818
1819 if( zone->GetNumCorners() >= 3 )
1820 {
1821 zone->SetIsFilled( false );
1822 m_board->Add( zone, ADD_MODE::APPEND );
1823 }
1824 else
1825 {
1826 delete zone;
1827 }
1828
1829 continue;
1830 }
1831
1832 // Text[X Y Direction Scale "String" SFlags]
1833 if( parameters[0].CmpNoCase( wxT( "Text" ) ) == 0 )
1834 {
1835 if( paramCnt < 8 )
1836 continue;
1837
1838 PCB_TEXT* text = new PCB_TEXT( m_board );
1839 text->SetLayer( kicadLayer );
1840
1841 int tx = static_cast<int>( parseInt( parameters[2], conv_unit ) );
1842 int ty = static_cast<int>( parseInt( parameters[3], conv_unit ) );
1843 text->SetPosition( VECTOR2I( tx, ty ) );
1844
1845 long direction = 0;
1846 parameters[4].ToLong( &direction );
1847
1848 EDA_ANGLE textAngle( static_cast<double>( direction ) * 90.0, DEGREES_T );
1849 text->SetTextAngle( textAngle );
1850
1851 long scale = 100;
1852 parameters[5].ToLong( &scale );
1853
1854 int textSize = KiROUND( TEXT_DEFAULT_SIZE * static_cast<double>( scale ) / 100.0 );
1855 text->SetTextSize( VECTOR2I( textSize, textSize ) );
1856
1857 text->SetText( parameters[6] );
1858 m_board->Add( text, ADD_MODE::APPEND );
1859 continue;
1860 }
1861 }
1862}
1863
1864
1866{
1867 // NetList() (
1868 // Net("netname" "style") (
1869 // Connect("refdes-pinnumber")
1870 // )
1871 // )
1872
1873 // Build a lookup map for fast refdes -> footprint resolution
1874 std::map<wxString, FOOTPRINT*> fpByRef;
1875
1876 for( FOOTPRINT* fp : m_board->Footprints() )
1877 fpByRef[fp->GetReference()] = fp;
1878
1879 wxArrayString parameters;
1880
1881 while( aLineReader->ReadLine() )
1882 {
1883 parameters.Clear();
1884 parseParameters( parameters, aLineReader );
1885
1886 if( parameters.IsEmpty() )
1887 continue;
1888
1889 if( parameters[0] == wxT( ")" ) )
1890 break;
1891
1892 if( parameters[0] == wxT( "(" ) )
1893 continue;
1894
1895 // Net("netname" "style") (
1896 if( parameters[0].CmpNoCase( wxT( "Net" ) ) == 0 )
1897 {
1898 wxString netName;
1899
1900 if( parameters.GetCount() > 3 )
1901 netName = parameters[2];
1902
1903 // Create or find the net
1904 NETINFO_ITEM* netInfo = nullptr;
1905 auto it = m_netMap.find( netName );
1906
1907 if( it != m_netMap.end() )
1908 {
1909 netInfo = it->second;
1910 }
1911 else
1912 {
1913 netInfo = new NETINFO_ITEM( m_board, netName );
1914 m_board->Add( netInfo );
1915 m_netMap[netName] = netInfo;
1916 }
1917
1918 // Parse Connect entries within this Net
1919 while( aLineReader->ReadLine() )
1920 {
1921 wxArrayString netParams;
1922 parseParameters( netParams, aLineReader );
1923
1924 if( netParams.IsEmpty() )
1925 continue;
1926
1927 if( netParams[0] == wxT( ")" ) )
1928 break;
1929
1930 if( netParams[0] == wxT( "(" ) )
1931 continue;
1932
1933 // Connect("refdes-pinnumber")
1934 if( netParams[0].CmpNoCase( wxT( "Connect" ) ) == 0 && netParams.GetCount() > 3 )
1935 {
1936 wxString connectStr = netParams[2];
1937
1938 // Find the last hyphen to split refdes from pinnumber
1939 int lastDash = connectStr.Find( '-', true );
1940
1941 if( lastDash == wxNOT_FOUND )
1942 continue;
1943
1944 wxString refdes = connectStr.Left( lastDash );
1945 wxString pinNumber = connectStr.Mid( lastDash + 1 );
1946
1947 auto fpIt = fpByRef.find( refdes );
1948
1949 if( fpIt == fpByRef.end() )
1950 continue;
1951
1952 for( PAD* pad : fpIt->second->Pads() )
1953 {
1954 if( pad->GetNumber() == pinNumber )
1955 {
1956 pad->SetNet( netInfo );
1957 break;
1958 }
1959 }
1960 }
1961 }
1962 }
1963 }
1964}
1965
1966
1967BOARD* PCB_IO_GEDA::LoadBoard( const wxString& aFileName, BOARD* aAppendToMe,
1968 const std::map<std::string, UTF8>* aProperties,
1969 PROJECT* aProject )
1970{
1972
1973 init( aProperties );
1974
1975 m_board = aAppendToMe ? aAppendToMe : new BOARD();
1976
1977 if( !aAppendToMe )
1978 m_board->SetFileName( aFileName );
1979
1980 std::unique_ptr<BOARD> deleter( aAppendToMe ? nullptr : m_board );
1981
1982 for( FOOTPRINT* fp : m_cachedFootprints )
1983 delete fp;
1984
1985 m_cachedFootprints.clear();
1986 m_netMap.clear();
1988
1989 FILE_LINE_READER reader( aFileName );
1990
1991 double conv_unit = NEW_GPCB_UNIT_CONV;
1992
1993 while( reader.ReadLine() )
1994 {
1995 wxArrayString parameters;
1996 parseParameters( parameters, &reader );
1997
1998 if( parameters.IsEmpty() )
1999 continue;
2000
2001 int paramCnt = parameters.GetCount();
2002
2003 if( paramCnt > 3 )
2004 {
2005 if( parameters[1] == wxT( "(" ) )
2006 conv_unit = OLD_GPCB_UNIT_CONV;
2007 else
2008 conv_unit = NEW_GPCB_UNIT_CONV;
2009 }
2010
2011 // PCB["name" width height]
2012 if( parameters[0].CmpNoCase( wxT( "PCB" ) ) == 0 )
2013 {
2014 if( paramCnt > 4 )
2015 {
2016 int boardWidth = static_cast<int>( parseInt( parameters[3], conv_unit ) );
2017 int boardHeight = static_cast<int>( parseInt( parameters[4], conv_unit ) );
2018
2019 // Set page size from board dimensions
2020 VECTOR2I pageSize( boardWidth, boardHeight );
2021 PAGE_INFO page;
2022 page.SetWidthMils( boardWidth / pcbIUScale.IU_PER_MILS );
2023 page.SetHeightMils( boardHeight / pcbIUScale.IU_PER_MILS );
2024 m_board->SetPageSettings( page );
2025 }
2026
2027 continue;
2028 }
2029
2030 // FileVersion[YYYYMMDD]
2031 if( parameters[0].CmpNoCase( wxT( "FileVersion" ) ) == 0 )
2032 continue;
2033
2034 // Grid, Cursor, Thermal, DRC, Flags, Groups, Styles -- skip
2035 if( parameters[0].CmpNoCase( wxT( "Grid" ) ) == 0
2036 || parameters[0].CmpNoCase( wxT( "Cursor" ) ) == 0
2037 || parameters[0].CmpNoCase( wxT( "Thermal" ) ) == 0
2038 || parameters[0].CmpNoCase( wxT( "DRC" ) ) == 0
2039 || parameters[0].CmpNoCase( wxT( "Flags" ) ) == 0
2040 || parameters[0].CmpNoCase( wxT( "Groups" ) ) == 0
2041 || parameters[0].CmpNoCase( wxT( "Styles" ) ) == 0
2042 || parameters[0].CmpNoCase( wxT( "Attribute" ) ) == 0 )
2043 {
2044 continue;
2045 }
2046
2047 // Via[X Y Thickness Clearance Mask Drill "Name" SFlags]
2048 if( parameters[0].CmpNoCase( wxT( "Via" ) ) == 0 )
2049 {
2050 parseVia( parameters, conv_unit );
2051 continue;
2052 }
2053
2054 // Element[SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags] (...)
2055 if( parameters[0].CmpNoCase( wxT( "Element" ) ) == 0 )
2056 {
2057 FOOTPRINT* fp = parseElement( parameters, &reader, conv_unit );
2058
2059 if( fp )
2060 {
2061 m_board->Add( fp, ADD_MODE::APPEND );
2062
2063 // Cache a copy for GetImportedCachedLibraryFootprints
2064 FOOTPRINT* fpCopy = static_cast<FOOTPRINT*>( fp->Clone() );
2065 fpCopy->SetParent( nullptr );
2066 m_cachedFootprints.push_back( fpCopy );
2067 }
2068
2069 continue;
2070 }
2071
2072 // Layer(N "name") ( ... )
2073 if( parameters[0].CmpNoCase( wxT( "Layer" ) ) == 0 )
2074 {
2075 parseLayer( parameters, &reader, conv_unit );
2076 continue;
2077 }
2078
2079 // Rat[X1 Y1 Group1 X2 Y2 Group2 SFlags] -- skip rats nest
2080 if( parameters[0].CmpNoCase( wxT( "Rat" ) ) == 0 )
2081 continue;
2082
2083 // NetList() ( ... )
2084 if( parameters[0].CmpNoCase( wxT( "NetList" ) ) == 0 )
2085 {
2086 parseNetList( &reader );
2087 continue;
2088 }
2089 }
2090
2091 // Set copper layer count
2092 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
2093 m_board->SetCopperLayerCount( std::max( 2, m_numCopperLayers ) );
2094
2095 LSET enabledLayers = bds.GetEnabledLayers();
2096 enabledLayers.set( F_Cu );
2097 enabledLayers.set( B_Cu );
2098 enabledLayers.set( F_SilkS );
2099 enabledLayers.set( B_SilkS );
2100 enabledLayers.set( F_Mask );
2101 enabledLayers.set( B_Mask );
2102 enabledLayers.set( Edge_Cuts );
2103 bds.SetEnabledLayers( enabledLayers );
2104
2105 m_board->m_LegacyDesignSettingsLoaded = true;
2106 m_board->m_LegacyNetclassesLoaded = true;
2107
2108 deleter.release();
2109 return m_board;
2110}
2111
2112
2114{
2115 std::vector<FOOTPRINT*> retval;
2116
2117 for( FOOTPRINT* fp : m_cachedFootprints )
2118 retval.push_back( static_cast<FOOTPRINT*>( fp->Clone() ) );
2119
2120 return retval;
2121}
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
BASE_SET & set(size_t pos)
Definition base_set.h:116
virtual void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Container for design settings for a BOARD object.
void SetEnabledLayers(const LSET &aMask)
Change the bit-mask of enabled layers to aMask.
const LSET & GetEnabledLayers() const
Return a bit-mask of all the layers that are enabled.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:93
void SetCenter(const VECTOR2I &aCenter)
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:198
void SetShape(SHAPE_T aShape)
Definition eda_shape.h:188
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:240
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
A LINE_READER that reads from an open file.
Definition richio.h:158
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition richio.cpp:212
RAII class to set and restore the fontconfig reporter.
Definition reporter.h:336
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:430
EDA_ITEM * Clone() const override
Invoke a function on all children.
BOARD_ITEM * Duplicate(bool addToParentGroup, BOARD_COMMIT *aCommit=nullptr) const override
Create a copy of this BOARD_ITEM.
helper class for creating a footprint library cache.
std::unique_ptr< FOOTPRINT > m_footprint
WX_FILENAME GetFileName() const
std::unique_ptr< FOOTPRINT > & GetFootprint()
WX_FILENAME m_filename
The full file name and path of the footprint to cache.
GPCB_FPL_CACHE_ENTRY(FOOTPRINT *aFootprint, const WX_FILENAME &aFileName)
FOOTPRINT * parseFOOTPRINT(LINE_READER *aLineReader)
void Remove(const wxString &aFootprintName)
boost::ptr_map< std::string, GPCB_FPL_CACHE_ENTRY > & GetFootprints()
GPCB_FPL_CACHE(PCB_IO_GEDA *aOwner, const wxString &aLibraryPath)
bool IsModified()
Return true if the cache is not up-to-date.
static long long GetTimestamp(const wxString &aLibPath)
Generate a timestamp representing all source files in the cache (including the parent directory).
PCB_IO_GEDA * m_owner
Plugin object that owns the cache.
boost::ptr_map< std::string, GPCB_FPL_CACHE_ENTRY > m_footprints
Map of footprint filename to cache entries.
void parseParameters(wxArrayString &aParameterList, LINE_READER *aLineReader)
Extract parameters and tokens from aLineReader and adds them to aParameterList.
long long m_cache_timestamp
A hash of the timestamps for all the footprint files.
bool IsWritable() const
wxFileName m_lib_path
The path of the library.
bool m_cache_dirty
Stored separately because it's expensive to check m_cache_timestamp against all the files.
void Load()
Save not implemented for the Geda PCB footprint library format.
bool testFlags(const wxString &aFlag, long aMask, const wxChar *aName)
Test aFlag for aMask or aName.
wxString GetPath() const
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()
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition richio.h:66
virtual char * ReadLine()=0
Read a line of text into the buffer and increments the line number counter.
virtual const wxString & GetSource() const
Returns the name of the source of the lines in an abstract sense.
Definition richio.h:94
virtual unsigned LineNumber() const
Return the line number of the last line read from this LINE_READER.
Definition richio.h:120
char * Line() const
Return a pointer to the last line that was read in.
Definition richio.h:102
static LOAD_INFO_REPORTER & GetInstance()
Definition reporter.cpp:249
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
Handle the data for a net.
Definition netinfo.h:50
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition netinfo.h:260
static NETINFO_ITEM * OrphanedItem()
NETINFO_ITEM meaning that there was no net assigned for an item, as there was no board storing net li...
Definition netinfo.h:268
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
Definition pad.h:65
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:79
void SetHeightMils(double aHeightInMils)
void SetWidthMils(double aWidthInMils)
void SetMid(const VECTOR2I &aMid)
Definition pcb_track.h:289
A #PLUGIN derivation for saving and loading Geda PCB files.
Definition pcb_io_geda.h:63
void FootprintDelete(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Delete aFootprintName from the library at aLibraryPath.
void parseVia(wxArrayString &aParameters, double aConvUnit)
void parseNetList(LINE_READER *aLineReader)
bool IsLibraryWritable(const wxString &aLibraryPath) override
Return true if the library at aLibraryPath is writable.
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.
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
std::map< wxString, NETINFO_ITEM * > m_netMap
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,...
bool CanReadBoard(const wxString &aFileName) const override
Checks if this PCB_IO can read the specified board file.
~PCB_IO_GEDA() override
int m_numCopperLayers
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...
std::vector< FOOTPRINT * > GetImportedCachedLibraryFootprints() override
Return a container with the cached library footprints generated in the last call to Load.
void init(const std::map< std::string, UTF8 > *aProperties)
GPCB_FPL_CACHE * m_cache
Footprint library cache.
bool testFlags(const wxString &aFlag, long aMask, const wxChar *aName)
FOOTPRINT * parseElement(wxArrayString &aParameters, LINE_READER *aLineReader, double aConvUnit)
void validateCache(const wxString &aLibraryPath, bool checkModified=true)
void parseParameters(wxArrayString &aParameterList, LINE_READER *aLineReader)
void parseLayer(wxArrayString &aParameters, LINE_READER *aLineReader, double aConvUnit)
std::vector< FOOTPRINT * > m_cachedFootprints
LINE_READER * m_reader
no ownership here.
friend class GPCB_FPL_CACHE
PCB_LAYER_ID mapLayer(int aGedaLayer, const wxString &aLayerName) const
FOOTPRINT * ImportFootprint(const wxString &aFootprintPath, wxString &aFootprintNameOut, const std::map< std::string, UTF8 > *aProperties) override
Load a single footprint from aFootprintPath and put its name in aFootprintNameOut.
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 ...
const FOOTPRINT * getFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties, bool checkModified)
BOARD * m_board
The board BOARD being worked on, no ownership here.
Definition pcb_io.h:353
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:346
const std::map< std::string, UTF8 > * m_props
Properties passed via Save() or Load(), no ownership, may be NULL.
Definition pcb_io.h:356
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void Move(const VECTOR2I &aMoveVector) override
Move this object.
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition pcb_shape.h:98
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:93
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:96
virtual void SetWidth(int aWidth)
Definition pcb_track.h:90
Container for project specific data.
Definition project.h:66
Simple container to manage line stroke parameters.
Read lines of text from another LINE_READER but only returns non-comment lines and non-blank lines wi...
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
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)
wxString GetName() const
wxString GetFullPath() const
Handle a list of polygons defining a copper zone.
Definition zone.h:74
void SetLocalClearance(std::optional< int > aClearance)
Definition zone.h:187
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:603
bool SetNetCode(int aNetCode, bool aNoAssert) override
Override that clamps the netcode to 0 when this zone is in copper-thieving fill mode.
Definition zone.cpp:585
void SetIsFilled(bool isFilled)
Definition zone.h:311
bool AppendCorner(VECTOR2I aPosition, int aHoleIdx, bool aAllowDuplication=false)
Add a new corner to the zone outline (to the main outline or a hole)
Definition zone.cpp:1280
void SetAssignedPriority(unsigned aPriority)
Definition zone.h:121
int GetNumCorners(void) const
Access to m_Poly parameters.
Definition zone.h:606
#define _(s)
@ TENTHS_OF_A_DEGREE_T
Definition eda_angle.h:30
@ RADIANS_T
Definition eda_angle.h:32
@ DEGREES_T
Definition eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:417
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:415
#define IGNORE_PARENT_GROUP
Definition eda_item.h:57
@ SEGMENT
Definition eda_shape.h:50
void CollectFilesLoopSafe(const wxString &aRoot, wxArrayString &aFiles, const wxString &aFileSpec, int aFlags)
Recursively collect every file under aRoot, deduplicating subdirectories by their resolved path.
Definition gestfich.cpp:821
static const std::string GedaPcbFootprintLibFileExtension
static const std::string KiCadFootprintFileExtension
const wxChar *const traceGedaPcbPlugin
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)
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:679
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ In11_Cu
Definition layer_ids.h:76
@ Edge_Cuts
Definition layer_ids.h:112
@ F_Paste
Definition layer_ids.h:104
@ In9_Cu
Definition layer_ids.h:74
@ In7_Cu
Definition layer_ids.h:72
@ 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
@ In2_Cu
Definition layer_ids.h:67
@ F_Fab
Definition layer_ids.h:119
@ In10_Cu
Definition layer_ids.h:75
@ F_SilkS
Definition layer_ids.h:100
@ In4_Cu
Definition layer_ids.h:69
@ In1_Cu
Definition layer_ids.h:66
@ B_SilkS
Definition layer_ids.h:101
@ In13_Cu
Definition layer_ids.h:78
@ In8_Cu
Definition layer_ids.h:73
@ In14_Cu
Definition layer_ids.h:79
@ In12_Cu
Definition layer_ids.h:77
@ In6_Cu
Definition layer_ids.h:71
@ In5_Cu
Definition layer_ids.h:70
@ In3_Cu
Definition layer_ids.h:68
@ F_Cu
Definition layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:83
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:29
long long TimestampDir(const wxString &aDirPath, const wxString &aFilespec)
Computes a hash of modification times and sizes for files matching a pattern.
Definition unix/io.cpp:123
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:99
@ RECTANGLE
Definition padstack.h:54
static long parseInt(const wxString &aValue, double aScalar)
#define NEW_GPCB_UNIT_CONV
#define OLD_GPCB_UNIT_CONV
#define TEXT_DEFAULT_SIZE
Geda PCB file plugin definition file.
int parseInt(LINE_READER &aReader, const char *aLine, const char **aOutput)
Parse an ASCII integer string with possible leading whitespace into an integer and updates the pointe...
const int scale
wxString From_UTF8(const char *cstring)
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
int radius
int clearance
int delta
wxString dump(const wxArrayString &aArray)
Debug helper for printing wxArrayString contents.
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
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
Definition of file extensions used in Kicad.