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