KiCad PCB EDA Suite
Loading...
Searching...
No Matches
export_gencad.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) 2016 Jean-Pierre Charras, [email protected]
5 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
6 * Copyright (C) 2012 Wayne Stambaugh <[email protected]>
7 * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
32#include <build_version.h>
33#include <board.h>
36#include <pcb_shape.h>
37#include <footprint.h>
38#include <pad.h>
39#include <pcb_track.h>
40#include <confirm.h>
41#include <core/arraydim.h>
43#include <locale_io.h>
44#include <macros.h>
45#include <hash_eda.h>
46#include <pcb_edit_frame.h>
47#include <pcbnew_settings.h>
48#include <pgm_base.h>
49#include <project/project_file.h> // LAST_PATH_TYPE
50
51#include <wx/app.h>
52
53static bool CreateHeaderInfoData( FILE* aFile, PCB_EDIT_FRAME* frame );
54static void CreateArtworksSection( FILE* aFile );
55static void CreateTracksInfoData( FILE* aFile, BOARD* aPcb );
56static void CreateBoardSection( FILE* aFile, BOARD* aPcb );
57static void CreateComponentsSection( FILE* aFile, BOARD* aPcb );
58static void CreateDevicesSection( FILE* aFile, BOARD* aPcb );
59static void CreateRoutesSection( FILE* aFile, BOARD* aPcb );
60static void CreateSignalsSection( FILE* aFile, BOARD* aPcb );
61static void CreateShapesSection( FILE* aFile, BOARD* aPcb );
62static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb );
63static void FootprintWriteShape( FILE* File, FOOTPRINT* aFootprint, const wxString& aShapeName );
64
65
66// layer names for Gencad export
67static std::string GenCADLayerName( int aCuCount, PCB_LAYER_ID aId )
68{
69 if( IsCopperLayer( aId ) )
70 {
71 if( aId == F_Cu )
72 return "TOP";
73 else if( aId == B_Cu )
74 return "BOTTOM";
75 else if( aId <= 14 )
76 return StrPrintf( "INNER%d", aCuCount - aId - 1 );
77 else
78 return StrPrintf( "LAYER%d", aId );
79 }
80
81 else
82 {
83 const char* txt;
84
85 // using a switch to clearly show mapping & catch out of bounds index.
86 switch( aId )
87 {
88 // Technicals
89 case B_Adhes: txt = "B.Adhes"; break;
90 case F_Adhes: txt = "F.Adhes"; break;
91 case B_Paste: txt = "SOLDERPASTE_BOTTOM"; break;
92 case F_Paste: txt = "SOLDERPASTE_TOP"; break;
93 case B_SilkS: txt = "SILKSCREEN_BOTTOM"; break;
94 case F_SilkS: txt = "SILKSCREEN_TOP"; break;
95 case B_Mask: txt = "SOLDERMASK_BOTTOM"; break;
96 case F_Mask: txt = "SOLDERMASK_TOP"; break;
97
98 // Users
99 case Dwgs_User: txt = "Dwgs.User"; break;
100 case Cmts_User: txt = "Cmts.User"; break;
101 case Eco1_User: txt = "Eco1.User"; break;
102 case Eco2_User: txt = "Eco2.User"; break;
103 case Edge_Cuts: txt = "Edge.Cuts"; break;
104 case Margin: txt = "Margin"; break;
105
106 // Footprint
107 case F_CrtYd: txt = "F_CrtYd"; break;
108 case B_CrtYd: txt = "B_CrtYd"; break;
109 case F_Fab: txt = "F_Fab"; break;
110 case B_Fab: txt = "B_Fab"; break;
111
112 default:
113 wxASSERT_MSG( 0, wxT( "aId UNEXPECTED" ) );
114 txt = "BAD-INDEX!"; break;
115 }
116
117 return txt;
118 }
119}
120
121
122static const PCB_LAYER_ID gc_seq[] = {
123 B_Cu,
124 In30_Cu,
125 In29_Cu,
126 In28_Cu,
127 In27_Cu,
128 In26_Cu,
129 In25_Cu,
130 In24_Cu,
131 In23_Cu,
132 In22_Cu,
133 In21_Cu,
134 In20_Cu,
135 In19_Cu,
136 In18_Cu,
137 In17_Cu,
138 In16_Cu,
139 In15_Cu,
140 In14_Cu,
141 In13_Cu,
142 In12_Cu,
143 In11_Cu,
144 In10_Cu,
145 In9_Cu,
146 In8_Cu,
147 In7_Cu,
148 In6_Cu,
149 In5_Cu,
150 In4_Cu,
151 In3_Cu,
152 In2_Cu,
153 In1_Cu,
154 F_Cu,
155};
156
157
158// flipped layer name for Gencad export (to make CAM350 imports correct)
159static std::string GenCADLayerNameFlipped( int aCuCount, PCB_LAYER_ID aId )
160{
161 if( 1<= aId && aId <= 14 )
162 return StrPrintf( "INNER%d", 14 - aId );
163
164 return GenCADLayerName( aCuCount, aId );
165}
166
167
168static wxString escapeString( const wxString& aString )
169{
170 wxString copy( aString );
171 copy.Replace( wxT( "\"" ), wxT( "\\\"" ) );
172 return copy;
173}
174
175
176static std::string fmt_mask( LSET aSet )
177{
178 return StrPrintf( "%08x", (unsigned) ( aSet & LSET::AllCuMask() ).to_ulong() );
179}
180
181
182// Export options
183static bool flipBottomPads;
184static bool uniquePins;
187
188// These are the export origin (the auxiliary axis)
190
191// Association between shape names (using shapeName index) and components
192static std::map<FOOTPRINT*, int> componentShapes;
193static std::map<int, wxString> shapeNames;
194
195
196static const wxString getShapeName( FOOTPRINT* aFootprint )
197{
198 static const wxString invalid( "invalid" );
199
200 if( individualShapes )
201 return aFootprint->GetReference();
202
203 auto itShape = componentShapes.find( aFootprint );
204 wxCHECK( itShape != componentShapes.end(), invalid );
205
206 auto itName = shapeNames.find( itShape->second );
207 wxCHECK( itName != shapeNames.end(), invalid );
208
209 return itName->second;
210}
211
212
213// GerbTool chokes on units different than INCH so this is the conversion factor
214const static double SCALE_FACTOR = 1000.0 * pcbIUScale.IU_PER_MILS;
215
216
217/* Two helper functions to calculate coordinates of footprints in gencad values
218 * (GenCAD Y axis from bottom to top)
219 */
220static double MapXTo( int aX )
221{
222 return (aX - GencadOffsetX) / SCALE_FACTOR;
223}
224
225
226static double MapYTo( int aY )
227{
228 return (GencadOffsetY - aY) / SCALE_FACTOR;
229}
230
231
232/* Driver function: processing starts here */
233void PCB_EDIT_FRAME::ExportToGenCAD( wxCommandEvent& aEvent )
234{
235 // Build default output file name
236 wxString path = GetLastPath( LAST_PATH_GENCAD );
237
238 if( path.IsEmpty() )
239 {
240 wxFileName brdFile = GetBoard()->GetFileName();
241 brdFile.SetExt( wxT( "cad" ) );
242 path = brdFile.GetFullPath();
243 }
244
245 DIALOG_GENCAD_EXPORT_OPTIONS optionsDialog( this, path );
246
247 if( optionsDialog.ShowModal() == wxID_CANCEL )
248 return;
249
250 path = optionsDialog.GetFileName();
252 FILE* file = wxFopen( path, wxT( "wt" ) );
253
254 if( !file )
255 {
256 DisplayError( this, wxString::Format( _( "Failed to create file '%s'." ),
257 optionsDialog.GetFileName() ) );
258 return;
259 }
260
261 // Get options
262 flipBottomPads = optionsDialog.GetOption( FLIP_BOTTOM_PADS );
263 uniquePins = optionsDialog.GetOption( UNIQUE_PIN_NAMES );
266
267 // Switch the locale to standard C (needed to print floating point numbers)
268 LOCALE_IO toggle;
269
270 // Update some board data, to ensure a reliable gencad export
272
273 // Save the auxiliary origin for the rest of the footprint
275 GencadOffsetX = optionsDialog.GetOption( USE_AUX_ORIGIN ) ? auxOrigin.x : 0;
276 GencadOffsetY = optionsDialog.GetOption( USE_AUX_ORIGIN ) ? auxOrigin.y : 0;
277
278 // No idea on *why* this should be needed... maybe to fix net names?
279 Compile_Ratsnest( true );
280
281 /* Temporary modification of footprints that are flipped (i.e. on bottom
282 * layer) to convert them to non flipped footprints.
283 * This is necessary to easily export shapes to GenCAD,
284 * that are given as normal orientation (non flipped, rotation = 0))
285 * these changes will be undone later
286 */
287 BOARD* pcb = GetBoard();
288
289 for( FOOTPRINT* footprint : pcb->Footprints() )
290 {
291 footprint->SetFlag( 0 );
292
293 if( footprint->GetLayer() == B_Cu )
294 {
295 footprint->Flip( footprint->GetPosition(), false );
296 footprint->SetFlag( 1 );
297 }
298 }
299
300 /* Gencad has some mandatory and some optional sections: some importer
301 * need the padstack section (which is optional) anyway. Also the
302 * order of the section *is* important */
303
304 CreateHeaderInfoData( file, this ); // Gencad header
305 CreateBoardSection( file, pcb ); // Board perimeter
306
307 CreatePadsShapesSection( file, pcb ); // Pads and padstacks
308 CreateArtworksSection( file ); // Empty but mandatory
309
310 /* Gencad splits a component info in shape, component and device.
311 * We don't do any sharing (it would be difficult since each module is
312 * customizable after placement) */
313 CreateShapesSection( file, pcb );
314 CreateComponentsSection( file, pcb );
315 CreateDevicesSection( file, pcb );
316
317 // In a similar way the netlist is split in net, track and route
318 CreateSignalsSection( file, pcb );
319 CreateTracksInfoData( file, pcb );
320 CreateRoutesSection( file, pcb );
321
322 fclose( file );
323
324 // Undo the footprints modifications (flipped footprints)
325 for( FOOTPRINT* footprint : pcb->Footprints() )
326 {
327 if( footprint->GetFlag() )
328 {
329 footprint->Flip( footprint->GetPosition(), false );
330 footprint->SetFlag( 0 );
331 }
332 }
333
334 componentShapes.clear();
335 shapeNames.clear();
336}
337
338
339// Sort vias for uniqueness
340static bool ViaSort( const PCB_VIA* aPadref, const PCB_VIA* aPadcmp )
341{
342 if( aPadref->GetWidth() != aPadcmp->GetWidth() )
343 return aPadref->GetWidth() < aPadcmp->GetWidth();
344
345 if( aPadref->GetDrillValue() != aPadcmp->GetDrillValue() )
346 return aPadref->GetDrillValue() < aPadcmp->GetDrillValue();
347
348 if( aPadref->GetLayerSet() != aPadcmp->GetLayerSet() )
349 return aPadref->GetLayerSet().FmtBin().compare( aPadcmp->GetLayerSet().FmtBin() ) < 0;
350
351 return false;
352}
353
354
355// The ARTWORKS section is empty but (officially) mandatory
356static void CreateArtworksSection( FILE* aFile )
357{
358 /* The artworks section is empty */
359 fputs( "$ARTWORKS\n", aFile );
360 fputs( "$ENDARTWORKS\n\n", aFile );
361}
362
363
364// Emit PADS and PADSTACKS. They are sorted and emitted uniquely.
365// Via name is synthesized from their attributes, pads are numbered
366static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb )
367{
368 std::vector<PAD*> padstacks;
369 std::vector<PCB_VIA*> vias;
370 std::vector<PCB_VIA*> viastacks;
371
372 padstacks.resize( 1 ); // We count pads from 1
373
374 // The master layermask (i.e. the enabled layers) for padstack generation
375 LSET master_layermask = aPcb->GetDesignSettings().GetEnabledLayers();
376 int cu_count = aPcb->GetCopperLayerCount();
377
378 fputs( "$PADS\n", aFile );
379
380 // Enumerate and sort the pads
381
382 std::vector<PAD*> pads = aPcb->GetPads();
383 std::sort( pads.begin(), pads.end(), []( const PAD* a, const PAD* b )
384 {
385 return PAD::Compare( a, b ) < 0;
386 } );
387
388
389 // The same for vias
390 for( PCB_TRACK* track : aPcb->Tracks() )
391 {
392 if( PCB_VIA* via = dyn_cast<PCB_VIA*>( track ) )
393 vias.push_back( via );
394 }
395
396 std::sort( vias.begin(), vias.end(), ViaSort );
397 vias.erase( std::unique( vias.begin(), vias.end(), []( const PCB_VIA* a, const PCB_VIA* b )
398 {
399 return ViaSort( a, b ) == false;
400 } ),
401 vias.end() );
402
403 // Emit vias pads
404 for( PCB_VIA* via : vias )
405 {
406 viastacks.push_back( via );
407 fprintf( aFile, "PAD V%d.%d.%s ROUND %g\nCIRCLE 0 0 %g\n",
408 via->GetWidth(), via->GetDrillValue(),
409 fmt_mask( via->GetLayerSet() & master_layermask ).c_str(),
410 via->GetDrillValue() / SCALE_FACTOR,
411 via->GetWidth() / (SCALE_FACTOR * 2) );
412 }
413
414 // Emit component pads
415 PAD* old_pad = nullptr;
416 int pad_name_number = 0;
417
418 for( unsigned i = 0; i<pads.size(); ++i )
419 {
420 PAD* pad = pads[i];
421 const VECTOR2I& off = pad->GetOffset();
422
423 pad->SetSubRatsnest( pad_name_number );
424
425 // @warning: This code is not 100% correct. The #PAD::Compare function does not test
426 // custom pad primitives so there may be duplicate custom pads in the export.
427 if( old_pad && 0 == PAD::Compare( old_pad, pad ) )
428 continue;
429
430 old_pad = pad;
431
432 pad_name_number++;
433 pad->SetSubRatsnest( pad_name_number );
434
435 fprintf( aFile, "PAD P%d", pad->GetSubRatsnest() );
436
437 padstacks.push_back( pad ); // Will have its own padstack later
438 int dx = pad->GetSize().x / 2;
439 int dy = pad->GetSize().y / 2;
440
441 switch( pad->GetShape() )
442 {
443 default:
444 UNIMPLEMENTED_FOR( pad->ShowPadShape() );
446
448 fprintf( aFile, " ROUND %g\n",
449 pad->GetDrillSize().x / SCALE_FACTOR );
450
451 /* Circle is center, radius */
452 fprintf( aFile, "CIRCLE %g %g %g\n",
453 off.x / SCALE_FACTOR,
454 -off.y / SCALE_FACTOR,
455 pad->GetSize().x / (SCALE_FACTOR * 2) );
456 break;
457
459 fprintf( aFile, " RECTANGULAR %g\n",
460 pad->GetDrillSize().x / SCALE_FACTOR );
461
462 // Rectangle is begin, size *not* begin, end!
463 fprintf( aFile, "RECTANGLE %g %g %g %g\n",
464 (-dx + off.x ) / SCALE_FACTOR,
465 (-dy - off.y ) / SCALE_FACTOR,
466 dx / (SCALE_FACTOR / 2), dy / (SCALE_FACTOR / 2) );
467 break;
468
470 case PAD_SHAPE::OVAL:
471 {
472 const VECTOR2I& size = pad->GetSize();
473 int radius = std::min( size.x, size.y ) / 2;
474
475 if( pad->GetShape() == PAD_SHAPE::ROUNDRECT )
476 {
477 radius = pad->GetRoundRectCornerRadius();
478 }
479
480 int lineX = size.x / 2 - radius;
481 int lineY = size.y / 2 - radius;
482
483 fprintf( aFile, " POLYGON %g\n", pad->GetDrillSize().x / SCALE_FACTOR );
484
485 // bottom left arc
486 fprintf( aFile, "ARC %g %g %g %g %g %g\n",
487 ( off.x - lineX - radius ) / SCALE_FACTOR,
488 ( -off.y - lineY ) / SCALE_FACTOR, ( off.x - lineX ) / SCALE_FACTOR,
489 ( -off.y - lineY - radius ) / SCALE_FACTOR,
490 ( off.x - lineX ) / SCALE_FACTOR, ( -off.y - lineY ) / SCALE_FACTOR );
491
492 // bottom line
493 if( lineX > 0 )
494 {
495 fprintf( aFile, "LINE %g %g %g %g\n",
496 ( off.x - lineX ) / SCALE_FACTOR,
497 ( -off.y - lineY - radius ) / SCALE_FACTOR,
498 ( off.x + lineX ) / SCALE_FACTOR,
499 ( -off.y - lineY - radius ) / SCALE_FACTOR );
500 }
501
502 // bottom right arc
503 fprintf( aFile, "ARC %g %g %g %g %g %g\n",
504 ( off.x + lineX ) / SCALE_FACTOR,
505 ( -off.y - lineY - radius ) / SCALE_FACTOR,
506 ( off.x + lineX + radius ) / SCALE_FACTOR,
507 ( -off.y - lineY ) / SCALE_FACTOR, ( off.x + lineX ) / SCALE_FACTOR,
508 ( -off.y - lineY ) / SCALE_FACTOR );
509
510 // right line
511 if( lineY > 0 )
512 {
513 fprintf( aFile, "LINE %g %g %g %g\n",
514 ( off.x + lineX + radius ) / SCALE_FACTOR,
515 ( -off.y + lineY ) / SCALE_FACTOR,
516 ( off.x + lineX + radius ) / SCALE_FACTOR,
517 ( -off.y - lineY ) / SCALE_FACTOR );
518 }
519
520 // top right arc
521 fprintf( aFile, "ARC %g %g %g %g %g %g\n",
522 ( off.x + lineX + radius ) / SCALE_FACTOR,
523 ( -off.y + lineY ) / SCALE_FACTOR, ( off.x + lineX ) / SCALE_FACTOR,
524 ( -off.y + lineY + radius ) / SCALE_FACTOR,
525 ( off.x + lineX ) / SCALE_FACTOR, ( -off.y + lineY ) / SCALE_FACTOR );
526
527 // top line
528 if( lineX > 0 )
529 {
530 fprintf( aFile, "LINE %g %g %g %g\n"
531 , ( off.x - lineX ) / SCALE_FACTOR,
532 ( -off.y + lineY + radius ) / SCALE_FACTOR,
533 ( off.x + lineX ) / SCALE_FACTOR,
534 ( -off.y + lineY + radius ) / SCALE_FACTOR );
535 }
536
537 // top left arc
538 fprintf( aFile, "ARC %g %g %g %g %g %g\n",
539 ( off.x - lineX ) / SCALE_FACTOR,
540 ( -off.y + lineY + radius ) / SCALE_FACTOR,
541 ( off.x - lineX - radius ) / SCALE_FACTOR,
542 ( -off.y + lineY ) / SCALE_FACTOR, ( off.x - lineX ) / SCALE_FACTOR,
543 ( -off.y + lineY ) / SCALE_FACTOR );
544
545 // left line
546 if( lineY > 0 )
547 {
548 fprintf( aFile, "LINE %g %g %g %g\n",
549 ( off.x - lineX - radius ) / SCALE_FACTOR,
550 ( -off.y - lineY ) / SCALE_FACTOR,
551 ( off.x - lineX - radius ) / SCALE_FACTOR,
552 ( -off.y + lineY ) / SCALE_FACTOR );
553 }
554
555 break;
556 }
557
559 {
560 fprintf( aFile, " POLYGON %g\n", pad->GetDrillSize().x / SCALE_FACTOR );
561
562 int ddx = pad->GetDelta().x / 2;
563 int ddy = pad->GetDelta().y / 2;
564
565 VECTOR2I poly[4];
566 poly[0] = VECTOR2I( -dx + ddy, dy + ddx );
567 poly[1] = VECTOR2I( dx - ddy, dy - ddx );
568 poly[2] = VECTOR2I( dx + ddy, -dy + ddx );
569 poly[3] = VECTOR2I( -dx - ddy, -dy - ddx );
570
571 for( int cur = 0; cur < 4; ++cur )
572 {
573 int next = ( cur + 1 ) % 4;
574 fprintf( aFile, "LINE %g %g %g %g\n",
575 ( off.x + poly[cur].x ) / SCALE_FACTOR,
576 ( -off.y - poly[cur].y ) / SCALE_FACTOR,
577 ( off.x + poly[next].x ) / SCALE_FACTOR,
578 ( -off.y - poly[next].y ) / SCALE_FACTOR );
579 }
580
581 break;
582 }
583
585 {
586 fprintf( aFile, " POLYGON %g\n", pad->GetDrillSize().x / SCALE_FACTOR );
587
588 SHAPE_POLY_SET outline;
589 int maxError = aPcb->GetDesignSettings().m_MaxError;
590 VECTOR2I padOffset( 0, 0 );
591
592 TransformRoundChamferedRectToPolygon( outline, padOffset, pad->GetSize(),
593 pad->GetOrientation(),
594 pad->GetRoundRectCornerRadius(),
595 pad->GetChamferRectRatio(),
596 pad->GetChamferPositions(), 0, maxError,
597 ERROR_INSIDE );
598
599 for( int jj = 0; jj < outline.OutlineCount(); ++jj )
600 {
601 const SHAPE_LINE_CHAIN& poly = outline.COutline( jj );
602 int pointCount = poly.PointCount();
603
604 for( int ii = 0; ii < pointCount; ii++ )
605 {
606 int next = ( ii + 1 ) % pointCount;
607 fprintf( aFile, "LINE %g %g %g %g\n",
608 poly.CPoint( ii ).x / SCALE_FACTOR,
609 -poly.CPoint( ii ).y / SCALE_FACTOR,
610 poly.CPoint( next ).x / SCALE_FACTOR,
611 -poly.CPoint( next ).y / SCALE_FACTOR );
612 }
613 }
614
615 break;
616 }
617
619 {
620 fprintf( aFile, " POLYGON %g\n", pad->GetDrillSize().x / SCALE_FACTOR );
621
622 SHAPE_POLY_SET outline;
623 pad->MergePrimitivesAsPolygon( &outline );
624
625 for( int jj = 0; jj < outline.OutlineCount(); ++jj )
626 {
627 const SHAPE_LINE_CHAIN& poly = outline.COutline( jj );
628 int pointCount = poly.PointCount();
629
630 for( int ii = 0; ii < pointCount; ii++ )
631 {
632 int next = ( ii + 1 ) % pointCount;
633 fprintf( aFile, "LINE %g %g %g %g\n",
634 ( off.x + poly.CPoint( ii ).x ) / SCALE_FACTOR,
635 ( -off.y - poly.CPoint( ii ).y ) / SCALE_FACTOR,
636 ( off.x + poly.CPoint( next ).x ) / SCALE_FACTOR,
637 ( -off.y - poly.CPoint( next ).y ) / SCALE_FACTOR );
638 }
639 }
640
641 break;
642 }
643 }
644 }
645
646 fputs( "\n$ENDPADS\n\n", aFile );
647
648 // Now emit the padstacks definitions, using the combined layer masks
649 fputs( "$PADSTACKS\n", aFile );
650
651 // Via padstacks
652 for( unsigned i = 0; i < viastacks.size(); i++ )
653 {
654 PCB_VIA* via = viastacks[i];
655
656 LSET mask = via->GetLayerSet() & master_layermask;
657
658 fprintf( aFile, "PADSTACK VIA%d.%d.%s %g\n",
659 via->GetWidth(), via->GetDrillValue(),
660 fmt_mask( mask ).c_str(),
661 via->GetDrillValue() / SCALE_FACTOR );
662
663 for( LSEQ seq = mask.Seq( gc_seq, arrayDim( gc_seq ) ); seq; ++seq )
664 {
665 PCB_LAYER_ID layer = *seq;
666
667 fprintf( aFile, "PAD V%d.%d.%s %s 0 0\n",
668 via->GetWidth(), via->GetDrillValue(),
669 fmt_mask( mask ).c_str(),
670 GenCADLayerName( cu_count, layer ).c_str() );
671 }
672 }
673
674 /* Component padstacks
675 * Older versions of CAM350 don't apply correctly the FLIP semantics for
676 * padstacks, i.e. doesn't swap the top and bottom layers... so I need to
677 * define the shape as MIRRORX and define a separate 'flipped' padstack...
678 * until it appears yet another non-compliant importer */
679 for( unsigned i = 1; i < padstacks.size(); i++ )
680 {
681 PAD* pad = padstacks[i];
682
683 // Straight padstack
684 fprintf( aFile, "PADSTACK PAD%u %g\n", i, pad->GetDrillSize().x / SCALE_FACTOR );
685
686 LSET pad_set = pad->GetLayerSet() & master_layermask;
687
688 // the special gc_seq
689 for( LSEQ seq = pad_set.Seq( gc_seq, arrayDim( gc_seq ) ); seq; ++seq )
690 {
691 PCB_LAYER_ID layer = *seq;
692
693 fprintf( aFile, "PAD P%u %s 0 0\n", i, GenCADLayerName( cu_count, layer ).c_str() );
694 }
695
696 // Flipped padstack
697 if( flipBottomPads )
698 {
699 fprintf( aFile, "PADSTACK PAD%uF %g\n", i, pad->GetDrillSize().x / SCALE_FACTOR );
700
701 // the normal PCB_LAYER_ID sequence is inverted from gc_seq[]
702 for( LSEQ seq = pad_set.Seq(); seq; ++seq )
703 {
704 PCB_LAYER_ID layer = *seq;
705
706 fprintf( aFile, "PAD P%u %s 0 0\n", i,
707 GenCADLayerNameFlipped( cu_count, layer ).c_str() );
708 }
709 }
710 }
711
712 fputs( "$ENDPADSTACKS\n\n", aFile );
713}
714
715
717static size_t hashFootprint( const FOOTPRINT* aFootprint )
718{
719 size_t ret = 0x11223344;
720 constexpr int flags = HASH_FLAGS::HASH_POS | HASH_FLAGS::REL_COORD
722
723 for( PCB_FIELD* i : aFootprint->Fields() )
724 ret += hash_fp_item( i, flags );
725
726 for( BOARD_ITEM* i : aFootprint->GraphicalItems() )
727 ret += hash_fp_item( i, flags );
728
729 for( PAD* i : aFootprint->Pads() )
730 ret += hash_fp_item( i, flags );
731
732 return ret;
733}
734
735
736/* Creates the footprint shape list.
737 * Since module shape is customizable after the placement we cannot share them;
738 * instead we opt for the one-module-one-shape-one-component-one-device approach
739 */
740static void CreateShapesSection( FILE* aFile, BOARD* aPcb )
741{
742 const char* layer;
743 wxString pinname;
744 const char* mirror = "0";
745 std::map<wxString, size_t> shapes;
746
747 fputs( "$SHAPES\n", aFile );
748
749 for( FOOTPRINT* footprint : aPcb->Footprints() )
750 {
751 if( !individualShapes )
752 {
753 // Check if such shape has been already generated, and if so - reuse it
754 // It is necessary to compute hash (i.e. check all children objects) as
755 // certain components instances might have been modified on the board.
756 // In such case the shape will be different despite the same LIB_ID.
757 wxString shapeName = footprint->GetFPID().Format();
758
759 auto shapeIt = shapes.find( shapeName );
760 size_t modHash = hashFootprint( footprint );
761
762 if( shapeIt != shapes.end() )
763 {
764 if( modHash != shapeIt->second )
765 {
766 // there is an entry for this footprint, but it has a modified shape,
767 // so we need to create a new entry
768 wxString newShapeName;
769 int suffix = 0;
770
771 // find an unused name or matching entry
772 do
773 {
774 newShapeName = wxString::Format( wxT( "%s_%d" ), shapeName, suffix );
775 shapeIt = shapes.find( newShapeName );
776 ++suffix;
777 }
778 while( shapeIt != shapes.end() && shapeIt->second != modHash );
779
780 shapeName = newShapeName;
781 }
782
783 if( shapeIt != shapes.end() && modHash == shapeIt->second )
784 {
785 // shape found, so reuse it
786 componentShapes[footprint] = modHash;
787 continue;
788 }
789 }
790
791 // new shape
792 componentShapes[footprint] = modHash;
793 shapeNames[modHash] = shapeName;
794 shapes[shapeName] = modHash;
795 FootprintWriteShape( aFile, footprint, shapeName );
796 }
797 else // individual shape for each component
798 {
799 FootprintWriteShape( aFile, footprint, footprint->GetReference() );
800 }
801
802 // set of already emitted pins to check for duplicates
803 std::set<wxString> pins;
804
805 for( PAD* pad : footprint->Pads() )
806 {
807 /* Padstacks are defined using the correct layers for the pads, therefore to
808 * all pads need to be marked as TOP to use the padstack information correctly.
809 */
810 layer = "TOP";
811 pinname = pad->GetNumber();
812
813 if( pinname.IsEmpty() )
814 pinname = wxT( "none" );
815
816 if( uniquePins )
817 {
818 int suffix = 0;
819 wxString origPinname( pinname );
820
821 auto it = pins.find( pinname );
822
823 while( it != pins.end() )
824 {
825 pinname = wxString::Format( wxT( "%s_%d" ), origPinname, suffix );
826 ++suffix;
827 it = pins.find( pinname );
828 }
829
830 pins.insert( pinname );
831 }
832
833 EDA_ANGLE orient = pad->GetOrientation() - footprint->GetOrientation();
834 orient.Normalize();
835
836 VECTOR2I padPos = pad->GetFPRelativePosition();
837
838 // Bottom side footprints use the flipped padstack
839 fprintf( aFile, ( flipBottomPads && footprint->GetFlag() ) ?
840 "PIN \"%s\" PAD%dF %g %g %s %g %s\n" :
841 "PIN \"%s\" PAD%d %g %g %s %g %s\n",
842 TO_UTF8( escapeString( pinname ) ), pad->GetSubRatsnest(),
843 padPos.x / SCALE_FACTOR,
844 -padPos.y / SCALE_FACTOR,
845 layer, orient.AsDegrees(), mirror );
846 }
847 }
848
849 fputs( "$ENDSHAPES\n\n", aFile );
850}
851
852
853/* Creates the section $COMPONENTS (Footprints placement)
854 * Bottom side components are difficult to handle: shapes must be mirrored or
855 * flipped, silk layers need to be handled correctly and so on. Also it seems
856 * that *no one* follows the specs...
857 */
858static void CreateComponentsSection( FILE* aFile, BOARD* aPcb )
859{
860 fputs( "$COMPONENTS\n", aFile );
861
862 int cu_count = aPcb->GetCopperLayerCount();
863
864 for( FOOTPRINT* footprint : aPcb->Footprints() )
865 {
866 const char* mirror;
867 const char* flip;
868 EDA_ANGLE fp_orient = footprint->GetOrientation();
869
870 if( footprint->GetFlag() )
871 {
872 mirror = "MIRRORX";
873 flip = "FLIP";
874 fp_orient = fp_orient.Invert().Normalize();
875 }
876 else
877 {
878 mirror = "0";
879 flip = "0";
880 }
881
882 fprintf( aFile, "\nCOMPONENT \"%s\"\n",
883 TO_UTF8( escapeString( footprint->GetReference() ) ) );
884 fprintf( aFile, "DEVICE \"DEV_%s\"\n",
885 TO_UTF8( escapeString( getShapeName( footprint ) ) ) );
886 fprintf( aFile, "PLACE %g %g\n",
887 MapXTo( footprint->GetPosition().x ),
888 MapYTo( footprint->GetPosition().y ) );
889 fprintf( aFile, "LAYER %s\n",
890 footprint->GetFlag() ? "BOTTOM" : "TOP" );
891 fprintf( aFile, "ROTATION %g\n",
892 fp_orient.AsDegrees() );
893 fprintf( aFile, "SHAPE \"%s\" %s %s\n",
894 TO_UTF8( escapeString( getShapeName( footprint ) ) ),
895 mirror, flip );
896
897 // Text on silk layer: RefDes and value (are they actually useful?)
898 for( PCB_TEXT* textItem : { &footprint->Reference(), &footprint->Value() } )
899 {
900 std::string layer = GenCADLayerName( cu_count,
901 footprint->GetFlag() ? B_SilkS : F_SilkS );
902
903 fprintf( aFile, "TEXT %g %g %g %g %s %s \"%s\"",
904 textItem->GetFPRelativePosition().x / SCALE_FACTOR,
905 -textItem->GetFPRelativePosition().y / SCALE_FACTOR,
906 textItem->GetTextWidth() / SCALE_FACTOR,
907 textItem->GetTextAngle().AsDegrees(),
908 mirror,
909 layer.c_str(),
910 TO_UTF8( escapeString( textItem->GetText() ) ) );
911
912 BOX2I textBox = textItem->GetTextBox();
913
914 fprintf( aFile, " 0 0 %g %g\n",
915 textBox.GetWidth() / SCALE_FACTOR,
916 textBox.GetHeight() / SCALE_FACTOR );
917 }
918
919 // The SHEET is a 'generic description' for referencing the component
920 fprintf( aFile, "SHEET \"RefDes: %s, Value: %s\"\n",
921 TO_UTF8( footprint->GetReference() ),
922 TO_UTF8( footprint->GetValue() ) );
923 }
924
925 fputs( "$ENDCOMPONENTS\n\n", aFile );
926}
927
928
929/* Emit the netlist (which is actually the thing for which GenCAD is used these
930 * days!); tracks are handled later */
931static void CreateSignalsSection( FILE* aFile, BOARD* aPcb )
932{
933 wxString msg;
934 NETINFO_ITEM* net;
935 int NbNoConn = 1;
936
937 fputs( "$SIGNALS\n", aFile );
938
939 for( unsigned ii = 0; ii < aPcb->GetNetCount(); ii++ )
940 {
941 net = aPcb->FindNet( ii );
942
943 if( net )
944 {
945 if( net->GetNetname() == wxEmptyString ) // dummy netlist (no connection)
946 {
947 msg.Printf( wxT( "NoConnection%d" ), NbNoConn++ );
948 }
949
950 if( net->GetNetCode() <= 0 ) // dummy netlist (no connection)
951 continue;
952
953 msg = wxT( "SIGNAL \"" ) + escapeString( net->GetNetname() ) + wxT( "\"" );
954
955 fputs( TO_UTF8( msg ), aFile );
956 fputs( "\n", aFile );
957
958 for( FOOTPRINT* footprint : aPcb->Footprints() )
959 {
960 for( PAD* pad : footprint->Pads() )
961 {
962 if( pad->GetNetCode() != net->GetNetCode() )
963 continue;
964
965 msg.Printf( wxT( "NODE \"%s\" \"%s\"" ),
966 escapeString( footprint->GetReference() ),
967 escapeString( pad->GetNumber() ) );
968
969 fputs( TO_UTF8( msg ), aFile );
970 fputs( "\n", aFile );
971 }
972 }
973 }
974 }
975
976 fputs( "$ENDSIGNALS\n\n", aFile );
977}
978
979
980// Creates the header section
981static bool CreateHeaderInfoData( FILE* aFile, PCB_EDIT_FRAME* aFrame )
982{
983 wxString msg;
984 BOARD* board = aFrame->GetBoard();
985
986 fputs( "$HEADER\n", aFile );
987 fputs( "GENCAD 1.4\n", aFile );
988
989 // Please note: GenCAD syntax requires quoted strings if they can contain spaces
990 msg.Printf( wxT( "USER \"%s %s\"\n" ),
991 Pgm().App().GetAppName(),
992 GetBuildVersion() );
993 fputs( TO_UTF8( msg ), aFile );
994
995 msg = wxT( "DRAWING \"" ) + board->GetFileName() + wxT( "\"\n" );
996 fputs( TO_UTF8( msg ), aFile );
997
998 wxString rev = ExpandTextVars( board->GetTitleBlock().GetRevision(), board->GetProject() );
999 wxString date = ExpandTextVars( board->GetTitleBlock().GetDate(), board->GetProject() );
1000 msg = wxT( "REVISION \"" ) + rev + wxT( " " ) + date + wxT( "\"\n" );
1001
1002 fputs( TO_UTF8( msg ), aFile );
1003 fputs( "UNITS INCH\n", aFile );
1004
1005 // giving 0 as the argument to Map{X,Y}To returns the scaled origin point
1006 msg.Printf( wxT( "ORIGIN %g %g\n" ),
1007 storeOriginCoords ? MapXTo( 0 ) : 0,
1008 storeOriginCoords ? MapYTo( 0 ) : 0 );
1009 fputs( TO_UTF8( msg ), aFile );
1010
1011 fputs( "INTERTRACK 0\n", aFile );
1012 fputs( "$ENDHEADER\n\n", aFile );
1013
1014 return true;
1015}
1016
1017
1018/* Creates the section ROUTES
1019 * that handles tracks, vias
1020 * TODO: add zones
1021 * section:
1022 * $ROUTE
1023 * ...
1024 * $ENROUTE
1025 * Track segments must be sorted by nets
1026 */
1027static void CreateRoutesSection( FILE* aFile, BOARD* aPcb )
1028{
1029 int vianum = 1;
1030 int old_netcode, old_width, old_layer;
1031 LSET master_layermask = aPcb->GetDesignSettings().GetEnabledLayers();
1032
1033 int cu_count = aPcb->GetCopperLayerCount();
1034
1035 TRACKS tracks( aPcb->Tracks() );
1036 std::sort( tracks.begin(), tracks.end(),
1037 []( const PCB_TRACK* a, const PCB_TRACK* b )
1038 {
1039 if( a->GetNetCode() == b->GetNetCode() )
1040 {
1041 if( a->GetWidth() == b->GetWidth() )
1042 return ( a->GetLayer() < b->GetLayer() );
1043
1044 return ( a->GetWidth() < b->GetWidth() );
1045 }
1046
1047 return ( a->GetNetCode() < b->GetNetCode() );
1048 } );
1049
1050 fputs( "$ROUTES\n", aFile );
1051
1052 old_netcode = -1; old_width = -1; old_layer = -1;
1053
1054 for( PCB_TRACK* track : tracks )
1055 {
1056 if( old_netcode != track->GetNetCode() )
1057 {
1058 old_netcode = track->GetNetCode();
1059 NETINFO_ITEM* net = track->GetNet();
1060 wxString netname;
1061
1062 if( net && (net->GetNetname() != wxEmptyString) )
1063 netname = net->GetNetname();
1064 else
1065 netname = wxT( "_noname_" );
1066
1067 fprintf( aFile, "ROUTE \"%s\"\n", TO_UTF8( escapeString( netname ) ) );
1068 }
1069
1070 if( old_width != track->GetWidth() )
1071 {
1072 old_width = track->GetWidth();
1073 fprintf( aFile, "TRACK TRACK%d\n", track->GetWidth() );
1074 }
1075
1076 if( track->Type() == PCB_TRACE_T )
1077 {
1078 if( old_layer != track->GetLayer() )
1079 {
1080 old_layer = track->GetLayer();
1081 fprintf( aFile, "LAYER %s\n",
1082 GenCADLayerName( cu_count, track->GetLayer() ).c_str() );
1083 }
1084
1085 fprintf( aFile, "LINE %g %g %g %g\n",
1086 MapXTo( track->GetStart().x ), MapYTo( track->GetStart().y ),
1087 MapXTo( track->GetEnd().x ), MapYTo( track->GetEnd().y ) );
1088 }
1089
1090 if( track->Type() == PCB_VIA_T )
1091 {
1092 const PCB_VIA* via = static_cast<const PCB_VIA*>(track);
1093
1094 LSET vset = via->GetLayerSet() & master_layermask;
1095
1096 fprintf( aFile, "VIA VIA%d.%d.%s %g %g ALL %g via%d\n",
1097 via->GetWidth(), via->GetDrillValue(),
1098 fmt_mask( vset ).c_str(),
1099 MapXTo( via->GetStart().x ), MapYTo( via->GetStart().y ),
1100 via->GetDrillValue() / SCALE_FACTOR, vianum++ );
1101 }
1102 }
1103
1104 fputs( "$ENDROUTES\n\n", aFile );
1105}
1106
1107
1108/* Creates the section $DEVICES
1109 * This is a list of footprints properties
1110 * ( Shapes are in section $SHAPE )
1111 */
1112static void CreateDevicesSection( FILE* aFile, BOARD* aPcb )
1113{
1114 std::set<wxString> emitted;
1115 fputs( "$DEVICES\n", aFile );
1116
1117 for( const auto& componentShape : componentShapes )
1118 {
1119 const wxString& shapeName = shapeNames[componentShape.second];
1120 bool newDevice;
1121 std::tie( std::ignore, newDevice ) = emitted.insert( shapeName );
1122
1123 if( !newDevice ) // do not repeat device definitions
1124 continue;
1125
1126 const FOOTPRINT* footprint = componentShape.first;
1127
1128 fprintf( aFile, "\nDEVICE \"DEV_%s\"\n", TO_UTF8( escapeString( shapeName ) ) );
1129
1130 fprintf( aFile, "PART \"%s\"\n",
1131 TO_UTF8( escapeString( footprint->GetValue() ) ) );
1132
1133 fprintf( aFile, "PACKAGE \"%s\"\n",
1134 TO_UTF8( escapeString( footprint->GetFPID().Format() ) ) );
1135 }
1136
1137 fputs( "$ENDDEVICES\n\n", aFile );
1138}
1139
1140
1141/* Creates the section $BOARD.
1142 * We output here only the board perimeter
1143 */
1144static void CreateBoardSection( FILE* aFile, BOARD* aPcb )
1145{
1146 fputs( "$BOARD\n", aFile );
1147
1148 // Extract the board edges
1149 for( BOARD_ITEM* drawing : aPcb->Drawings() )
1150 {
1151 if( drawing->Type() == PCB_SHAPE_T )
1152 {
1153 PCB_SHAPE* drawseg = static_cast<PCB_SHAPE*>( drawing );
1154
1155 if( drawseg->GetLayer() == Edge_Cuts )
1156 {
1157 // XXX GenCAD supports arc boundaries but I've seen nothing that reads them
1158 fprintf( aFile, "LINE %g %g %g %g\n",
1159 MapXTo( drawseg->GetStart().x ), MapYTo( drawseg->GetStart().y ),
1160 MapXTo( drawseg->GetEnd().x ), MapYTo( drawseg->GetEnd().y ) );
1161 }
1162 }
1163 }
1164
1165 fputs( "$ENDBOARD\n\n", aFile );
1166}
1167
1168
1169/* Creates the section "$TRACKS"
1170 * This sections give the list of widths (tools) used in tracks and vias
1171 * format:
1172 * $TRACK
1173 * TRACK <name> <width>
1174 * $ENDTRACK
1175 *
1176 * Each tool name is build like this: "TRACK" + track width.
1177 * For instance for a width = 120 : name = "TRACK120".
1178 */
1179static void CreateTracksInfoData( FILE* aFile, BOARD* aPcb )
1180{
1181 // Find thickness used for traces
1182
1183 std::set<int> trackinfo;
1184
1185 for( PCB_TRACK* track : aPcb->Tracks() )
1186 trackinfo.insert( track->GetWidth() );
1187
1188 // Write data
1189 fputs( "$TRACKS\n", aFile );
1190
1191 for( int size : trackinfo )
1192 fprintf( aFile, "TRACK TRACK%d %g\n", size, size / SCALE_FACTOR );
1193
1194 fputs( "$ENDTRACKS\n\n", aFile );
1195}
1196
1197
1198/* Creates the shape of a footprint (section SHAPE)
1199 * The shape is always given "normal" (Orient 0, not mirrored)
1200 * It's almost guaranteed that the silk layer will be imported wrong but
1201 * the shape also contains the pads!
1202 */
1203static void FootprintWriteShape( FILE* aFile, FOOTPRINT* aFootprint, const wxString& aShapeName )
1204{
1205 /* creates header: */
1206 fprintf( aFile, "\nSHAPE \"%s\"\n", TO_UTF8( escapeString( aShapeName ) ) );
1207
1208 if( aFootprint->GetAttributes() & FP_THROUGH_HOLE )
1209 fprintf( aFile, "INSERT TH\n" );
1210 else
1211 fprintf( aFile, "INSERT SMD\n" );
1212
1213 // Silk outline; wildly interpreted by various importers:
1214 // CAM350 read it right but only closed shapes
1215 // ProntoPlace double-flip it (at least the pads are correct)
1216 // GerberTool usually get it right...
1217 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
1218 {
1219 if( item->Type() == PCB_SHAPE_T
1220 && ( item->GetLayer() == F_SilkS || item->GetLayer() == B_SilkS ) )
1221 {
1222 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
1223 VECTOR2I start = shape->GetStart() - aFootprint->GetPosition();
1224 VECTOR2I end = shape->GetEnd() - aFootprint->GetPosition();
1225 VECTOR2I center = shape->GetCenter() - aFootprint->GetPosition();
1226
1227 RotatePoint( start, -aFootprint->GetOrientation() );
1228 RotatePoint( end, -aFootprint->GetOrientation() );
1229 RotatePoint( center, -aFootprint->GetOrientation() );
1230
1231 switch( shape->GetShape() )
1232 {
1233 case SHAPE_T::SEGMENT:
1234 fprintf( aFile, "LINE %g %g %g %g\n",
1235 start.x / SCALE_FACTOR,
1236 -start.y / SCALE_FACTOR,
1237 end.x / SCALE_FACTOR,
1238 -end.y / SCALE_FACTOR );
1239 break;
1240
1241 case SHAPE_T::RECTANGLE:
1242 fprintf( aFile, "LINE %g %g %g %g\n",
1243 start.x / SCALE_FACTOR,
1244 -start.y / SCALE_FACTOR,
1245 end.x / SCALE_FACTOR,
1246 -end.y / SCALE_FACTOR );
1247 fprintf( aFile, "LINE %g %g %g %g\n",
1248 end.x / SCALE_FACTOR,
1249 -start.y / SCALE_FACTOR,
1250 end.x / SCALE_FACTOR,
1251 -end.y / SCALE_FACTOR );
1252 fprintf( aFile, "LINE %g %g %g %g\n",
1253 end.x / SCALE_FACTOR,
1254 -end.y / SCALE_FACTOR,
1255 start.x / SCALE_FACTOR,
1256 -end.y / SCALE_FACTOR );
1257 fprintf( aFile, "LINE %g %g %g %g\n",
1258 start.x / SCALE_FACTOR,
1259 -end.y / SCALE_FACTOR,
1260 start.x / SCALE_FACTOR,
1261 -start.y / SCALE_FACTOR );
1262 break;
1263
1264 case SHAPE_T::CIRCLE:
1265 {
1266 int radius = KiROUND( GetLineLength( end, start ) );
1267
1268 fprintf( aFile, "CIRCLE %g %g %g\n",
1269 start.x / SCALE_FACTOR,
1270 -start.y / SCALE_FACTOR,
1271 radius / SCALE_FACTOR );
1272 break;
1273 }
1274
1275 case SHAPE_T::ARC:
1276 if( shape->GetArcAngle() > ANGLE_0 )
1277 std::swap( start, end );
1278
1279 fprintf( aFile, "ARC %g %g %g %g %g %g\n",
1280 start.x / SCALE_FACTOR,
1281 -start.y / SCALE_FACTOR,
1282 end.x / SCALE_FACTOR,
1283 -end.y / SCALE_FACTOR,
1284 center.x / SCALE_FACTOR,
1285 -center.y / SCALE_FACTOR );
1286 break;
1287
1288 case SHAPE_T::POLY:
1289 // Not exported (TODO)
1290 break;
1291
1292 default:
1293 wxFAIL_MSG( wxString::Format( wxT( "Shape type %d invalid." ), item->Type() ) );
1294 break;
1295 }
1296 }
1297 }
1298}
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
wxString GetBuildVersion()
Get the full KiCad version string.
LSET GetEnabledLayers() const
Return a bit-mask of all the layers that are enabled.
const VECTOR2I & GetAuxOrigin()
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:77
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:271
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1562
const std::vector< PAD * > GetPads() const
Return a reference to a list of all the pads.
Definition: board.cpp:2073
TITLE_BLOCK & GetTitleBlock()
Definition: board.h:647
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition: board.cpp:1329
FOOTPRINTS & Footprints()
Definition: board.h:313
int GetCopperLayerCount() const
Definition: board.cpp:590
TRACKS & Tracks()
Definition: board.h:310
const wxString & GetFileName() const
Definition: board.h:308
DRAWINGS & Drawings()
Definition: board.h:316
PROJECT * GetProject() const
Definition: board.h:449
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:731
unsigned GetNetCount() const
Definition: board.h:834
coord_type GetHeight() const
Definition: box2.h:189
coord_type GetWidth() const
Definition: box2.h:188
bool GetOption(GENCAD_EXPORT_OPT aOption) const
Return all export settings.
EDA_ANGLE Normalize()
Definition: eda_angle.h:249
double AsDegrees() const
Definition: eda_angle.h:149
EDA_ANGLE Invert() const
Definition: eda_angle.h:201
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:658
SHAPE_T GetShape() const
Definition: eda_shape.h:117
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:149
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:124
EDA_ANGLE GetOrientation() const
Definition: footprint.h:209
int GetAttributes() const
Definition: footprint.h:274
PADS & Pads()
Definition: footprint.h:188
const LIB_ID & GetFPID() const
Definition: footprint.h:230
PCB_FIELDS & Fields()
Definition: footprint.h:185
const wxString & GetValue() const
Definition: footprint.h:575
const wxString & GetReference() const
Definition: footprint.h:553
VECTOR2I GetPosition() const override
Definition: footprint.h:206
DRAWINGS & GraphicalItems()
Definition: footprint.h:191
UTF8 Format() const
Definition: lib_id.cpp:118
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:49
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition: layer_ids.h:513
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:552
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:773
std::string FmtBin() const
Return a binary string showing contents of this LSEQ.
Definition: lset.cpp:297
Handle the data for a net.
Definition: netinfo.h:67
const wxString & GetNetname() const
Definition: netinfo.h:125
int GetNetCode() const
Definition: netinfo.h:119
Definition: pad.h:58
static int Compare(const PAD *aPadRef, const PAD *aPadCmp)
Compare two pads and return 0 if they are equal.
Definition: pad.cpp:1135
BOARD * GetBoard() const
void Compile_Ratsnest(bool aDisplayStatus)
Create the entire board ratsnest.
Definition: ratsnest.cpp:35
The main frame for Pcbnew.
void SetLastPath(LAST_PATH_TYPE aType, const wxString &aLastPath)
Set the path of the last file successfully read.
wxString GetLastPath(LAST_PATH_TYPE aType)
Get the last path for a particular type.
void ExportToGenCAD(wxCommandEvent &event)
Create a file in GenCAD 1.4 format from the current board.
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:72
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: pcb_shape.h:67
int GetWidth() const
Definition: pcb_track.h:107
int GetDrillValue() const
Calculate the drill value for vias (m-Drill if > 0, or default drill value for the board.
Definition: pcb_track.cpp:220
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pcb_track.cpp:501
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
const wxString & GetRevision() const
Definition: title_block.h:86
const wxString & GetDate() const
Definition: title_block.h:76
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject)
Definition: common.cpp:58
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:280
This file is part of the common library.
void TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aInflate, int aError, ERROR_LOC aErrorLoc)
Convert a rectangle with rounded corners and/or chamfered corners to a polygon.
#define _(s)
static constexpr EDA_ANGLE & ANGLE_0
Definition: eda_angle.h:437
@ ARC
use RECTANGLE instead of RECT to avoid collision in a Windows header
static std::map< int, wxString > shapeNames
static const wxString getShapeName(FOOTPRINT *aFootprint)
static bool ViaSort(const PCB_VIA *aPadref, const PCB_VIA *aPadcmp)
static std::string GenCADLayerName(int aCuCount, PCB_LAYER_ID aId)
static void CreateTracksInfoData(FILE *aFile, BOARD *aPcb)
static const PCB_LAYER_ID gc_seq[]
static bool uniquePins
static const double SCALE_FACTOR
static bool flipBottomPads
static size_t hashFootprint(const FOOTPRINT *aFootprint)
Compute hashes for footprints without taking into account their position, rotation or layer.
static void CreatePadsShapesSection(FILE *aFile, BOARD *aPcb)
static bool storeOriginCoords
static void CreateRoutesSection(FILE *aFile, BOARD *aPcb)
static std::string fmt_mask(LSET aSet)
static double MapXTo(int aX)
static bool CreateHeaderInfoData(FILE *aFile, PCB_EDIT_FRAME *frame)
static void FootprintWriteShape(FILE *File, FOOTPRINT *aFootprint, const wxString &aShapeName)
static int GencadOffsetX
static std::map< FOOTPRINT *, int > componentShapes
static bool individualShapes
static void CreateBoardSection(FILE *aFile, BOARD *aPcb)
static int GencadOffsetY
static void CreateShapesSection(FILE *aFile, BOARD *aPcb)
static void CreateDevicesSection(FILE *aFile, BOARD *aPcb)
static double MapYTo(int aY)
static std::string GenCADLayerNameFlipped(int aCuCount, PCB_LAYER_ID aId)
static void CreateComponentsSection(FILE *aFile, BOARD *aPcb)
static wxString escapeString(const wxString &aString)
static void CreateArtworksSection(FILE *aFile)
static void CreateSignalsSection(FILE *aFile, BOARD *aPcb)
@ FP_THROUGH_HOLE
Definition: footprint.h:72
@ ERROR_INSIDE
size_t hash_fp_item(const EDA_ITEM *aItem, int aFlags)
Calculate hash of an EDA_ITEM.
Definition: hash_eda.cpp:51
@ HASH_POS
use coordinates relative to the parent object
Definition: hash_eda.h:43
@ REL_COORD
Definition: hash_eda.h:46
@ HASH_LAYER
Definition: hash_eda.h:48
@ HASH_ROT
Definition: hash_eda.h:47
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:847
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ In22_Cu
Definition: layer_ids.h:87
@ In11_Cu
Definition: layer_ids.h:76
@ In29_Cu
Definition: layer_ids.h:94
@ In30_Cu
Definition: layer_ids.h:95
@ F_CrtYd
Definition: layer_ids.h:118
@ In17_Cu
Definition: layer_ids.h:82
@ B_Adhes
Definition: layer_ids.h:98
@ Edge_Cuts
Definition: layer_ids.h:114
@ Dwgs_User
Definition: layer_ids.h:110
@ F_Paste
Definition: layer_ids.h:102
@ In9_Cu
Definition: layer_ids.h:74
@ Cmts_User
Definition: layer_ids.h:111
@ In19_Cu
Definition: layer_ids.h:84
@ In7_Cu
Definition: layer_ids.h:72
@ In28_Cu
Definition: layer_ids.h:93
@ In26_Cu
Definition: layer_ids.h:91
@ F_Adhes
Definition: layer_ids.h:99
@ B_Mask
Definition: layer_ids.h:107
@ B_Cu
Definition: layer_ids.h:96
@ Eco1_User
Definition: layer_ids.h:112
@ F_Mask
Definition: layer_ids.h:108
@ In21_Cu
Definition: layer_ids.h:86
@ In23_Cu
Definition: layer_ids.h:88
@ B_Paste
Definition: layer_ids.h:101
@ In15_Cu
Definition: layer_ids.h:80
@ In2_Cu
Definition: layer_ids.h:67
@ F_Fab
Definition: layer_ids.h:121
@ In10_Cu
Definition: layer_ids.h:75
@ Margin
Definition: layer_ids.h:115
@ F_SilkS
Definition: layer_ids.h:105
@ In4_Cu
Definition: layer_ids.h:69
@ B_CrtYd
Definition: layer_ids.h:117
@ Eco2_User
Definition: layer_ids.h:113
@ In16_Cu
Definition: layer_ids.h:81
@ In24_Cu
Definition: layer_ids.h:89
@ In1_Cu
Definition: layer_ids.h:66
@ B_SilkS
Definition: layer_ids.h:104
@ In13_Cu
Definition: layer_ids.h:78
@ In8_Cu
Definition: layer_ids.h:73
@ In14_Cu
Definition: layer_ids.h:79
@ In12_Cu
Definition: layer_ids.h:77
@ In27_Cu
Definition: layer_ids.h:92
@ In6_Cu
Definition: layer_ids.h:71
@ In5_Cu
Definition: layer_ids.h:70
@ In3_Cu
Definition: layer_ids.h:68
@ In20_Cu
Definition: layer_ids.h:85
@ F_Cu
Definition: layer_ids.h:65
@ In18_Cu
Definition: layer_ids.h:83
@ In25_Cu
Definition: layer_ids.h:90
@ B_Fab
Definition: layer_ids.h:120
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:96
see class PGM_BASE
@ LAST_PATH_GENCAD
Definition: project_file.h:54
CITER next(CITER it)
Definition: ptree.cpp:126
int StrPrintf(std::string *result, const char *format,...)
This is like sprintf() but the output is appended to a std::string instead of to a character array.
Definition: richio.cpp:85
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:115
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:378
const double IU_PER_MILS
Definition: base_units.h:78
void vset(double *v, double x, double y, double z)
Definition: trackball.cpp:82
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Definition: trigo.cpp:183
double GetLineLength(const VECTOR2I &aPointA, const VECTOR2I &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:188
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:94
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:93
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588