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