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