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
514 pad->SetAttribute( PAD_ATTRIB::SMD );
515 pad->SetLayerSet( pad_front );
516
517 if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
518 pad->SetLayerSet( pad_back );
519
520 // Set the pad name:
521 // Pcbnew pad name is used for electrical connection calculations.
522 // Accordingly it should be mapped to gEDA's pin/pad number,
523 // which is used for the same purpose.
524 // gEDA also features a pin/pad "name", which is an arbitrary string
525 // and set to the pin name of the netlist on instantiation. Many gEDA
526 // bare footprints use identical strings for name and number, so this
527 // can be a bit confusing.
528 pad->SetNumber( parameters[paramCnt-3] );
529
530 int x1 = static_cast<int>( parseInt( parameters[2], conv_unit ) );
531 int x2 = static_cast<int>( parseInt( parameters[4], conv_unit ) );
532 int y1 = static_cast<int>( parseInt( parameters[3], conv_unit ) );
533 int y2 = static_cast<int>( parseInt( parameters[5], conv_unit ) );
534 int width = static_cast<int>( parseInt( parameters[6], conv_unit ) );
535 VECTOR2I delta( x2 - x1, y2 - y1 );
536 double angle = atan2( (double)delta.y, (double)delta.x );
537
538 // Get the pad clearance and the solder mask clearance.
539 if( paramCnt == 13 )
540 {
541 int clearance = static_cast<int>( parseInt( parameters[7], conv_unit ) );
542 // One of gEDA's oddities is that clearance between pad and polygon
543 // is given as the gap on both sides of the pad together, so for
544 // KiCad it has to halfed.
545 pad->SetLocalClearance( clearance / 2 );
546
547 // In GEDA, the mask value is the size of the hole in this
548 // solder mask. In Pcbnew, it is a margin, therefore the distance
549 // between the copper and the mask
550 int maskMargin = static_cast<int>( parseInt( parameters[8], conv_unit ) );
551 maskMargin = ( maskMargin - width ) / 2;
552 pad->SetLocalSolderMaskMargin( maskMargin );
553 }
554
555 // Negate angle (due to Y reversed axis)
556 EDA_ANGLE orient( -angle, RADIANS_T );
557 pad->SetOrientation( orient );
558
559 VECTOR2I padPos( ( x1 + x2 ) / 2, ( y1 + y2 ) / 2 );
560
561 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( delta.EuclideanNorm() + width, width ) );
562
563 padPos += footprint->GetPosition();
564 pad->SetPosition( padPos );
565
566 if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
567 {
568 if( pad->GetSize( PADSTACK::ALL_LAYERS ).x == pad->GetSize( PADSTACK::ALL_LAYERS ).y )
570 else
572 }
573
574 if( pad->GetSizeX() > 0 && pad->GetSizeY() > 0 )
575 {
576 footprint->Add( pad.release() );
577 }
578 else
579 {
580 m_owner->Report( wxString::Format( _( "Invalid zero-sized pad ignored in\n"
581 "file: %s" ),
582 aLineReader->GetSource() ),
584 }
585
586 continue;
587 }
588
589 // Parse a Pin with through hole with format:
590 // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
591 // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
592 // Pin (aX aY Thickness Drill "Name" "Number" NFlags)
593 // Pin (aX aY Thickness Drill "Name" NFlags)
594 // Pin (aX aY Thickness "Name" NFlags)
595 if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
596 {
597 if( paramCnt < 8 || paramCnt > 12 )
598 {
599 msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt );
600 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
601 aLineReader->LineNumber(), 0 );
602 }
603
604 PAD* pad = new PAD( footprint.get() );
605
607
608 static const LSET pad_set = LSET::AllCuMask() | LSET( { F_SilkS, F_Mask, B_Mask } );
609
610 pad->SetLayerSet( pad_set );
611
612 if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
614
615 // Set the pad name:
616 // Pcbnew pad name is used for electrical connection calculations.
617 // Accordingly it should be mapped to gEDA's pin/pad number,
618 // which is used for the same purpose.
619 pad->SetNumber( parameters[paramCnt-3] );
620
621 VECTOR2I padPos( static_cast<int>( parseInt( parameters[2], conv_unit ) ),
622 static_cast<int>( parseInt( parameters[3], conv_unit ) ) );
623
624 int padSize = static_cast<int>( parseInt( parameters[4], conv_unit ) );
625
626 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( padSize, padSize ) );
627
628 int drillSize = 0;
629
630 // Get the pad clearance, solder mask clearance, and drill size.
631 if( paramCnt == 12 )
632 {
633 int clearance = static_cast<int>( parseInt( parameters[5], conv_unit ) );
634 // One of gEDA's oddities is that clearance between pad and polygon
635 // is given as the gap on both sides of the pad together, so for
636 // KiCad it has to halfed.
637 pad->SetLocalClearance( clearance / 2 );
638
639 // In GEDA, the mask value is the size of the hole in this
640 // solder mask. In Pcbnew, it is a margin, therefore the distance
641 // between the copper and the mask
642 int maskMargin = static_cast<int>( parseInt( parameters[6], conv_unit ) );
643 maskMargin = ( maskMargin - padSize ) / 2;
644 pad->SetLocalSolderMaskMargin( maskMargin );
645
646 drillSize = static_cast<int>( parseInt( parameters[7], conv_unit ) );
647 }
648 else
649 {
650 drillSize = static_cast<int>( parseInt( parameters[5], conv_unit ) );
651 }
652
653 pad->SetDrillSize( VECTOR2I( drillSize, drillSize ) );
654
655 padPos += footprint->GetPosition();
656 pad->SetPosition( padPos );
657
658 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE
659 && pad->GetSize( PADSTACK::ALL_LAYERS ).x != pad->GetSize( PADSTACK::ALL_LAYERS ).y )
660 {
662 }
663
664 footprint->Add( pad );
665 continue;
666 }
667 }
668
669 footprint->AutoPositionFields();
670
671 return footprint.release();
672}
673
674
675void GPCB_FPL_CACHE::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
676{
677 char key;
678 wxString tmp;
679 char* line = aLineReader->Line();
680
681 // Last line already ready in main parser loop.
682 while( *line != 0 )
683 {
684 key = *line;
685 line++;
686
687 switch( key )
688 {
689 case '[':
690 case '(':
691 if( !tmp.IsEmpty() )
692 {
693 aParameterList.Add( tmp );
694 tmp.Clear();
695 }
696
697 tmp.Append( key );
698 aParameterList.Add( tmp );
699 tmp.Clear();
700
701 // Opening delimiter "(" after Element statement. Any other occurrence is part
702 // of a keyword definition.
703 if( aParameterList.GetCount() == 1 )
704 {
705 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
706 return;
707 }
708
709 break;
710
711 case ']':
712 case ')':
713 if( !tmp.IsEmpty() )
714 {
715 aParameterList.Add( tmp );
716 tmp.Clear();
717 }
718
719 tmp.Append( key );
720 aParameterList.Add( tmp );
721 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
722 return;
723
724 case '\n':
725 case '\r':
726 // Element descriptions can span multiple lines.
727 line = aLineReader->ReadLine();
729
730 case '\t':
731 case ' ':
732 if( !tmp.IsEmpty() )
733 {
734 aParameterList.Add( tmp );
735 tmp.Clear();
736 }
737
738 break;
739
740 case '"':
741 // Handle empty quotes.
742 if( *line == '"' )
743 {
744 line++;
745 tmp.Clear();
746 aParameterList.Add( wxEmptyString );
747 break;
748 }
749
750 while( *line != 0 )
751 {
752 key = *line;
753 line++;
754
755 if( key == '"' )
756 {
757 aParameterList.Add( tmp );
758 tmp.Clear();
759 break;
760 }
761 else
762 {
763 tmp.Append( key );
764 }
765 }
766
767 break;
768
769 case '#':
770 line = aLineReader->ReadLine();
771
772 if( !line )
773 return;
774
775 break;
776
777 default:
778 tmp.Append( key );
779 break;
780 }
781 }
782}
783
784
785bool GPCB_FPL_CACHE::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
786{
787 wxString number;
788
789 if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
790 {
791 long lflags;
792
793 if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
794 return true;
795 }
796 else if( aFlag.Contains( aName ) )
797 {
798 return true;
799 }
800
801 return false;
802}
803
804
805PCB_IO_GEDA::PCB_IO_GEDA() : PCB_IO( wxS( "gEDA PCB" ) ),
806 m_cache( nullptr ),
807 m_ctl( 0 ),
809{
810 m_reader = nullptr;
811 init( nullptr );
812}
813
814
815PCB_IO_GEDA::PCB_IO_GEDA( int aControlFlags ) : PCB_IO( wxS( "gEDA PCB" ) ),
816 m_cache( nullptr ),
817 m_ctl( aControlFlags ),
819{
820 m_reader = nullptr;
821 init( nullptr );
822}
823
824
826{
827 for( FOOTPRINT* fp : m_cachedFootprints )
828 delete fp;
829
830 delete m_cache;
831}
832
833
834void PCB_IO_GEDA::init( const std::map<std::string, UTF8>* aProperties )
835{
836 m_props = aProperties;
837}
838
839
840void PCB_IO_GEDA::validateCache( const wxString& aLibraryPath, bool checkModified )
841{
842 if( !m_cache || ( checkModified && m_cache->IsModified() ) )
843 {
844 // a spectacular episode in memory management:
845 delete m_cache;
846 m_cache = new GPCB_FPL_CACHE( this, aLibraryPath );
847 m_cache->Load();
848 }
849}
850
851
852FOOTPRINT* PCB_IO_GEDA::ImportFootprint( const wxString& aFootprintPath,
853 wxString& aFootprintNameOut,
854 const std::map<std::string, UTF8>* aProperties )
855{
856 wxFileName fn( aFootprintPath );
857
858 FILE_LINE_READER freader( aFootprintPath );
859 WHITESPACE_FILTER_READER reader( freader );
860
861 reader.ReadLine();
862 char* line = reader.Line();
863
864 if( !line )
865 return nullptr;
866
867 if( strncasecmp( line, "Element", strlen( "Element" ) ) != 0 )
868 return nullptr;
869
870 aFootprintNameOut = fn.GetName();
871
872 return FootprintLoad( fn.GetPath(), aFootprintNameOut );
873}
874
875
876void PCB_IO_GEDA::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
877 bool aBestEfforts, const std::map<std::string, UTF8>* aProperties )
878{
879 wxDir dir( aLibraryPath );
880 wxString errorMsg;
881
882 if( !dir.IsOpened() )
883 {
884 if( aBestEfforts )
885 return;
886 else
887 THROW_IO_ERRORF( _( "Footprint library '%s' not found." ), aLibraryPath );
888 }
889
890 init( aProperties );
891
892 try
893 {
894 validateCache( aLibraryPath );
895 }
896 catch( const IO_ERROR& ioe )
897 {
898 errorMsg = ioe.What();
899 }
900
901 // Some of the files may have been parsed correctly so we want to add the valid files to
902 // the library.
903
904 for( const auto& footprint : m_cache->GetFootprints() )
905 aFootprintNames.Add( From_UTF8( footprint.first.c_str() ) );
906
907 if( !errorMsg.IsEmpty() && !aBestEfforts )
908 THROW_IO_ERROR( errorMsg );
909}
910
911
912const FOOTPRINT* PCB_IO_GEDA::getFootprint( const wxString& aLibraryPath,
913 const wxString& aFootprintName,
914 const std::map<std::string, UTF8>* aProperties,
915 bool checkModified )
916{
917 init( aProperties );
918
919 validateCache( aLibraryPath, checkModified );
920
921 auto it = m_cache->GetFootprints().find( TO_UTF8( aFootprintName ) );
922
923 if( it == m_cache->GetFootprints().end() )
924 return nullptr;
925
926 return it->second->GetFootprint().get();
927}
928
929
930FOOTPRINT* PCB_IO_GEDA::FootprintLoad( const wxString& aLibraryPath,
931 const wxString& aFootprintName,
932 bool aKeepUUID,
933 const std::map<std::string, UTF8>* aProperties )
934{
935 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
936 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
937
938 const FOOTPRINT* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
939
940 if( footprint )
941 {
943 copy->SetParent( nullptr );
944 return copy;
945 }
946
947 return nullptr;
948}
949
950
951void PCB_IO_GEDA::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
952 const std::map<std::string, UTF8>* aProperties )
953{
954 init( aProperties );
955
956 validateCache( aLibraryPath );
957
958 if( !m_cache->IsWritable() )
959 THROW_IO_ERRORF( _( "Library '%s' is read only." ), aLibraryPath.GetData() );
960
961 m_cache->Remove( aFootprintName );
962}
963
964
965bool PCB_IO_GEDA::DeleteLibrary( const wxString& aLibraryPath, const std::map<std::string, UTF8>* aProperties )
966{
967 wxFileName fn;
968 fn.SetPath( aLibraryPath );
969
970 // Return if there is no library path to delete.
971 if( !fn.DirExists() )
972 return false;
973
974 if( !fn.IsDirWritable() )
975 THROW_IO_ERRORF( _( "Insufficient permissions to delete folder '%s'." ), aLibraryPath.GetData() );
976
977 wxDir dir( aLibraryPath );
978
979 if( dir.HasSubDirs() )
980 THROW_IO_ERRORF( _( "Library folder '%s' has unexpected sub-folders." ), aLibraryPath.GetData() );
981
982 // All the footprint files must be deleted before the directory can be deleted.
983 if( dir.HasFiles() )
984 {
985 wxFileName tmp;
986 wxArrayString files;
987
988 CollectFilesLoopSafe( aLibraryPath, files );
989
990 for( unsigned i = 0; i < files.GetCount(); i++ )
991 {
992 tmp = files[i];
993
994 if( tmp.GetExt() != FILEEXT::KiCadFootprintFileExtension )
995 {
996 THROW_IO_ERRORF( _( "Unexpected file '%s' found in library '%s'." ),
997 files[i].GetData(),
998 aLibraryPath.GetData() );
999 }
1000 }
1001
1002 for( unsigned i = 0; i < files.GetCount(); i++ )
1003 wxRemoveFile( files[i] );
1004 }
1005
1006 wxLogTrace( traceGedaPcbPlugin, wxT( "Removing footprint library '%s'" ), aLibraryPath.GetData() );
1007
1008 // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
1009 // we don't want that. we want bare metal portability with no UI here.
1010 if( !wxRmdir( aLibraryPath ) )
1011 THROW_IO_ERRORF( _( "Footprint library '%s' cannot be deleted." ), aLibraryPath.GetData() );
1012
1013 // For some reason removing a directory in Windows is not immediately updated. This delay
1014 // prevents an error when attempting to immediately recreate the same directory when over
1015 // writing an existing library.
1016#ifdef __WINDOWS__
1017 wxMilliSleep( 250L );
1018#endif
1019
1020 if( m_cache && m_cache->GetPath() == aLibraryPath )
1021 {
1022 delete m_cache;
1023 m_cache = nullptr;
1024 }
1025
1026 return true;
1027}
1028
1029
1030long long PCB_IO_GEDA::GetLibraryTimestamp( const wxString& aLibraryPath ) const
1031{
1032 return GPCB_FPL_CACHE::GetTimestamp( aLibraryPath );
1033}
1034
1035
1036bool PCB_IO_GEDA::IsLibraryWritable( const wxString& aLibraryPath )
1037{
1038 init( nullptr );
1039
1040 validateCache( aLibraryPath );
1041
1042 return m_cache->IsWritable();
1043}
1044
1045
1046// =====================================================================
1047// Board-level import
1048// =====================================================================
1049
1050
1051void PCB_IO_GEDA::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
1052{
1053 char key;
1054 wxString tmp;
1055 char* line = aLineReader->Line();
1056
1057 while( *line != 0 )
1058 {
1059 key = *line;
1060 line++;
1061
1062 switch( key )
1063 {
1064 case '[':
1065 case '(':
1066 if( !tmp.IsEmpty() )
1067 {
1068 aParameterList.Add( tmp );
1069 tmp.Clear();
1070 }
1071
1072 tmp.Append( key );
1073 aParameterList.Add( tmp );
1074 tmp.Clear();
1075
1076 if( aParameterList.GetCount() == 1 )
1077 {
1078 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
1079 return;
1080 }
1081
1082 break;
1083
1084 case ']':
1085 case ')':
1086 if( !tmp.IsEmpty() )
1087 {
1088 aParameterList.Add( tmp );
1089 tmp.Clear();
1090 }
1091
1092 tmp.Append( key );
1093 aParameterList.Add( tmp );
1094 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
1095 return;
1096
1097 case '\n':
1098 case '\r':
1099 line = aLineReader->ReadLine();
1100
1101 if( !line )
1102 return;
1103
1105
1106 case '\t':
1107 case ' ':
1108 if( !tmp.IsEmpty() )
1109 {
1110 aParameterList.Add( tmp );
1111 tmp.Clear();
1112 }
1113
1114 break;
1115
1116 case '"':
1117 if( *line == '"' )
1118 {
1119 line++;
1120 tmp.Clear();
1121 aParameterList.Add( wxEmptyString );
1122 break;
1123 }
1124
1125 while( *line != 0 )
1126 {
1127 key = *line;
1128 line++;
1129
1130 if( key == '"' )
1131 {
1132 aParameterList.Add( tmp );
1133 tmp.Clear();
1134 break;
1135 }
1136 else
1137 {
1138 tmp.Append( key );
1139 }
1140 }
1141
1142 break;
1143
1144 case '#':
1145 line = aLineReader->ReadLine();
1146
1147 if( !line )
1148 return;
1149
1150 break;
1151
1152 default:
1153 tmp.Append( key );
1154 break;
1155 }
1156 }
1157}
1158
1159
1160bool PCB_IO_GEDA::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
1161{
1162 wxString number;
1163
1164 if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
1165 {
1166 long lflags;
1167
1168 if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
1169 return true;
1170 }
1171 else if( aFlag.Contains( aName ) )
1172 {
1173 return true;
1174 }
1175
1176 return false;
1177}
1178
1179
1180bool PCB_IO_GEDA::CanReadBoard( const wxString& aFileName ) const
1181{
1182 if( !PCB_IO::CanReadBoard( aFileName ) )
1183 return false;
1184
1185 wxFileInputStream input( aFileName );
1186
1187 if( !input.IsOk() )
1188 return false;
1189
1190 wxTextInputStream text( input );
1191
1192 for( int i = 0; i < 20; i++ )
1193 {
1194 if( input.Eof() )
1195 return false;
1196
1197 wxString line = text.ReadLine();
1198
1199 if( line.Contains( wxS( "PCB[" ) ) || line.Contains( wxS( "PCB(" ) ) )
1200 return true;
1201 }
1202
1203 return false;
1204}
1205
1206
1207PCB_LAYER_ID PCB_IO_GEDA::mapLayer( int aGedaLayer, const wxString& aLayerName ) const
1208{
1209 wxString name = aLayerName.Lower();
1210
1211 if( name.Contains( wxT( "outline" ) ) || name.Contains( wxT( "route" ) ) )
1212 return Edge_Cuts;
1213
1214 if( name.Contains( wxT( "silk" ) ) )
1215 {
1216 if( name.Contains( wxT( "solder" ) ) || name.Contains( wxT( "bottom" ) ) )
1217 return B_SilkS;
1218
1219 return F_SilkS;
1220 }
1221
1222 if( name.Contains( wxT( "mask" ) ) )
1223 {
1224 if( name.Contains( wxT( "solder" ) ) || name.Contains( wxT( "bottom" ) ) )
1225 return B_Mask;
1226
1227 return F_Mask;
1228 }
1229
1230 if( name.Contains( wxT( "paste" ) ) )
1231 {
1232 if( name.Contains( wxT( "solder" ) ) || name.Contains( wxT( "bottom" ) ) )
1233 return B_Paste;
1234
1235 return F_Paste;
1236 }
1237
1238 if( name.Contains( wxT( "fab" ) ) )
1239 return F_Fab;
1240
1241 // Copper layers: gEDA uses 1-based numbering. 1 = component/top, 2 = solder/bottom.
1242 if( name.Contains( wxT( "component" ) ) || name.Contains( wxT( "top" ) )
1243 || ( aGedaLayer == 1 && !name.Contains( wxT( "solder" ) ) ) )
1244 {
1245 return F_Cu;
1246 }
1247
1248 if( name.Contains( wxT( "solder" ) ) || name.Contains( wxT( "bottom" ) )
1249 || aGedaLayer == 2 )
1250 {
1251 return B_Cu;
1252 }
1253
1254 // Inner copper layers (gEDA layer numbers 3+)
1255 if( aGedaLayer >= 3 && aGedaLayer <= 16 )
1256 {
1257 int innerIdx = aGedaLayer - 3;
1258 PCB_LAYER_ID innerLayers[] = { In1_Cu, In2_Cu, In3_Cu, In4_Cu, In5_Cu, In6_Cu,
1260 In13_Cu, In14_Cu };
1261
1262 if( innerIdx < 14 )
1263 return innerLayers[innerIdx];
1264 }
1265
1266 return F_Cu;
1267}
1268
1269
1270void PCB_IO_GEDA::parseVia( wxArrayString& aParameters, double aConvUnit )
1271{
1272 // Via[X Y Thickness Clearance Mask Drill "Name" SFlags]
1273 int paramCnt = aParameters.GetCount();
1274
1275 if( paramCnt < 10 )
1276 THROW_IO_ERRORF( _( "Via token contains %d parameters, expected at least 10." ), paramCnt );
1277
1278 PCB_VIA* via = new PCB_VIA( m_board );
1279
1280 int x = static_cast<int>( parseInt( aParameters[2], aConvUnit ) );
1281 int y = static_cast<int>( parseInt( aParameters[3], aConvUnit ) );
1282 int thickness = static_cast<int>( parseInt( aParameters[4], aConvUnit ) );
1283 int drill = static_cast<int>( parseInt( aParameters[7], aConvUnit ) );
1284
1285 via->SetPosition( VECTOR2I( x, y ) );
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
1447 pad->SetAttribute( PAD_ATTRIB::SMD );
1448 pad->SetLayerSet( pad_front );
1449
1450 if( testFlags( parameters[paramCnt - 2], 0x0080, wxT( "onsolder" ) ) )
1451 pad->SetLayerSet( pad_back );
1452
1453 pad->SetNumber( parameters[paramCnt - 3] );
1454
1455 int x1 = static_cast<int>( parseInt( parameters[2], conv_unit ) );
1456 int x2 = static_cast<int>( parseInt( parameters[4], conv_unit ) );
1457 int y1 = static_cast<int>( parseInt( parameters[3], conv_unit ) );
1458 int y2 = static_cast<int>( parseInt( parameters[5], conv_unit ) );
1459 int width = static_cast<int>( parseInt( parameters[6], conv_unit ) );
1460 VECTOR2I delta( x2 - x1, y2 - y1 );
1461 double angle = atan2( (double) delta.y, (double) delta.x );
1462
1463 if( paramCnt == 13 )
1464 {
1465 int clearance = static_cast<int>( parseInt( parameters[7], conv_unit ) );
1466 pad->SetLocalClearance( clearance / 2 );
1467
1468 int maskMargin = static_cast<int>( parseInt( parameters[8], conv_unit ) );
1469 maskMargin = ( maskMargin - width ) / 2;
1470 pad->SetLocalSolderMaskMargin( maskMargin );
1471 }
1472
1473 EDA_ANGLE orient( -angle, RADIANS_T );
1474 pad->SetOrientation( orient );
1475
1476 VECTOR2I padPos( ( x1 + x2 ) / 2, ( y1 + y2 ) / 2 );
1477
1478 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( delta.EuclideanNorm() + width, width ) );
1479
1480 padPos += footprint->GetPosition();
1481 pad->SetPosition( padPos );
1482
1483 if( !testFlags( parameters[paramCnt - 2], 0x0100, wxT( "square" ) ) )
1484 {
1485 if( pad->GetSize( PADSTACK::ALL_LAYERS ).x == pad->GetSize( PADSTACK::ALL_LAYERS ).y )
1487 else
1489 }
1490
1491 if( pad->GetSizeX() > 0 && pad->GetSizeY() > 0 )
1492 footprint->Add( pad.release() );
1493
1494 continue;
1495 }
1496
1497 // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
1498 if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
1499 {
1500 if( paramCnt < 8 || paramCnt > 12 )
1501 continue;
1502
1503 PAD* pad = new PAD( footprint.get() );
1504
1506
1507 static const LSET pad_set = LSET::AllCuMask() | LSET( { F_SilkS, F_Mask, B_Mask } );
1508
1509 pad->SetLayerSet( pad_set );
1510
1511 if( testFlags( parameters[paramCnt - 2], 0x0100, wxT( "square" ) ) )
1513
1514 pad->SetNumber( parameters[paramCnt - 3] );
1515
1516 VECTOR2I padPos( static_cast<int>( parseInt( parameters[2], conv_unit ) ),
1517 static_cast<int>( parseInt( parameters[3], conv_unit ) ) );
1518
1519 int padSize = static_cast<int>( parseInt( parameters[4], conv_unit ) );
1520 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( padSize, padSize ) );
1521
1522 int drillSize = 0;
1523
1524 if( paramCnt == 12 )
1525 {
1526 int clearance = static_cast<int>( parseInt( parameters[5], conv_unit ) );
1527 pad->SetLocalClearance( clearance / 2 );
1528
1529 int maskMargin = static_cast<int>( parseInt( parameters[6], conv_unit ) );
1530 maskMargin = ( maskMargin - padSize ) / 2;
1531 pad->SetLocalSolderMaskMargin( maskMargin );
1532
1533 drillSize = static_cast<int>( parseInt( parameters[7], conv_unit ) );
1534 }
1535 else
1536 {
1537 drillSize = static_cast<int>( parseInt( parameters[5], conv_unit ) );
1538 }
1539
1540 pad->SetDrillSize( VECTOR2I( drillSize, drillSize ) );
1541
1542 padPos += footprint->GetPosition();
1543 pad->SetPosition( padPos );
1544
1545 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE
1546 && pad->GetSize( PADSTACK::ALL_LAYERS ).x != pad->GetSize( PADSTACK::ALL_LAYERS ).y )
1547 {
1549 }
1550
1551 footprint->Add( pad );
1552 continue;
1553 }
1554 }
1555
1556 // Handle the onsolder element flag to flip bottom-side components.
1557 // In the long form, SFlags is at index 2; in the short form, NFlags is at index 2.
1558 wxString elementFlags = aParameters[2];
1559
1560 if( elementFlags.Contains( wxT( "onsolder" ) ) )
1561 footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
1562
1563 footprint->AutoPositionFields();
1564
1565 return footprint.release();
1566}
1567
1568
1569void PCB_IO_GEDA::parseLayer( wxArrayString& aParameters, LINE_READER* aLineReader, double aConvUnit )
1570{
1571 // Layer(N "name") ( ... objects ... )
1572 // In new format: Layer[N "name"]
1573 int paramCnt = aParameters.GetCount();
1574
1575 if( paramCnt < 4 )
1576 return;
1577
1578 long layerNum = 0;
1579 aParameters[2].ToLong( &layerNum );
1580
1581 wxString layerName;
1582
1583 if( paramCnt > 4 )
1584 layerName = aParameters[3];
1585
1586 PCB_LAYER_ID kicadLayer = mapLayer( (int) layerNum, layerName );
1587
1588 bool isCopperLayer = IsCopperLayer( kicadLayer );
1589
1590 if( isCopperLayer )
1591 {
1592 // gEDA layer numbers are 1-based (1=component, 2=solder, 3+=inner)
1593 int layerCount = static_cast<int>( layerNum );
1594
1595 if( layerCount > m_numCopperLayers )
1596 m_numCopperLayers = layerCount;
1597 }
1598
1599 wxArrayString parameters;
1600 double conv_unit = aConvUnit;
1601
1602 while( aLineReader->ReadLine() )
1603 {
1604 parameters.Clear();
1605 parseParameters( parameters, aLineReader );
1606
1607 if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
1608 continue;
1609
1610 if( parameters[0] == wxT( ")" ) )
1611 break;
1612
1613 paramCnt = parameters.GetCount();
1614
1615 if( paramCnt > 3 )
1616 {
1617 if( parameters[1] == wxT( "(" ) )
1618 conv_unit = OLD_GPCB_UNIT_CONV;
1619 else
1620 conv_unit = NEW_GPCB_UNIT_CONV;
1621 }
1622
1623 // Line[X1 Y1 X2 Y2 Thickness Clearance SFlags]
1624 if( parameters[0].CmpNoCase( wxT( "Line" ) ) == 0 )
1625 {
1626 if( paramCnt < 9 )
1627 continue;
1628
1629 int x1 = static_cast<int>( parseInt( parameters[2], conv_unit ) );
1630 int y1 = static_cast<int>( parseInt( parameters[3], conv_unit ) );
1631 int x2 = static_cast<int>( parseInt( parameters[4], conv_unit ) );
1632 int y2 = static_cast<int>( parseInt( parameters[5], conv_unit ) );
1633 int thickness = static_cast<int>( parseInt( parameters[6], conv_unit ) );
1634
1635 if( isCopperLayer )
1636 {
1637 PCB_TRACK* track = new PCB_TRACK( m_board );
1638 track->SetStart( VECTOR2I( x1, y1 ) );
1639 track->SetEnd( VECTOR2I( x2, y2 ) );
1640 track->SetWidth( thickness );
1641 track->SetLayer( kicadLayer );
1643 m_board->Add( track, ADD_MODE::APPEND );
1644 }
1645 else
1646 {
1648 shape->SetStart( VECTOR2I( x1, y1 ) );
1649 shape->SetEnd( VECTOR2I( x2, y2 ) );
1650 shape->SetStroke( STROKE_PARAMS( thickness, LINE_STYLE::SOLID ) );
1651 shape->SetLayer( kicadLayer );
1652 m_board->Add( shape, ADD_MODE::APPEND );
1653 }
1654
1655 continue;
1656 }
1657
1658 // Arc[X Y Width Height Thickness Clearance StartAngle DeltaAngle SFlags]
1659 if( parameters[0].CmpNoCase( wxT( "Arc" ) ) == 0 )
1660 {
1661 if( paramCnt < 11 )
1662 continue;
1663
1664 int cx = static_cast<int>( parseInt( parameters[2], conv_unit ) );
1665 int cy = static_cast<int>( parseInt( parameters[3], conv_unit ) );
1666 int arcWidth = static_cast<int>( parseInt( parameters[4], conv_unit ) );
1667 int arcHeight = static_cast<int>( parseInt( parameters[5], conv_unit ) );
1668 int thickness = static_cast<int>( parseInt( parameters[6], conv_unit ) );
1669 int radius = ( arcWidth + arcHeight ) / 2;
1670
1671 VECTOR2I centre( cx, cy );
1672
1673 EDA_ANGLE start_angle( static_cast<int>( parseInt( parameters[8], -10.0 ) ), TENTHS_OF_A_DEGREE_T );
1674 start_angle += ANGLE_180;
1675
1676 EDA_ANGLE sweep_angle( static_cast<int>( parseInt( parameters[9], -10.0 ) ), TENTHS_OF_A_DEGREE_T );
1677
1678 if( isCopperLayer )
1679 {
1680 PCB_ARC* arc = new PCB_ARC( m_board );
1681 arc->SetLayer( kicadLayer );
1682 arc->SetWidth( thickness );
1684
1685 VECTOR2I arcStart( radius, 0 );
1686 RotatePoint( arcStart, -start_angle );
1687 arc->SetStart( arcStart + centre );
1688
1689 VECTOR2I arcMid( radius, 0 );
1690 RotatePoint( arcMid, -start_angle - sweep_angle / 2 );
1691 arc->SetMid( arcMid + centre );
1692
1693 VECTOR2I arcEnd( radius, 0 );
1694 RotatePoint( arcEnd, -start_angle - sweep_angle );
1695 arc->SetEnd( arcEnd + centre );
1696
1697 m_board->Add( arc, ADD_MODE::APPEND );
1698 }
1699 else
1700 {
1701 PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::ARC );
1702 shape->SetLayer( kicadLayer );
1703
1704 if( sweep_angle == -ANGLE_360 )
1705 {
1706 shape->SetShape( SHAPE_T::CIRCLE );
1707 shape->SetCenter( centre );
1708 shape->SetEnd( centre + VECTOR2I( radius, 0 ) );
1709 }
1710 else
1711 {
1712 VECTOR2I arcStart( radius, 0 );
1713 RotatePoint( arcStart, -start_angle );
1714 shape->SetCenter( centre );
1715 shape->SetStart( arcStart + centre );
1716 shape->SetArcAngleAndEnd( sweep_angle, true );
1717 }
1718
1719 shape->SetStroke( STROKE_PARAMS( thickness, LINE_STYLE::SOLID ) );
1720 m_board->Add( shape, ADD_MODE::APPEND );
1721 }
1722
1723 continue;
1724 }
1725
1726 // Polygon(SFlags) ( [X Y] [X Y] ... )
1727 if( parameters[0].CmpNoCase( wxT( "Polygon" ) ) == 0 )
1728 {
1729 ZONE* zone = new ZONE( m_board );
1730 zone->SetLayer( kicadLayer );
1732 zone->SetLocalClearance( 0 );
1733 zone->SetAssignedPriority( 0 );
1734
1735 const int outlineIdx = -1;
1736 bool parsingPoints = false;
1737
1738 while( aLineReader->ReadLine() )
1739 {
1740 wxArrayString polyParams;
1741 parseParameters( polyParams, aLineReader );
1742
1743 if( polyParams.IsEmpty() )
1744 continue;
1745
1746 if( polyParams[0] == wxT( ")" ) )
1747 break;
1748
1749 if( polyParams[0] == wxT( "(" ) )
1750 {
1751 parsingPoints = true;
1752 continue;
1753 }
1754
1755 if( !parsingPoints )
1756 continue;
1757
1758 // Parse coordinate pairs [X Y]
1759 for( size_t i = 0; i < polyParams.GetCount(); i++ )
1760 {
1761 if( polyParams[i] == wxT( "[" ) && i + 2 < polyParams.GetCount() )
1762 {
1763 int px = static_cast<int>( parseInt( polyParams[i + 1], conv_unit ) );
1764 int py = static_cast<int>( parseInt( polyParams[i + 2], conv_unit ) );
1765 zone->AppendCorner( VECTOR2I( px, py ), outlineIdx );
1766 i += 3; // skip past X, Y, ]
1767 }
1768 }
1769 }
1770
1771 if( zone->GetNumCorners() >= 3 )
1772 {
1773 zone->SetIsFilled( false );
1774 m_board->Add( zone, ADD_MODE::APPEND );
1775 }
1776 else
1777 {
1778 delete zone;
1779 }
1780
1781 continue;
1782 }
1783
1784 // Text[X Y Direction Scale "String" SFlags]
1785 if( parameters[0].CmpNoCase( wxT( "Text" ) ) == 0 )
1786 {
1787 if( paramCnt < 8 )
1788 continue;
1789
1790 PCB_TEXT* text = new PCB_TEXT( m_board );
1791 text->SetLayer( kicadLayer );
1792
1793 int tx = static_cast<int>( parseInt( parameters[2], conv_unit ) );
1794 int ty = static_cast<int>( parseInt( parameters[3], conv_unit ) );
1795 text->SetPosition( VECTOR2I( tx, ty ) );
1796
1797 long direction = 0;
1798 parameters[4].ToLong( &direction );
1799
1800 EDA_ANGLE textAngle( static_cast<double>( direction ) * 90.0, DEGREES_T );
1801 text->SetTextAngle( textAngle );
1802
1803 long scale = 100;
1804 parameters[5].ToLong( &scale );
1805
1806 int textSize = KiROUND( TEXT_DEFAULT_SIZE * static_cast<double>( scale ) / 100.0 );
1807 text->SetTextSize( VECTOR2I( textSize, textSize ) );
1808
1809 text->SetText( parameters[6] );
1810 m_board->Add( text, ADD_MODE::APPEND );
1811 continue;
1812 }
1813 }
1814}
1815
1816
1818{
1819 // NetList() (
1820 // Net("netname" "style") (
1821 // Connect("refdes-pinnumber")
1822 // )
1823 // )
1824
1825 // Build a lookup map for fast refdes -> footprint resolution
1826 std::map<wxString, FOOTPRINT*> fpByRef;
1827
1828 for( FOOTPRINT* fp : m_board->Footprints() )
1829 fpByRef[fp->GetReference()] = fp;
1830
1831 wxArrayString parameters;
1832
1833 while( aLineReader->ReadLine() )
1834 {
1835 parameters.Clear();
1836 parseParameters( parameters, aLineReader );
1837
1838 if( parameters.IsEmpty() )
1839 continue;
1840
1841 if( parameters[0] == wxT( ")" ) )
1842 break;
1843
1844 if( parameters[0] == wxT( "(" ) )
1845 continue;
1846
1847 // Net("netname" "style") (
1848 if( parameters[0].CmpNoCase( wxT( "Net" ) ) == 0 )
1849 {
1850 wxString netName;
1851
1852 if( parameters.GetCount() > 3 )
1853 netName = parameters[2];
1854
1855 // Create or find the net
1856 NETINFO_ITEM* netInfo = nullptr;
1857 auto it = m_netMap.find( netName );
1858
1859 if( it != m_netMap.end() )
1860 {
1861 netInfo = it->second;
1862 }
1863 else
1864 {
1865 netInfo = new NETINFO_ITEM( m_board, netName );
1866 m_board->Add( netInfo );
1867 m_netMap[netName] = netInfo;
1868 }
1869
1870 // Parse Connect entries within this Net
1871 while( aLineReader->ReadLine() )
1872 {
1873 wxArrayString netParams;
1874 parseParameters( netParams, aLineReader );
1875
1876 if( netParams.IsEmpty() )
1877 continue;
1878
1879 if( netParams[0] == wxT( ")" ) )
1880 break;
1881
1882 if( netParams[0] == wxT( "(" ) )
1883 continue;
1884
1885 // Connect("refdes-pinnumber")
1886 if( netParams[0].CmpNoCase( wxT( "Connect" ) ) == 0 && netParams.GetCount() > 3 )
1887 {
1888 wxString connectStr = netParams[2];
1889
1890 // Find the last hyphen to split refdes from pinnumber
1891 int lastDash = connectStr.Find( '-', true );
1892
1893 if( lastDash == wxNOT_FOUND )
1894 continue;
1895
1896 wxString refdes = connectStr.Left( lastDash );
1897 wxString pinNumber = connectStr.Mid( lastDash + 1 );
1898
1899 auto fpIt = fpByRef.find( refdes );
1900
1901 if( fpIt == fpByRef.end() )
1902 continue;
1903
1904 for( PAD* pad : fpIt->second->Pads() )
1905 {
1906 if( pad->GetNumber() == pinNumber )
1907 {
1908 pad->SetNet( netInfo );
1909 break;
1910 }
1911 }
1912 }
1913 }
1914 }
1915 }
1916}
1917
1918
1919BOARD* PCB_IO_GEDA::LoadBoard( const wxString& aFileName, BOARD* aAppendToMe,
1920 const std::map<std::string, UTF8>* aProperties,
1921 PROJECT* aProject )
1922{
1924
1925 init( aProperties );
1926
1927 m_board = aAppendToMe ? aAppendToMe : new BOARD();
1928
1929 if( !aAppendToMe )
1930 m_board->SetFileName( aFileName );
1931
1932 std::unique_ptr<BOARD> deleter( aAppendToMe ? nullptr : m_board );
1933
1934 for( FOOTPRINT* fp : m_cachedFootprints )
1935 delete fp;
1936
1937 m_cachedFootprints.clear();
1938 m_netMap.clear();
1940
1941 FILE_LINE_READER reader( aFileName );
1942
1943 double conv_unit = NEW_GPCB_UNIT_CONV;
1944
1945 while( reader.ReadLine() )
1946 {
1947 wxArrayString parameters;
1948 parseParameters( parameters, &reader );
1949
1950 if( parameters.IsEmpty() )
1951 continue;
1952
1953 int paramCnt = parameters.GetCount();
1954
1955 if( paramCnt > 3 )
1956 {
1957 if( parameters[1] == wxT( "(" ) )
1958 conv_unit = OLD_GPCB_UNIT_CONV;
1959 else
1960 conv_unit = NEW_GPCB_UNIT_CONV;
1961 }
1962
1963 // PCB["name" width height]
1964 if( parameters[0].CmpNoCase( wxT( "PCB" ) ) == 0 )
1965 {
1966 if( paramCnt > 4 )
1967 {
1968 int boardWidth = static_cast<int>( parseInt( parameters[3], conv_unit ) );
1969 int boardHeight = static_cast<int>( parseInt( parameters[4], conv_unit ) );
1970
1971 // Set page size from board dimensions
1972 VECTOR2I pageSize( boardWidth, boardHeight );
1973 PAGE_INFO page;
1974 page.SetWidthMils( boardWidth / pcbIUScale.IU_PER_MILS );
1975 page.SetHeightMils( boardHeight / pcbIUScale.IU_PER_MILS );
1976 m_board->SetPageSettings( page );
1977 }
1978
1979 continue;
1980 }
1981
1982 // FileVersion[YYYYMMDD]
1983 if( parameters[0].CmpNoCase( wxT( "FileVersion" ) ) == 0 )
1984 continue;
1985
1986 // Grid, Cursor, Thermal, DRC, Flags, Groups, Styles -- skip
1987 if( parameters[0].CmpNoCase( wxT( "Grid" ) ) == 0
1988 || parameters[0].CmpNoCase( wxT( "Cursor" ) ) == 0
1989 || parameters[0].CmpNoCase( wxT( "Thermal" ) ) == 0
1990 || parameters[0].CmpNoCase( wxT( "DRC" ) ) == 0
1991 || parameters[0].CmpNoCase( wxT( "Flags" ) ) == 0
1992 || parameters[0].CmpNoCase( wxT( "Groups" ) ) == 0
1993 || parameters[0].CmpNoCase( wxT( "Styles" ) ) == 0
1994 || parameters[0].CmpNoCase( wxT( "Attribute" ) ) == 0 )
1995 {
1996 continue;
1997 }
1998
1999 // Via[X Y Thickness Clearance Mask Drill "Name" SFlags]
2000 if( parameters[0].CmpNoCase( wxT( "Via" ) ) == 0 )
2001 {
2002 parseVia( parameters, conv_unit );
2003 continue;
2004 }
2005
2006 // Element[SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags] (...)
2007 if( parameters[0].CmpNoCase( wxT( "Element" ) ) == 0 )
2008 {
2009 FOOTPRINT* fp = parseElement( parameters, &reader, conv_unit );
2010
2011 if( fp )
2012 {
2013 m_board->Add( fp, ADD_MODE::APPEND );
2014
2015 // Cache a copy for GetImportedCachedLibraryFootprints
2016 FOOTPRINT* fpCopy = static_cast<FOOTPRINT*>( fp->Clone() );
2017 fpCopy->SetParent( nullptr );
2018 m_cachedFootprints.push_back( fpCopy );
2019 }
2020
2021 continue;
2022 }
2023
2024 // Layer(N "name") ( ... )
2025 if( parameters[0].CmpNoCase( wxT( "Layer" ) ) == 0 )
2026 {
2027 parseLayer( parameters, &reader, conv_unit );
2028 continue;
2029 }
2030
2031 // Rat[X1 Y1 Group1 X2 Y2 Group2 SFlags] -- skip rats nest
2032 if( parameters[0].CmpNoCase( wxT( "Rat" ) ) == 0 )
2033 continue;
2034
2035 // NetList() ( ... )
2036 if( parameters[0].CmpNoCase( wxT( "NetList" ) ) == 0 )
2037 {
2038 parseNetList( &reader );
2039 continue;
2040 }
2041 }
2042
2043 // Set copper layer count
2044 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
2045 m_board->SetCopperLayerCount( std::max( 2, m_numCopperLayers ) );
2046
2047 LSET enabledLayers = bds.GetEnabledLayers();
2048 enabledLayers.set( F_Cu );
2049 enabledLayers.set( B_Cu );
2050 enabledLayers.set( F_SilkS );
2051 enabledLayers.set( B_SilkS );
2052 enabledLayers.set( F_Mask );
2053 enabledLayers.set( B_Mask );
2054 enabledLayers.set( Edge_Cuts );
2055 bds.SetEnabledLayers( enabledLayers );
2056
2057 m_board->m_LegacyDesignSettingsLoaded = true;
2058 m_board->m_LegacyNetclassesLoaded = true;
2059
2060 deleter.release();
2061 return m_board;
2062}
2063
2064
2066{
2067 std::vector<FOOTPRINT*> retval;
2068
2069 for( FOOTPRINT* fp : m_cachedFootprints )
2070 retval.push_back( static_cast<FOOTPRINT*>( fp->Clone() ) );
2071
2072 return retval;
2073}
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:373
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:204
RAII class to set and restore the fontconfig reporter.
Definition reporter.h:368
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:445
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:306
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: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
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:619
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:601
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:1410
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: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:683
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.
@ 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.