KiCad PCB EDA Suite
Loading...
Searching...
No Matches
specctra_export.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) 2007-2015 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
5 * Copyright (C) 2015-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25
26/* This source is a complement to specctra.cpp and implements the export to
27 specctra dsn file format. The specification for the grammar of the specctra
28 dsn file used to develop this code is given here:
29 http://tech.groups.yahoo.com/group/kicad-users/files/ then file "specctra.pdf"
30
31 Also see the comments at the top of the specctra.cpp file itself.
32*/
33
34#include <pcb_edit_frame.h>
35#include <confirm.h> // DisplayError()
36#include <gestfich.h> // EDA_FileSelector()
37#include <locale_io.h>
38#include <macros.h>
39#include <math/util.h> // for KiROUND
40
41#include <set> // std::set
42#include <map> // std::map
43
44#include <board.h>
46#include <footprint.h>
47#include <pcb_shape.h>
48#include <pcb_track.h>
49#include <pad.h>
50#include <zone.h>
51#include <base_units.h>
52#include <collectors.h>
57#include <pcbnew_settings.h>
58#include <wx/log.h>
59
60#include "specctra.h"
61
62using namespace DSN;
63
64// comment the line #define EXPORT_CUSTOM_PADS_CONVEX_HULL to export CUSTOM pads exact shapes.
65// Shapes can be non convex polygons with holes (linked to outline) that can create issues.
66// Especially Freerouter does not handle them very well:
67// - too complex shapes are not accepted, especially shapes with holes (dsn files are not loaded).
68// - and Freerouter actually uses something like a convex hull of the shape (that works poorly).
69// I am guessing non convex polygons with holes linked could create issues with any Router.
70#define EXPORT_CUSTOM_PADS_CONVEX_HULL
71
72
73bool PCB_EDIT_FRAME::ExportSpecctraFile( const wxString& aFullFilename )
74{
75 BASE_SCREEN* screen = GetScreen();
76 bool wasModified = screen->IsContentModified();
77 wxString errorText;
78 bool ok = true;
79
80 try
81 {
82 DSN::ExportBoardToSpecctraFile( GetBoard(), aFullFilename );
83 }
84 catch( const IO_ERROR& ioe )
85 {
86 ok = false;
87
88 // copy the error string to safe place, ioe is in this scope only.
89 errorText = ioe.What();
90 }
91
92 // The two calls to FOOTPRINT::Flip() in ExportBoardToSpecctraFile both set the modified flag,
93 // yet their actions cancel each other out, so it should be ok to clear the flag.
94 if( !wasModified )
95 screen->SetContentModified( false );
96
97 if( ok )
98 SetStatusText( wxString( _( "BOARD exported OK." ) ) );
99 else
100 DisplayErrorMessage( this, _( "Unable to export, please fix and try again" ), errorText );
101
102 return ok;
103}
104
105namespace DSN
106{
107
108
109void ExportBoardToSpecctraFile( BOARD* aBoard, const wxString& aFullFilename )
110{
111 SPECCTRA_DB db;
112
114
115 LOCALE_IO toggle; // Switch the locale to standard C
116
117 // Build the board outlines *before* flipping footprints
118 if( !db.BuiltBoardOutlines( aBoard ) )
119 wxLogWarning( _( "Board outline is malformed. Run DRC for a full analysis." ) );
120
121 // DSN Images (=KiCad FOOTPRINTs and PADs) must be presented from the top view. So we
122 // temporarily flip any footprints which are on the back side of the board to the front,
123 // and record this in the FOOTPRINT's flag field.
124 db.FlipFOOTPRINTs( aBoard );
125
126 try
127 {
128 aBoard->SynchronizeNetsAndNetClasses( false );
129 db.FromBOARD( aBoard );
130 db.ExportPCB( aFullFilename, true );
131 db.RevertFOOTPRINTs( aBoard );
132
133 // if an exception is thrown by FromBOARD() or ExportPCB(), then ~SPECCTRA_DB() will
134 // close the file.
135 }
136 catch( ... )
137 {
138 db.RevertFOOTPRINTs( aBoard );
139 throw;
140 }
141}
142
143
144// "specctra reported units" are what we tell the external router that our exported lengths are in
145
150static inline double scale( int kicadDist )
151{
152 // nanometers to um
153 return kicadDist / ( pcbIUScale.IU_PER_MM / 1000.0 );
154}
155
156
158static inline double IU2um( int kicadDist )
159{
160 return kicadDist * ( 1000.0 / pcbIUScale.IU_PER_MM );
161}
162
163
164static inline double mapX( int x )
165{
166 return scale( x );
167}
168
169
170static inline double mapY( int y )
171{
172 return -scale( y ); // make y negative, since it is increasing going down.
173}
174
175
182static POINT mapPt( const VECTOR2I& pt )
183{
184 POINT ret;
185
186 ret.x = mapX( pt.x );
187 ret.y = mapY( pt.y );
188 ret.FixNegativeZero();
189 return ret;
190}
191
192
193static POINT mapPt( const VECTOR2I& pt, FOOTPRINT* aFootprint )
194{
195 VECTOR2I fpRelative = pt - aFootprint->GetPosition();
196 RotatePoint( fpRelative, -aFootprint->GetOrientation() );
197 return mapPt( fpRelative );
198}
199
200
204static bool isRoundKeepout( PAD* aPad )
205{
206 if( aPad->GetShape() == PAD_SHAPE::CIRCLE )
207 {
208 if( aPad->GetDrillSize().x >= aPad->GetSize().x )
209 return true;
210
211 if( !( aPad->GetLayerSet() & LSET::AllCuMask() ).any() )
212 return true;
213 }
214
215 return false;
216}
217
218
222static PATH* makePath( const POINT& aStart, const POINT& aEnd, const std::string& aLayerName )
223{
224 PATH* path = new PATH( nullptr, T_path );
225
226 path->AppendPoint( aStart );
227 path->AppendPoint( aEnd );
228 path->SetLayerId( aLayerName.c_str() );
229 return path;
230}
231
232
234{
235 return aBoard->GetBoardPolygonOutlines( m_brd_outlines );
236}
237
238
240{
241 char name[256]; // padstack name builder
242 std::string uniqifier;
243
244 // caller must do these checks before calling here.
245 wxASSERT( !isRoundKeepout( aPad ) );
246
247 PADSTACK* padstack = new PADSTACK();
248
249 int reportedLayers = 0; // how many in reported padstack
250 const char* layerName[MAX_CU_LAYERS];
251
252 uniqifier = '[';
253
254 static const LSET all_cu = LSET::AllCuMask();
255
256 bool onAllCopperLayers = ( (aPad->GetLayerSet() & all_cu) == all_cu );
257
258 if( onAllCopperLayers )
259 uniqifier += 'A'; // A for all layers
260
261 const int copperCount = aBoard->GetCopperLayerCount();
262
263 for( int layer=0; layer<copperCount; ++layer )
264 {
265 PCB_LAYER_ID kilayer = m_pcbLayer2kicad[layer];
266
267 if( onAllCopperLayers || aPad->IsOnLayer( kilayer ) )
268 {
269 layerName[reportedLayers++] = m_layerIds[layer].c_str();
270
271 if( !onAllCopperLayers )
272 {
273 if( layer == 0 )
274 uniqifier += 'T';
275 else if( layer == copperCount - 1 )
276 uniqifier += 'B';
277 else
278 uniqifier += char('0' + layer); // layer index char
279 }
280 }
281 }
282
283 uniqifier += ']';
284
285 POINT dsnOffset;
286
287 if( aPad->GetOffset().x || aPad->GetOffset().y )
288 {
289 char offsetTxt[64];
290
291 VECTOR2I offset( aPad->GetOffset().x, aPad->GetOffset().y );
292
293 dsnOffset = mapPt( offset );
294
295 // using () would cause padstack name to be quoted, and {} locks freerouter, so use [].
296 std::snprintf( offsetTxt, sizeof( offsetTxt ), "[%.6g,%.6g]", dsnOffset.x, dsnOffset.y );
297
298 uniqifier += offsetTxt;
299 }
300
301 switch( aPad->GetShape() )
302 {
303 case PAD_SHAPE::CIRCLE:
304 {
305 double diameter = scale( aPad->GetSize().x );
306
307 for( int ndx = 0; ndx < reportedLayers; ++ndx )
308 {
309 SHAPE* shape = new SHAPE( padstack );
310
311 padstack->Append( shape );
312
313 CIRCLE* circle = new CIRCLE( shape );
314
315 shape->SetShape( circle );
316
317 circle->SetLayerId( layerName[ndx] );
318 circle->SetDiameter( diameter );
319 circle->SetVertex( dsnOffset );
320 }
321
322 snprintf( name, sizeof(name), "Round%sPad_%.6g_um",
323 uniqifier.c_str(), IU2um( aPad->GetSize().x ) );
324
325 name[ sizeof(name) - 1 ] = 0;
326
327 padstack->SetPadstackId( name );
328 break;
329 }
330
331 case PAD_SHAPE::RECTANGLE:
332 {
333 double dx = scale( aPad->GetSize().x ) / 2.0;
334 double dy = scale( aPad->GetSize().y ) / 2.0;
335
336 POINT lowerLeft( -dx, -dy );
337 POINT upperRight( dx, dy );
338
339 lowerLeft += dsnOffset;
340 upperRight += dsnOffset;
341
342 for( int ndx = 0; ndx < reportedLayers; ++ndx )
343 {
344 SHAPE* shape = new SHAPE( padstack );
345
346 padstack->Append( shape );
347
348 RECTANGLE* rect = new RECTANGLE( shape );
349
350 shape->SetShape( rect );
351
352 rect->SetLayerId( layerName[ndx] );
353 rect->SetCorners( lowerLeft, upperRight );
354 }
355
356 snprintf( name, sizeof( name ), "Rect%sPad_%.6gx%.6g_um", uniqifier.c_str(),
357 IU2um( aPad->GetSize().x ), IU2um( aPad->GetSize().y ) );
358
359 name[sizeof( name ) - 1] = 0;
360
361 padstack->SetPadstackId( name );
362 break;
363 }
364
365 case PAD_SHAPE::OVAL:
366 {
367 double dx = scale( aPad->GetSize().x ) / 2.0;
368 double dy = scale( aPad->GetSize().y ) / 2.0;
369 double dr = dx - dy;
370 double radius;
371 POINT pstart;
372 POINT pstop;
373
374 if( dr >= 0 ) // oval is horizontal
375 {
376 radius = dy;
377
378 pstart = POINT( -dr, 0.0 );
379 pstop = POINT( dr, 0.0 );
380 }
381 else // oval is vertical
382 {
383 radius = dx;
384 dr = -dr;
385
386 pstart = POINT( 0.0, -dr );
387 pstop = POINT( 0.0, dr );
388 }
389
390 pstart += dsnOffset;
391 pstop += dsnOffset;
392
393 for( int ndx = 0; ndx < reportedLayers; ++ndx )
394 {
395 SHAPE* shape;
396 PATH* path;
397
398 // see http://www.freerouting.net/usren/viewtopic.php?f=3&t=317#p408
399 shape = new SHAPE( padstack );
400
401 padstack->Append( shape );
402 path = makePath( pstart, pstop, layerName[ndx] );
403 shape->SetShape( path );
404 path->aperture_width = 2.0 * radius;
405 }
406
407 snprintf( name, sizeof( name ), "Oval%sPad_%.6gx%.6g_um", uniqifier.c_str(),
408 IU2um( aPad->GetSize().x ), IU2um( aPad->GetSize().y ) );
409 name[sizeof( name ) - 1] = 0;
410
411 padstack->SetPadstackId( name );
412 break;
413 }
414
415 case PAD_SHAPE::TRAPEZOID:
416 {
417 double dx = scale( aPad->GetSize().x ) / 2.0;
418 double dy = scale( aPad->GetSize().y ) / 2.0;
419
420 double ddx = scale( aPad->GetDelta().x ) / 2.0;
421 double ddy = scale( aPad->GetDelta().y ) / 2.0;
422
423 // see class_pad_draw_functions.cpp which draws the trapezoid pad
424 POINT lowerLeft( -dx - ddy, -dy - ddx );
425 POINT upperLeft( -dx + ddy, +dy + ddx );
426 POINT upperRight( +dx - ddy, +dy - ddx );
427 POINT lowerRight( +dx + ddy, -dy + ddx );
428
429 lowerLeft += dsnOffset;
430 upperLeft += dsnOffset;
431 upperRight += dsnOffset;
432 lowerRight += dsnOffset;
433
434 for( int ndx = 0; ndx < reportedLayers; ++ndx )
435 {
436 SHAPE* shape = new SHAPE( padstack );
437
438 padstack->Append( shape );
439
440 // a T_polygon exists as a PATH
441 PATH* polygon = new PATH( shape, T_polygon );
442
443 shape->SetShape( polygon );
444
445 polygon->SetLayerId( layerName[ndx] );
446
447 polygon->AppendPoint( lowerLeft );
448 polygon->AppendPoint( upperLeft );
449 polygon->AppendPoint( upperRight );
450 polygon->AppendPoint( lowerRight );
451 }
452
453 // this string _must_ be unique for a given physical shape
454 snprintf( name, sizeof( name ), "Trapz%sPad_%.6gx%.6g_%c%.6gx%c%.6g_um", uniqifier.c_str(),
455 IU2um( aPad->GetSize().x ), IU2um( aPad->GetSize().y ),
456 aPad->GetDelta().x < 0 ? 'n' : 'p', std::abs( IU2um( aPad->GetDelta().x ) ),
457 aPad->GetDelta().y < 0 ? 'n' : 'p', std::abs( IU2um( aPad->GetDelta().y ) ) );
458 name[sizeof( name ) - 1] = 0;
459
460 padstack->SetPadstackId( name );
461 break;
462 }
463
464 case PAD_SHAPE::CHAMFERED_RECT:
465 case PAD_SHAPE::ROUNDRECT:
466 {
467 // Export the shape as as polygon, round rect does not exist as primitive
468 const int circleToSegmentsCount = 36;
469 int rradius = aPad->GetRoundRectCornerRadius();
470 SHAPE_POLY_SET cornerBuffer;
471
472 // Use a slightly bigger shape because the round corners are approximated by
473 // segments, giving to the polygon a slightly smaller shape than the actual shape
474
475 /* calculates the coeff to compensate radius reduction of holes clearance
476 * due to the segment approx.
477 * For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2)
478 * correctionFactor is cos( PI/s_CircleToSegmentsCount )
479 */
480 double correctionFactor = cos( M_PI / (double) circleToSegmentsCount );
481 int extra_clearance = KiROUND( rradius * ( 1.0 - correctionFactor ) );
482 VECTOR2I psize = aPad->GetSize();
483 psize.x += extra_clearance * 2;
484 psize.y += extra_clearance * 2;
485 rradius += extra_clearance;
486 bool doChamfer = aPad->GetShape() == PAD_SHAPE::CHAMFERED_RECT;
487
488 TransformRoundChamferedRectToPolygon( cornerBuffer, VECTOR2I( 0, 0 ), psize, ANGLE_0,
489 rradius, aPad->GetChamferRectRatio(),
490 doChamfer ? aPad->GetChamferPositions() : 0,
491 0, aBoard->GetDesignSettings().m_MaxError,
492 ERROR_INSIDE );
493
494 SHAPE_LINE_CHAIN& polygonal_shape = cornerBuffer.Outline( 0 );
495
496 for( int ndx = 0; ndx < reportedLayers; ++ndx )
497 {
498 SHAPE* shape = new SHAPE( padstack );
499
500 padstack->Append( shape );
501
502 // a T_polygon exists as a PATH
503 PATH* polygon = new PATH( shape, T_polygon );
504
505 shape->SetShape( polygon );
506
507 polygon->SetLayerId( layerName[ndx] );
508
509 // append a closed polygon
510 POINT first_corner;
511
512 for( int idx = 0; idx < polygonal_shape.PointCount(); idx++ )
513 {
514 POINT corner( scale( polygonal_shape.CPoint( idx ).x ),
515 scale( -polygonal_shape.CPoint( idx ).y ) );
516 corner += dsnOffset;
517 polygon->AppendPoint( corner );
518
519 if( idx == 0 )
520 first_corner = corner;
521 }
522
523 polygon->AppendPoint( first_corner ); // Close polygon
524 }
525
526 // this string _must_ be unique for a given physical shape
527 snprintf( name, sizeof( name ), "RoundRect%sPad_%.6gx%.6g_%.6g_um_%f_%X", uniqifier.c_str(),
528 IU2um( aPad->GetSize().x ), IU2um( aPad->GetSize().y ), IU2um( rradius ),
529 doChamfer ? aPad->GetChamferRectRatio() : 0.0,
530 doChamfer ? aPad->GetChamferPositions() : 0 );
531
532 name[sizeof( name ) - 1] = 0;
533
534 padstack->SetPadstackId( name );
535 break;
536 }
537
538 case PAD_SHAPE::CUSTOM:
539 {
540 std::vector<VECTOR2I> polygonal_shape;
541 SHAPE_POLY_SET pad_shape;
542 aPad->MergePrimitivesAsPolygon( &pad_shape );
543
544#ifdef EXPORT_CUSTOM_PADS_CONVEX_HULL
545 BuildConvexHull( polygonal_shape, pad_shape );
546#else
547 const SHAPE_LINE_CHAIN& p_outline = pad_shape.COutline( 0 );
548
549 for( int ii = 0; ii < p_outline.PointCount(); ++ii )
550 polygonal_shape.push_back( wxPoint( p_outline.CPoint( ii ) ) );
551#endif
552
553 // The polygon must be closed
554 if( polygonal_shape.front() != polygonal_shape.back() )
555 polygonal_shape.push_back( polygonal_shape.front() );
556
557 for( int ndx = 0; ndx < reportedLayers; ++ndx )
558 {
559 SHAPE* shape = new SHAPE( padstack );
560
561 padstack->Append( shape );
562
563 // a T_polygon exists as a PATH
564 PATH* polygon = new PATH( shape, T_polygon );
565
566 shape->SetShape( polygon );
567
568 polygon->SetLayerId( layerName[ndx] );
569
570 for( unsigned idx = 0; idx < polygonal_shape.size(); idx++ )
571 {
572 POINT corner( scale( polygonal_shape[idx].x ), scale( -polygonal_shape[idx].y ) );
573 corner += dsnOffset;
574 polygon->AppendPoint( corner );
575 }
576 }
577
578 // this string _must_ be unique for a given physical shape, so try to make it unique
579 MD5_HASH hash = pad_shape.GetHash();
580 BOX2I rect = aPad->GetBoundingBox();
581 snprintf( name, sizeof( name ), "Cust%sPad_%.6gx%.6g_%.6gx_%.6g_%d_um_%s",
582 uniqifier.c_str(), IU2um( aPad->GetSize().x ), IU2um( aPad->GetSize().y ),
583 IU2um( rect.GetWidth() ), IU2um( rect.GetHeight() ), (int) polygonal_shape.size(),
584 hash.Format( true ).c_str() );
585 name[sizeof( name ) - 1] = 0;
586
587 padstack->SetPadstackId( name );
588 break;
589 }
590 }
591
592 return padstack;
593}
594
595
597typedef std::map<wxString, int> PINMAP;
598
599
601{
602 PINMAP pinmap;
603 wxString padNumber;
604
605 PCB_TYPE_COLLECTOR fpItems;
606
607 // get all the FOOTPRINT's pads.
608 fpItems.Collect( aFootprint, { PCB_PAD_T } );
609
610 IMAGE* image = new IMAGE( nullptr );
611
612 image->m_image_id = aFootprint->GetFPID().Format().c_str();
613
614 // from the pads, and make an IMAGE using collated padstacks.
615 for( int p = 0; p < fpItems.GetCount(); ++p )
616 {
617 PAD* pad = (PAD*) fpItems[p];
618
619 // see if this pad is a through hole with no copper on its perimeter
620 if( isRoundKeepout( pad ) )
621 {
622 double diameter = scale( pad->GetDrillSize().x );
623 POINT vertex = mapPt( pad->GetFPRelativePosition() );
624
625 diameter += scale( aBoard->GetDesignSettings().m_HoleClearance * 2 );
626
627 int layerCount = aBoard->GetCopperLayerCount();
628
629 for( int layer=0; layer<layerCount; ++layer )
630 {
631 KEEPOUT* keepout = new KEEPOUT( image, T_keepout );
632
633 image->m_keepouts.push_back( keepout );
634
635 CIRCLE* circle = new CIRCLE( keepout );
636
637 keepout->SetShape( circle );
638
639 circle->SetDiameter( diameter );
640 circle->SetVertex( vertex );
641 circle->SetLayerId( m_layerIds[layer].c_str() );
642 }
643 }
644 else // else if() could there be a square keepout here?
645 {
646 // Pads not on copper layers (i.e. only on tech layers) are ignored
647 // because they create invalid pads in .dsn file for freeroute
648 LSET mask_copper_layers = pad->GetLayerSet() & LSET::AllCuMask();
649
650 if( !mask_copper_layers.any() )
651 continue;
652
653 PADSTACK* padstack = makePADSTACK( aBoard, pad );
654 PADSTACKSET::iterator iter = m_padstackset.find( *padstack );
655
656 if( iter != m_padstackset.end() )
657 {
658 // padstack is a duplicate, delete it and use the original
659 delete padstack;
660 padstack = (PADSTACK*) *iter.base(); // folklore, be careful here
661 }
662 else
663 {
664 m_padstackset.insert( padstack );
665 }
666
667 PIN* pin = new PIN( image );
668
669 padNumber = pad->GetNumber();
670 pin->m_pin_id = TO_UTF8( padNumber );
671
672 if( padNumber != wxEmptyString && pinmap.find( padNumber ) == pinmap.end() )
673 {
674 pinmap[ padNumber ] = 0;
675 }
676 else // pad name is a duplicate within this footprint
677 {
678 int duplicates = ++pinmap[ padNumber ];
679
680 pin->m_pin_id +=
681 "@" + std::to_string( duplicates ); // append "@1" or "@2", etc. to pin name
682 }
683
684 pin->m_kiNetCode = pad->GetNetCode();
685
686 image->m_pins.push_back( pin );
687
688 pin->m_padstack_id = padstack->m_padstack_id;
689
690 EDA_ANGLE angle = pad->GetOrientation() - aFootprint->GetOrientation();
691 pin->SetRotation( angle.Normalize().AsDegrees() );
692
693 VECTOR2I pos( pad->GetFPRelativePosition() );
694
695 pin->SetVertex( mapPt( pos ) );
696 }
697 }
698
699 // get all the FOOTPRINT's SHAPEs and convert those to DSN outlines.
700 fpItems.Collect( aFootprint, { PCB_SHAPE_T } );
701
702 for( int i = 0; i < fpItems.GetCount(); ++i )
703 {
704 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( fpItems[i] );
705 SHAPE* outline;
706 PATH* path;
707
708 switch( graphic->GetShape() )
709 {
710 case SHAPE_T::SEGMENT:
711 outline = new SHAPE( image, T_outline );
712
713 image->Append( outline );
714 path = new PATH( outline );
715
716 outline->SetShape( path );
717 path->SetAperture( scale( graphic->GetWidth() ) );
718 path->SetLayerId( "signal" );
719 path->AppendPoint( mapPt( graphic->GetStart(), aFootprint ) );
720 path->AppendPoint( mapPt( graphic->GetEnd(), aFootprint ) );
721 break;
722
723 case SHAPE_T::CIRCLE:
724 {
725 // this is best done by 4 QARC's but freerouter does not yet support QARCs.
726 // for now, support by using line segments.
727 outline = new SHAPE( image, T_outline );
728 image->Append( outline );
729
730 path = new PATH( outline );
731
732 outline->SetShape( path );
733 path->SetAperture( scale( graphic->GetWidth() ) );
734 path->SetLayerId( "signal" );
735
736 double radius = graphic->GetRadius();
737 VECTOR2I circle_centre = graphic->GetStart();
738
739 SHAPE_LINE_CHAIN polyline;
740 ConvertArcToPolyline( polyline, VECTOR2I( circle_centre ), radius, ANGLE_0, ANGLE_360,
742
743 for( int ii = 0; ii < polyline.PointCount(); ++ii )
744 {
745 VECTOR2I corner( polyline.CPoint( ii ).x, polyline.CPoint( ii ).y );
746 path->AppendPoint( mapPt( corner, aFootprint ) );
747 }
748
749 break;
750 }
751
752 case SHAPE_T::RECTANGLE:
753 {
754 outline = new SHAPE( image, T_outline );
755
756 image->Append( outline );
757 path = new PATH( outline );
758
759 outline->SetShape( path );
760 path->SetAperture( scale( graphic->GetWidth() ) );
761 path->SetLayerId( "signal" );
762 VECTOR2I corner = graphic->GetStart();
763 path->AppendPoint( mapPt( corner, aFootprint ) );
764
765 corner.x = graphic->GetEnd().x;
766 path->AppendPoint( mapPt( corner, aFootprint ) );
767
768 corner.y = graphic->GetEnd().y;
769 path->AppendPoint( mapPt( corner, aFootprint ) );
770
771 corner.x = graphic->GetStart().x;
772 path->AppendPoint( mapPt( corner, aFootprint ) );
773 break;
774 }
775
776 case SHAPE_T::ARC:
777 {
778 // this is best done by QARC's but freerouter does not yet support QARCs.
779 // for now, support by using line segments.
780 // So we use a polygon (PATH) to create a approximate arc shape
781 outline = new SHAPE( image, T_outline );
782
783 image->Append( outline );
784 path = new PATH( outline );
785
786 outline->SetShape( path );
787 path->SetAperture( 0 );//scale( graphic->GetWidth() ) );
788 path->SetLayerId( "signal" );
789
790 VECTOR2I arc_centre = graphic->GetCenter();
791 double radius = graphic->GetRadius() + graphic->GetWidth()/2;
792 EDA_ANGLE arcAngle = graphic->GetArcAngle();
793
794 VECTOR2I startRadial = graphic->GetStart() - graphic->GetCenter();
795 EDA_ANGLE arcStart( startRadial );
796
797 arcStart.Normalize();
798
799 // For some obscure reason, FreeRouter does not show the same polygonal
800 // shape for polygons CW and CCW. So used only the order of corners
801 // giving the best look.
802 if( arcAngle < ANGLE_0 )
803 {
804 VECTOR2I endRadial = graphic->GetEnd() - graphic->GetCenter();
805 arcStart = EDA_ANGLE( endRadial );
806 arcStart.Normalize();
807
808 arcAngle = -arcAngle;
809 }
810
811 SHAPE_LINE_CHAIN polyline;
812 ConvertArcToPolyline( polyline, VECTOR2I( arc_centre ), radius, arcStart, arcAngle,
814
815 SHAPE_POLY_SET polyBuffer;
816 polyBuffer.AddOutline( polyline );
817
818 radius -= graphic->GetWidth();
819
820 if( radius > 0 )
821 {
822 polyline.Clear();
823 ConvertArcToPolyline( polyline, VECTOR2I( arc_centre ), radius, arcStart, arcAngle,
825
826 // Add points in reverse order, to create a closed polygon
827 for( int ii = polyline.PointCount() - 1; ii >= 0; --ii )
828 polyBuffer.Append( polyline.CPoint( ii ) );
829 }
830
831 // ensure the polygon is closed
832 polyBuffer.Append( polyBuffer.Outline( 0 ).CPoint( 0 ) );
833
834 VECTOR2I move = graphic->GetCenter() - arc_centre;
835
836 TransformCircleToPolygon( polyBuffer, graphic->GetStart() - move,
837 graphic->GetWidth() / 2, ARC_HIGH_DEF, ERROR_INSIDE );
838
839 TransformCircleToPolygon( polyBuffer, graphic->GetEnd() - move,
840 graphic->GetWidth() / 2, ARC_HIGH_DEF, ERROR_INSIDE );
841
842 polyBuffer.Simplify( SHAPE_POLY_SET::PM_FAST );
843 SHAPE_LINE_CHAIN& poly = polyBuffer.Outline( 0 );
844
845 for( int ii = 0; ii < poly.PointCount(); ++ii )
846 {
847 VECTOR2I corner( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
848 path->AppendPoint( mapPt( corner, aFootprint ) );
849 }
850
851 break;
852 }
853
854 default:
855 continue;
856 }
857 }
858
859 for( ZONE* zone : aFootprint->Zones() )
860 {
861 if( !zone->GetIsRuleArea() )
862 continue;
863
864 // IMAGE object coordinates are relative to the IMAGE not absolute board coordinates.
865 ZONE untransformedZone( *zone );
866
867 EDA_ANGLE angle = -aFootprint->GetOrientation();
868 angle.Normalize();
869 untransformedZone.Rotate( aFootprint->GetPosition(), angle );
870
871 // keepout areas have a type. types are
872 // T_place_keepout, T_via_keepout, T_wire_keepout,
873 // T_bend_keepout, T_elongate_keepout, T_keepout.
874 // Pcbnew knows only T_keepout, T_via_keepout and T_wire_keepout
875 DSN_T keepout_type;
876
877 if( zone->GetDoNotAllowVias() && zone->GetDoNotAllowTracks() )
878 keepout_type = T_keepout;
879 else if( zone->GetDoNotAllowVias() )
880 keepout_type = T_via_keepout;
881 else if( zone->GetDoNotAllowTracks() )
882 keepout_type = T_wire_keepout;
883 else
884 keepout_type = T_keepout;
885
886 // Now, build keepout polygon on each copper layer where the zone
887 // keepout is living (keepout zones can live on many copper layers)
888 const int copperCount = aBoard->GetCopperLayerCount();
889
890 for( int layer = 0; layer < copperCount; layer++ )
891 {
892 if( layer == copperCount-1 )
893 layer = B_Cu;
894
895 if( !zone->IsOnLayer( PCB_LAYER_ID( layer ) ) )
896 continue;
897
898 KEEPOUT* keepout = new KEEPOUT( m_pcb->m_structure, keepout_type );
899 image->m_keepouts.push_back( keepout );
900
901 PATH* mainPolygon = new PATH( keepout, T_polygon );
902 keepout->SetShape( mainPolygon );
903
904 mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
905
906 // Handle the main outlines
908 bool is_first_point = true;
909 VECTOR2I startpoint;
910
911 for( iterator = untransformedZone.IterateWithHoles(); iterator; iterator++ )
912 {
913 VECTOR2I point( iterator->x, iterator->y );
914
915 point -= aFootprint->GetPosition();
916
917 if( is_first_point )
918 {
919 startpoint = point;
920 is_first_point = false;
921 }
922
923 mainPolygon->AppendPoint( mapPt( point ) );
924
925 // this was the end of the main polygon
926 if( iterator.IsEndContour() )
927 {
928 mainPolygon->AppendPoint( mapPt( startpoint ) );
929 break;
930 }
931 }
932
933 WINDOW* window = nullptr;
934 PATH* cutout = nullptr;
935 bool isStartContour = true;
936
937 // handle the cutouts
938 for( iterator++; iterator; iterator++ )
939 {
940 if( isStartContour )
941 {
942 is_first_point = true;
943 window = new WINDOW( keepout );
944 keepout->AddWindow( window );
945
946 cutout = new PATH( window, T_polygon );
947
948 window->SetShape( cutout );
949
950 cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ zone->GetLayer() ] ];
951 }
952
953 isStartContour = iterator.IsEndContour();
954
955 wxASSERT( window );
956 wxASSERT( cutout );
957
958 VECTOR2I point( iterator->x, iterator->y );
959
960 point -= aFootprint->GetPosition();
961
962 if( is_first_point )
963 {
964 startpoint = point;
965 is_first_point = false;
966 }
967
968 cutout->AppendPoint( mapPt( point ) );
969
970 // Close the polygon
971 if( iterator.IsEndContour() )
972 cutout->AppendPoint( mapPt( startpoint ) );
973 }
974 }
975 }
976
977 return image;
978}
979
980
981PADSTACK* SPECCTRA_DB::makeVia( int aCopperDiameter, int aDrillDiameter,
982 int aTopLayer, int aBotLayer )
983{
984 char name[48];
985 PADSTACK* padstack = new PADSTACK();
986 double dsnDiameter = scale( aCopperDiameter );
987
988 for( int layer=aTopLayer; layer<=aBotLayer; ++layer )
989 {
990 SHAPE* shape = new SHAPE( padstack );
991
992 padstack->Append( shape );
993
994 CIRCLE* circle = new CIRCLE( shape );
995
996 shape->SetShape( circle );
997
998 circle->SetDiameter( dsnDiameter );
999 circle->SetLayerId( m_layerIds[layer].c_str() );
1000 }
1001
1002 snprintf( name, sizeof( name ), "Via[%d-%d]_%.6g:%.6g_um",
1003 aTopLayer, aBotLayer, dsnDiameter,
1004 // encode the drill value into the name for later import
1005 IU2um( aDrillDiameter ) );
1006
1007 name[ sizeof(name) - 1 ] = 0;
1008 padstack->SetPadstackId( name );
1009
1010 return padstack;
1011}
1012
1013
1015{
1016 PCB_LAYER_ID topLayerNum;
1017 PCB_LAYER_ID botLayerNum;
1018
1019 aVia->LayerPair( &topLayerNum, &botLayerNum );
1020
1021 int topLayer = m_kicadLayer2pcb[topLayerNum];
1022 int botLayer = m_kicadLayer2pcb[botLayerNum];
1023
1024 if( topLayer > botLayer )
1025 std::swap( topLayer, botLayer );
1026
1027 return makeVia( aVia->GetWidth(), aVia->GetDrillValue(), topLayer, botLayer );
1028}
1029
1030
1031void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
1032{
1033 for( int cnt = 0; cnt < m_brd_outlines.OutlineCount(); cnt++ ) // Should be one outline
1034 {
1035 PATH* path = new PATH( boundary );
1036 boundary->paths.push_back( path );
1037 path->layer_id = "pcb";
1038
1039 SHAPE_LINE_CHAIN& outline = m_brd_outlines.Outline( cnt );
1040
1041 for( int ii = 0; ii < outline.PointCount(); ii++ )
1042 {
1043 VECTOR2I pos( outline.CPoint( ii ).x, outline.CPoint( ii ).y );
1044 path->AppendPoint( mapPt( pos ) );
1045 }
1046
1047 // Close polygon:
1048 VECTOR2I pos0( outline.CPoint( 0 ).x, outline.CPoint( 0 ).y );
1049 path->AppendPoint( mapPt( pos0 ) );
1050
1051 // Generate holes as keepout:
1052 for( int ii = 0; ii < m_brd_outlines.HoleCount( cnt ); ii++ )
1053 {
1054 // emit a signal layers keepout for every interior polygon left...
1055 KEEPOUT* keepout = new KEEPOUT( nullptr, T_keepout );
1056 PATH* poly_ko = new PATH( nullptr, T_polygon );
1057
1058 keepout->SetShape( poly_ko );
1059 poly_ko->SetLayerId( "signal" );
1060 m_pcb->m_structure->m_keepouts.push_back( keepout );
1061
1062 SHAPE_LINE_CHAIN& hole = m_brd_outlines.Hole( cnt, ii );
1063
1064 for( int jj = 0; jj < hole.PointCount(); jj++ )
1065 {
1066 VECTOR2I pos( hole.CPoint( jj ).x, hole.CPoint( jj ).y );
1067 poly_ko->AppendPoint( mapPt( pos ) );
1068 }
1069
1070 // Close polygon:
1071 VECTOR2I pos( hole.CPoint( 0 ).x, hole.CPoint( 0 ).y );
1072 poly_ko->AppendPoint( mapPt( pos ) );
1073 }
1074 }
1075}
1076
1077
1078typedef std::set<std::string> STRINGSET;
1079typedef std::pair<STRINGSET::iterator, bool> STRINGSET_PAIR;
1080
1081
1083{
1084 std::shared_ptr<NET_SETTINGS>& netSettings = aBoard->GetDesignSettings().m_NetSettings;
1085
1086 // Not all boards are exportable. Check that all reference Ids are unique, or we won't be
1087 // able to import the session file which comes back to us later from the router.
1088 {
1089 STRINGSET refs; // holds footprint reference designators
1090
1091 for( FOOTPRINT* footprint : aBoard->Footprints() )
1092 {
1093 if( footprint->GetReference() == wxEmptyString )
1094 {
1095 THROW_IO_ERROR( wxString::Format( _( "Footprint with value of '%s' has an empty "
1096 "reference designator." ),
1097 footprint->GetValue() ) );
1098 }
1099
1100 // if we cannot insert OK, that means the reference has been seen before.
1101 STRINGSET_PAIR refpair = refs.insert( TO_UTF8( footprint->GetReference() ) );
1102
1103 if( !refpair.second ) // insert failed
1104 {
1105 THROW_IO_ERROR( wxString::Format( _( "Multiple footprints have the reference "
1106 "designator '%s'." ),
1107 footprint->GetReference() ) );
1108 }
1109 }
1110 }
1111
1112 if( !m_pcb )
1114
1115 //-----<layer_descriptor>-----------------------------------------------
1116 {
1117 // Specctra wants top physical layer first, then going down to the bottom most physical
1118 // layer in physical sequence.
1119
1120 buildLayerMaps( aBoard );
1121
1122 int layerCount = aBoard->GetCopperLayerCount();
1123
1124 for( int pcbNdx=0; pcbNdx<layerCount; ++pcbNdx )
1125 {
1126 LAYER* layer = new LAYER( m_pcb->m_structure );
1127
1128 m_pcb->m_structure->m_layers.push_back( layer );
1129
1130 layer->name = m_layerIds[pcbNdx];
1131
1132 DSN_T layerType;
1133
1134 switch( aBoard->GetLayerType( m_pcbLayer2kicad[pcbNdx] ) )
1135 {
1136 default:
1137 case LT_SIGNAL: layerType = T_signal; break;
1138 case LT_POWER: layerType = T_power; break;
1139
1140 // Freerouter does not support type "mixed", only signal and power.
1141 // Remap "mixed" to "signal".
1142 case LT_MIXED: layerType = T_signal; break;
1143 case LT_JUMPER: layerType = T_jumper; break;
1144 }
1145
1146 layer->layer_type = layerType;
1147
1148 layer->properties.push_back( PROPERTY() );
1149 PROPERTY* property = &layer->properties.back();
1150 property->name = "index";
1151 property->value = std::to_string( pcbNdx );
1152 }
1153 }
1154
1155 // a space in a quoted token is NOT a terminator, true establishes this.
1157
1158 //-----<unit_descriptor> & <resolution_descriptor>--------------------
1159 {
1160 // Tell freerouter to use "tenths of micrometers", which is 100 nm resolution. Possibly
1161 // more resolution is possible in freerouter, but it would need testing.
1162
1163 m_pcb->m_unit->units = T_um;
1164 m_pcb->m_resolution->units = T_um;
1165 m_pcb->m_resolution->value = 10; // tenths of a um
1166 }
1167
1168 //-----<boundary_descriptor>------------------------------------------
1169 {
1170 // Because fillBOUNDARY() can throw an exception, we link in an empty boundary so the
1171 // BOUNDARY does not get lost in the event of of an exception.
1172 BOUNDARY* boundary = new BOUNDARY( nullptr );
1173
1174 m_pcb->m_structure->SetBOUNDARY( boundary );
1175 fillBOUNDARY( aBoard, boundary );
1176 }
1177
1178 //-----<rules>--------------------------------------------------------
1179 {
1180 char rule[80];
1181 int defaultTrackWidth = netSettings->m_DefaultNetClass->GetTrackWidth();
1182 int defaultClearance = netSettings->m_DefaultNetClass->GetClearance();
1183 double clearance = scale( defaultClearance );
1184
1186
1187 std::snprintf( rule, sizeof( rule ), "(width %.6g)", scale( defaultTrackWidth ) );
1188 rules.push_back( rule );
1189
1190 std::snprintf( rule, sizeof( rule ), "(clearance %.6g)", clearance );
1191 rules.push_back( rule );
1192
1193 // On a high density board (4 mil tracks, 4 mil spacing) a typical solder mask clearance
1194 // will be 2-3 mils. This exposes 2 to 3 mils of bare board around each pad, and would
1195 // leave only 1 to 2 mils of solder mask between the solder mask's boundary and the edge of
1196 // any trace within "clearance" of the pad. So we need at least 2 mils *extra* clearance
1197 // for traces which would come near a pad on a different net. So if the baseline trace to
1198 // trace clearance was 4 mils, then the SMD to trace clearance should be at least 6 mils.
1199 double default_smd = clearance;
1200
1201 if( default_smd <= 6.0 )
1202 default_smd = 6.0;
1203
1204 std::snprintf( rule, sizeof( rule ), "(clearance %.6g (type default_smd))", default_smd );
1205
1206 rules.push_back( rule );
1207
1208 // Pad to pad spacing on a single SMT part can be closer than our clearance. We don't want
1209 // freerouter complaining about that, so output a significantly smaller pad to pad
1210 // clearance to freerouter.
1211 clearance = scale( defaultClearance ) / 4;
1212
1213 std::snprintf( rule, sizeof( rule ), "(clearance %.6g (type smd_smd))", clearance );
1214 rules.push_back( rule );
1215 }
1216
1217 //-----<zones (not keepout areas) become planes>--------------------------------
1218 // Note: only zones are output here, keepout areas are created later.
1219 {
1220 int netlessZones = 0;
1221
1222 for( ZONE* zone : aBoard->Zones() )
1223 {
1224 if( zone->GetIsRuleArea() )
1225 continue;
1226
1227 // Currently, we export only copper layers
1228 if( ! zone->IsOnCopperLayer() )
1229 continue;
1230
1231 // Now, build zone polygon on each copper layer where the zone
1232 // is living (zones can live on many copper layers)
1233 const int copperCount = aBoard->GetCopperLayerCount();
1234
1235 for( int layer = 0; layer < copperCount; layer++ )
1236 {
1237 if( layer == copperCount-1 )
1238 layer = B_Cu;
1239
1240 if( !zone->IsOnLayer( PCB_LAYER_ID( layer ) ) )
1241 continue;
1242
1243 COPPER_PLANE* plane = new COPPER_PLANE( m_pcb->m_structure );
1244
1245 m_pcb->m_structure->m_planes.push_back( plane );
1246
1247 PATH* mainPolygon = new PATH( plane, T_polygon );
1248
1249 plane->SetShape( mainPolygon );
1250 plane->m_name = TO_UTF8( zone->GetNetname() );
1251
1252 if( plane->m_name.size() == 0 )
1253 {
1254 // This is one of those no connection zones, netcode=0, and it has no name.
1255 // Create a unique, bogus netname.
1256 NET* no_net = new NET( m_pcb->m_network );
1257
1258
1259 no_net->m_net_id = "@:no_net_" + std::to_string( netlessZones++ );
1260
1261 // add the bogus net name to network->nets.
1262 m_pcb->m_network->m_nets.push_back( no_net );
1263
1264 // use the bogus net name in the netless zone.
1265 plane->m_name = no_net->m_net_id;
1266 }
1267
1268 mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
1269
1270 // Handle the main outlines
1271 SHAPE_POLY_SET::ITERATOR iterator;
1272 VECTOR2I startpoint;
1273 bool is_first_point = true;
1274
1275 for( iterator = zone->IterateWithHoles(); iterator; iterator++ )
1276 {
1277 VECTOR2I point( iterator->x, iterator->y );
1278
1279 if( is_first_point )
1280 {
1281 startpoint = point;
1282 is_first_point = false;
1283 }
1284
1285 mainPolygon->AppendPoint( mapPt( point ) );
1286
1287 // this was the end of the main polygon
1288 if( iterator.IsEndContour() )
1289 {
1290 // Close polygon
1291 mainPolygon->AppendPoint( mapPt( startpoint ) );
1292 break;
1293 }
1294 }
1295
1296 WINDOW* window = nullptr;
1297 PATH* cutout = nullptr;
1298
1299 bool isStartContour = true;
1300
1301 // handle the cutouts
1302 for( iterator++; iterator; iterator++ )
1303 {
1304 if( isStartContour )
1305 {
1306 is_first_point = true;
1307 window = new WINDOW( plane );
1308 plane->AddWindow( window );
1309
1310 cutout = new PATH( window, T_polygon );
1311 window->SetShape( cutout );
1312 cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
1313 }
1314
1315 // If the point in this iteration is the last of the contour, the next iteration
1316 // will start with a new contour.
1317 isStartContour = iterator.IsEndContour();
1318
1319 wxASSERT( window );
1320 wxASSERT( cutout );
1321
1322 VECTOR2I point( iterator->x, iterator->y );
1323
1324 if( is_first_point )
1325 {
1326 startpoint = point;
1327 is_first_point = false;
1328 }
1329
1330 cutout->AppendPoint( mapPt( point ) );
1331
1332 // Close the polygon
1333 if( iterator.IsEndContour() )
1334 cutout->AppendPoint( mapPt( startpoint ) );
1335 }
1336 } // end build zones by layer
1337 }
1338 }
1339
1340 //-----<zones flagged keepout areas become keepout>--------------------------------
1341 {
1342 for( ZONE* zone : aBoard->Zones() )
1343 {
1344 if( !zone->GetIsRuleArea() )
1345 continue;
1346
1347 // Keepout areas have a type: T_place_keepout, T_via_keepout, T_wire_keepout,
1348 // T_bend_keepout, T_elongate_keepout, T_keepout.
1349 // Pcbnew knows only T_keepout, T_via_keepout and T_wire_keepout
1350 DSN_T keepout_type;
1351
1352 if( zone->GetDoNotAllowVias() && zone->GetDoNotAllowTracks() )
1353 keepout_type = T_keepout;
1354 else if( zone->GetDoNotAllowVias() )
1355 keepout_type = T_via_keepout;
1356 else if( zone->GetDoNotAllowTracks() )
1357 keepout_type = T_wire_keepout;
1358 else
1359 keepout_type = T_keepout;
1360
1361 // Now, build keepout polygon on each copper layer where the zone
1362 // keepout is living (keepout zones can live on many copper layers)
1363 const int copperCount = aBoard->GetCopperLayerCount();
1364
1365 for( int layer = 0; layer < copperCount; layer++ )
1366 {
1367 if( layer == copperCount - 1 )
1368 layer = B_Cu;
1369
1370 if( !zone->IsOnLayer( PCB_LAYER_ID( layer ) ) )
1371 continue;
1372
1373 KEEPOUT* keepout = new KEEPOUT( m_pcb->m_structure, keepout_type );
1374 m_pcb->m_structure->m_keepouts.push_back( keepout );
1375
1376 PATH* mainPolygon = new PATH( keepout, T_polygon );
1377 keepout->SetShape( mainPolygon );
1378
1379 mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
1380
1381 // Handle the main outlines
1382 SHAPE_POLY_SET::ITERATOR iterator;
1383 bool is_first_point = true;
1384 VECTOR2I startpoint;
1385
1386 for( iterator = zone->IterateWithHoles(); iterator; iterator++ )
1387 {
1388 VECTOR2I point( iterator->x, iterator->y );
1389
1390 if( is_first_point )
1391 {
1392 startpoint = point;
1393 is_first_point = false;
1394 }
1395
1396 mainPolygon->AppendPoint( mapPt( point ) );
1397
1398 // this was the end of the main polygon
1399 if( iterator.IsEndContour() )
1400 {
1401 mainPolygon->AppendPoint( mapPt( startpoint ) );
1402 break;
1403 }
1404 }
1405
1406 WINDOW* window = nullptr;
1407 PATH* cutout = nullptr;
1408
1409 bool isStartContour = true;
1410
1411 // handle the cutouts
1412 for( iterator++; iterator; iterator++ )
1413 {
1414 if( isStartContour )
1415 {
1416 is_first_point = true;
1417 window = new WINDOW( keepout );
1418 keepout->AddWindow( window );
1419
1420 cutout = new PATH( window, T_polygon );
1421 window->SetShape( cutout );
1422 cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
1423 }
1424
1425 isStartContour = iterator.IsEndContour();
1426
1427 wxASSERT( window );
1428 wxASSERT( cutout );
1429
1430 VECTOR2I point( iterator->x, iterator->y );
1431
1432 if( is_first_point )
1433 {
1434 startpoint = point;
1435 is_first_point = false;
1436 }
1437
1438 cutout->AppendPoint( mapPt(point) );
1439
1440 // Close the polygon
1441 if( iterator.IsEndContour() )
1442 cutout->AppendPoint( mapPt( startpoint ) );
1443 }
1444 }
1445 }
1446 }
1447
1448 //-----<build the images, components, and netlist>-----------------------
1449 {
1451 std::string componentId;
1452 int highestNetCode = 0;
1453 const NETINFO_LIST& netInfo = aBoard->GetNetInfo();
1454
1455 // find the highest numbered netCode within the board.
1456 for( NETINFO_LIST::iterator i = netInfo.begin(); i != netInfo.end(); ++i )
1457 highestNetCode = std::max( highestNetCode, i->GetNetCode() );
1458
1459 deleteNETs();
1460
1461 // expand the net vector to highestNetCode+1, setting empty to NULL
1462 m_nets.resize( highestNetCode + 1, nullptr );
1463
1464 for( unsigned i = 1 /* skip "No Net" at [0] */; i < m_nets.size(); ++i )
1465 m_nets[i] = new NET( m_pcb->m_network );
1466
1467 for( NETINFO_LIST::iterator i = netInfo.begin(); i != netInfo.end(); ++i )
1468 {
1469 if( i->GetNetCode() > 0 )
1470 m_nets[i->GetNetCode()]->m_net_id = TO_UTF8( i->GetNetname() );
1471 }
1472
1473 m_padstackset.clear();
1474
1475 for( FOOTPRINT* footprint : aBoard->Footprints() )
1476 {
1477 IMAGE* image = makeIMAGE( aBoard, footprint );
1478
1479 componentId = TO_UTF8( footprint->GetReference() );
1480
1481 // Create a net list entry for all the actual pins in the current footprint.
1482 // Location of this code is critical because we fabricated some pin names to ensure
1483 // unique-ness within a footprint, and the life of this 'IMAGE* image' is not
1484 // necessarily long. The exported netlist will have some fabricated pin names in it.
1485 // If you don't like fabricated pin names, then make sure all pads within your
1486 // FOOTPRINTs are uniquely named!
1487 for( unsigned p = 0; p < image->m_pins.size(); ++p )
1488 {
1489 PIN* pin = &image->m_pins[p];
1490 int netcode = pin->m_kiNetCode;
1491
1492 if( netcode > 0 )
1493 {
1494 NET* net = m_nets[netcode];
1495
1496 net->m_pins.push_back( empty );
1497
1498 PIN_REF& pin_ref = net->m_pins.back();
1499
1500 pin_ref.component_id = componentId;
1501 pin_ref.pin_id = pin->m_pin_id;
1502 }
1503 }
1504
1506
1507 if( registered != image )
1508 {
1509 // If our new 'image' is not a unique IMAGE, delete it.
1510 // and use the registered one, known as 'image' after this.
1511 delete image;
1512 image = registered;
1513 }
1514
1515 COMPONENT* comp = m_pcb->m_placement->LookupCOMPONENT( image->GetImageId() );
1516 PLACE* place = new PLACE( comp );
1517
1518 comp->m_places.push_back( place );
1519
1520 place->SetRotation( footprint->GetOrientationDegrees() );
1521 place->SetVertex( mapPt( footprint->GetPosition() ) );
1522 place->m_component_id = componentId;
1523 place->m_part_number = TO_UTF8( footprint->GetValue() );
1524
1525 // footprint is flipped from bottom side, set side to T_back
1526 if( footprint->GetFlag() )
1527 {
1528 EDA_ANGLE angle = ANGLE_180 - footprint->GetOrientation();
1529 place->SetRotation( angle.Normalize().AsDegrees() );
1530
1531 place->m_side = T_back;
1532 }
1533 }
1534
1535 // copy the SPECCTRA_DB::padstackset to the LIBRARY. Since we are
1536 // removing, do not increment the iterator
1537 for( PADSTACKSET::iterator i = m_padstackset.begin(); i != m_padstackset.end();
1538 i = m_padstackset.begin() )
1539 {
1540 PADSTACKSET::auto_type ps = m_padstackset.release( i );
1541 PADSTACK* padstack = ps.release();
1542
1543 m_pcb->m_library->AddPadstack( padstack );
1544 }
1545
1546 // copy our SPECCTRA_DB::nets to the pcb->network
1547 for( unsigned n = 1; n < m_nets.size(); ++n )
1548 {
1549 NET* net = m_nets[n];
1550
1551 if( net->m_pins.size() )
1552 {
1553 // give ownership to pcb->network
1554 m_pcb->m_network->m_nets.push_back( net );
1555 m_nets[n] = nullptr;
1556 }
1557 }
1558 }
1559
1560 //-----< output vias used in netclasses >-----------------------------------
1561 {
1562 // Assume the netclass vias are all the same kind of thru, blind, or buried vias.
1563 // This is in lieu of either having each netclass via have its own layer pair in
1564 // the netclass dialog, or such control in the specctra export dialog.
1565
1566 m_top_via_layer = 0; // first specctra cu layer is number zero.
1568
1569 // Add the via from the Default netclass first. The via container
1570 // in pcb->library preserves the sequence of addition.
1571
1572 PADSTACK* via = makeVia( netSettings->m_DefaultNetClass->GetViaDiameter(),
1573 netSettings->m_DefaultNetClass->GetViaDrill(),
1575
1576 // we AppendVia() this first one, there is no way it can be a duplicate,
1577 // the pcb->library via container is empty at this point. After this,
1578 // we'll have to use LookupVia().
1579 wxASSERT( m_pcb->m_library->m_vias.size() == 0 );
1581
1582 // set the "spare via" index at the start of the
1583 // pcb->library->spareViaIndex = pcb->library->vias.size();
1584
1585 // output the non-Default netclass vias
1586 for( const auto& [ name, netclass ] : netSettings->m_NetClasses )
1587 {
1588 via = makeVia( netclass->GetViaDiameter(), netclass->GetViaDrill(),
1590
1591 // maybe add 'via' to the library, but only if unique.
1593
1594 if( registered != via )
1595 delete via;
1596 }
1597 }
1598
1599 //-----<create the wires from tracks>-----------------------------------
1600 {
1601 // export all of them for now, later we'll decide what controls we need on this.
1602 std::string netname;
1603 WIRING* wiring = m_pcb->m_wiring;
1604 PATH* path = nullptr;
1605
1606 int old_netcode = -1;
1607 int old_width = -1;
1608 int old_layer = UNDEFINED_LAYER;
1609
1610 for( PCB_TRACK* track : aBoard->Tracks() )
1611 {
1612 if( !track->IsType( { PCB_TRACE_T, PCB_ARC_T } ) )
1613 continue;
1614
1615 int netcode = track->GetNetCode();
1616
1617 if( netcode == 0 )
1618 continue;
1619
1620 if( old_netcode != netcode
1621 || old_width != track->GetWidth()
1622 || old_layer != track->GetLayer()
1623 || ( path && path->points.back() != mapPt( track->GetStart() ) ) )
1624 {
1625 old_width = track->GetWidth();
1626 old_layer = track->GetLayer();
1627
1628 if( old_netcode != netcode )
1629 {
1630 old_netcode = netcode;
1631 NETINFO_ITEM* net = aBoard->FindNet( netcode );
1632 wxASSERT( net );
1633 netname = TO_UTF8( net->GetNetname() );
1634 }
1635
1636 WIRE* wire = new WIRE( wiring );
1637
1638 wiring->wires.push_back( wire );
1639 wire->m_net_id = netname;
1640
1641 if( track->IsLocked() )
1642 wire->m_wire_type = T_fix; // tracks with fix property are not returned in .ses files
1643 else
1644 wire->m_wire_type = T_route; // could be T_protect
1645
1646 int kiLayer = track->GetLayer();
1647 int pcbLayer = m_kicadLayer2pcb[kiLayer];
1648
1649 path = new PATH( wire );
1650 wire->SetShape( path );
1651 path->layer_id = m_layerIds[pcbLayer];
1652 path->aperture_width = scale( old_width );
1653 path->AppendPoint( mapPt( track->GetStart() ) );
1654 }
1655
1656 if( path ) // Should not occur
1657 path->AppendPoint( mapPt( track->GetEnd() ) );
1658 }
1659 }
1660
1661 //-----<export the existing real BOARD instantiated vias>-----------------
1662 {
1663 // Export all vias, once per unique size and drill diameter combo.
1664 for( PCB_TRACK* track : aBoard->Tracks() )
1665 {
1666 if( track->Type() != PCB_VIA_T )
1667 continue;
1668
1669 PCB_VIA* via = static_cast<PCB_VIA*>( track );
1670 int netcode = via->GetNetCode();
1671
1672 if( netcode == 0 )
1673 continue;
1674
1675 PADSTACK* padstack = makeVia( via );
1676 PADSTACK* registered = m_pcb->m_library->LookupVia( padstack );
1677
1678 // if the one looked up is not our padstack, then delete our padstack
1679 // since it was a duplicate of one already registered.
1680 if( padstack != registered )
1681 delete padstack;
1682
1683 WIRE_VIA* dsnVia = new WIRE_VIA( m_pcb->m_wiring );
1684
1685 m_pcb->m_wiring->wire_vias.push_back( dsnVia );
1686
1687 dsnVia->m_padstack_id = registered->m_padstack_id;
1688 dsnVia->m_vertexes.push_back( mapPt( via->GetPosition() ) );
1689
1690 NETINFO_ITEM* net = aBoard->FindNet( netcode );
1691 wxASSERT( net );
1692
1693 dsnVia->m_net_id = TO_UTF8( net->GetNetname() );
1694
1695 if( via->IsLocked() )
1696 dsnVia->m_via_type = T_fix; // vias with fix property are not returned in .ses files
1697 else
1698 dsnVia->m_via_type = T_route; // could be T_protect
1699 }
1700 }
1701
1702 //-----<via_descriptor>-------------------------------------------------
1703 {
1704 // The pcb->library will output <padstack_descriptors> which is a combined list of part
1705 // padstacks and via padstacks. specctra dsn uses the <via_descriptors> to say which of
1706 // those padstacks are vias.
1707
1708 // Output the vias in the padstack list here, by name only. This must be done after
1709 // exporting existing vias as WIRE_VIAs.
1710 VIA* vias = m_pcb->m_structure->m_via;
1711
1712 for( unsigned viaNdx = 0; viaNdx < m_pcb->m_library->m_vias.size(); ++viaNdx )
1713 vias->AppendVia( m_pcb->m_library->m_vias[viaNdx].m_padstack_id.c_str() );
1714 }
1715
1716 //-----<output NETCLASSs>----------------------------------------------------
1717
1718 exportNETCLASS( netSettings->m_DefaultNetClass, aBoard );
1719
1720 for( const auto& [ name, netclass ] : netSettings->m_NetClasses )
1721 exportNETCLASS( netclass, aBoard );
1722}
1723
1724
1725void SPECCTRA_DB::exportNETCLASS( const std::shared_ptr<NETCLASS>& aNetClass, BOARD* aBoard )
1726{
1727 /* From page 11 of specctra spec:
1728 *
1729 * Routing and Placement Rule Hierarchies
1730 *
1731 * Routing and placement rules can be defined at multiple levels of design
1732 * specification. When a routing or placement rule is defined for an object at
1733 * multiple levels, a predefined routing or placement precedence order
1734 * automatically determines which rule to apply to the object. The routing rule
1735 * precedence order is
1736 *
1737 * pcb < layer < class < class layer < group_set < group_set layer < net <
1738 * net layer < group < group layer < fromto < fromto layer < class_class <
1739 * class_class layer < padstack < region < class region < net region <
1740 * class_class region
1741 *
1742 * A pcb rule (global rule for the PCB design) has the lowest precedence in the
1743 * hierarchy. A class-to-class region rule has the highest precedence. Rules
1744 * set at one level of the hierarchy override conflicting rules set at lower
1745 * levels. The placement rule precedence order is
1746 *
1747 * pcb < image_set < image < component < super cluster < room <
1748 * room_image_set < family_family < image_image
1749 *
1750 * A pcb rule (global rule for the PCB design) has the lowest precedence in the
1751 * hierarchy. An image-to-image rule has the highest precedence. Rules set at
1752 * one level of the hierarchy override conflicting rules set at lower levels.
1753 */
1754
1755 char text[256];
1756
1757 CLASS* clazz = new CLASS( m_pcb->m_network );
1758
1759 m_pcb->m_network->m_classes.push_back( clazz );
1760
1761 // Freerouter creates a class named 'default' anyway, and if we try to use that we end up
1762 // with two 'default' via rules so use something else as the name of our default class.
1763 clazz->m_class_id = TO_UTF8( aNetClass->GetName() );
1764
1765 for( NETINFO_ITEM* net : aBoard->GetNetInfo() )
1766 {
1767 if( net->GetNetClass()->GetName() == clazz->m_class_id )
1768 clazz->m_net_ids.push_back( TO_UTF8( net->GetNetname() ) );
1769 }
1770
1771 clazz->m_rules = new RULE( clazz, T_rule );
1772
1773 // output the track width.
1774 int trackWidth = aNetClass->GetTrackWidth();
1775 std::snprintf( text, sizeof( text ), "(width %.6g)", scale( trackWidth ) );
1776 clazz->m_rules->m_rules.push_back( text );
1777
1778 // output the clearance.
1779 int clearance = aNetClass->GetClearance();
1780 std::snprintf( text, sizeof( text ), "(clearance %.6g)", scale( clearance ) );
1781 clazz->m_rules->m_rules.push_back( text );
1782
1783 if( aNetClass->GetName() == NETCLASS::Default )
1784 clazz->m_class_id = "kicad_default";
1785
1786 // The easiest way to get the via name is to create a temporary via (which generates the
1787 // name internal to the PADSTACK), and then grab the name and delete the via. There are not
1788 // that many netclasses so this should never become a performance issue.
1789
1790 PADSTACK* via = makeVia( aNetClass->GetViaDiameter(), aNetClass->GetViaDrill(),
1792
1793 snprintf( text, sizeof(text), "(use_via %s)", via->GetPadstackId().c_str() );
1794 clazz->m_circuit.push_back( text );
1795
1796 delete via;
1797}
1798
1799
1801{
1802 // DSN Images (=KiCad FOOTPRINTs and PADs) must be presented from the top view.
1803 // Note: to export footprints, the footprints must be flipped around the X axis, otherwise
1804 // the rotation angle is not good.
1805 for( FOOTPRINT* footprint : aBoard->Footprints() )
1806 {
1807 footprint->SetFlag( 0 );
1808
1809 if( footprint->GetLayer() == B_Cu )
1810 {
1811 footprint->Flip( footprint->GetPosition(), false );
1812 footprint->SetFlag( 1 );
1813 }
1814 }
1815
1817}
1818
1819
1821{
1823 return;
1824
1825 // DSN Images (=KiCad FOOTPRINTs and PADs) must be presented from the
1826 // top view. Restore those that were flipped.
1827 // Note: to export footprints, the footprints were flipped around the X axis,
1828 for( FOOTPRINT* footprint : aBoard->Footprints() )
1829 {
1830 if( footprint->GetFlag() )
1831 {
1832 footprint->Flip( footprint->GetPosition(), false );
1833 footprint->SetFlag( 0 );
1834 }
1835 }
1836
1837 m_footprintsAreFlipped = false;
1838}
1839
1840} // namespace DSN
const char * name
Definition: DXF_plotter.cpp:57
constexpr int ARC_HIGH_DEF
Definition: base_units.h:120
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
@ LT_POWER
Definition: board.h:153
@ LT_MIXED
Definition: board.h:154
@ LT_JUMPER
Definition: board.h:155
@ LT_SIGNAL
Definition: board.h:152
Handles how to draw a screen (a board, a schematic ...)
Definition: base_screen.h:41
bool IsContentModified() const
Definition: base_screen.h:60
void SetContentModified(bool aModified=true)
Definition: base_screen.h:59
std::shared_ptr< NET_SETTINGS > m_NetSettings
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:282
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr, bool aAllowUseArcsInPolygons=false, bool aIncludeNPTHAsOutlines=false)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
Definition: board.cpp:2373
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:853
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1803
const ZONES & Zones() const
Definition: board.h:327
LAYER_T GetLayerType(PCB_LAYER_ID aLayer) const
Return the type of the copper layer given by aLayer.
Definition: board.cpp:598
void SynchronizeNetsAndNetClasses(bool aResetTrackAndViaSizes)
Copy NETCLASS info to each NET, based on NET membership in a NETCLASS.
Definition: board.cpp:1936
int GetCopperLayerCount() const
Definition: board.cpp:653
const FOOTPRINTS & Footprints() const
Definition: board.h:323
const TRACKS & Tracks() const
Definition: board.h:321
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:794
size_type GetHeight() const
Definition: box2.h:205
size_type GetWidth() const
Definition: box2.h:204
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
PATHS paths
Definition: specctra.h:728
void SetLayerId(const char *aLayerId)
Definition: specctra.h:756
void SetVertex(const POINT &aVertex)
Definition: specctra.h:766
void SetDiameter(double aDiameter)
Definition: specctra.h:761
The <class_descriptor> in the specctra spec.
Definition: specctra.h:2747
std::string m_class_id
Definition: specctra.h:2818
RULE * m_rules
Definition: specctra.h:2825
STRINGS m_net_ids
Definition: specctra.h:2820
STRINGS m_circuit
circuit descriptor list
Definition: specctra.h:2823
Implement a <component_descriptor> in the specctra dsn spec.
Definition: specctra.h:1767
PLACES m_places
Definition: specctra.h:1808
A <plane_descriptor> in the specctra dsn spec.
Definition: specctra.h:1352
void Append(ELEM *aElem)
Definition: specctra.h:321
Used for <keepout_descriptor> and <plane_descriptor>.
Definition: specctra.h:905
void AddWindow(WINDOW *aWindow)
Definition: specctra.h:946
void SetShape(ELEM *aShape)
Definition: specctra.h:929
std::string m_name
Definition: specctra.h:1011
PROPERTIES properties
Definition: specctra.h:1287
DSN_T layer_type
one of: T_signal, T_power, T_mixed, T_jumper
Definition: specctra.h:1278
std::string name
Definition: specctra.h:1277
PADSTACK * LookupVia(PADSTACK *aVia)
Add the via only if one exactly like it does not already exist in the padstack container.
Definition: specctra.h:2380
void AppendVia(PADSTACK *aVia)
Add aVia to the internal via container.
Definition: specctra.h:2358
IMAGE * LookupIMAGE(IMAGE *aImage)
Add the image only if one exactly like it does not already exist in the image container.
Definition: specctra.h:2326
PADSTACKS m_vias
Definition: specctra.h:2441
void AddPadstack(PADSTACK *aPadstack)
Definition: specctra.h:2265
CLASSLIST m_classes
Definition: specctra.h:2856
A <net_descriptor> in the DSN spec.
Definition: specctra.h:2590
std::string m_net_id
Definition: specctra.h:2688
PIN_REFS m_pins
Definition: specctra.h:2693
Hold either a via or a pad definition.
Definition: specctra.h:2120
std::string m_padstack_id
Definition: specctra.h:2219
void SetPadstackId(const char *aPadstackId)
Definition: specctra.h:2153
bool space_in_quoted_tokens
Definition: specctra.h:380
Support both the <path_descriptor> and the <polygon_descriptor> per the specctra dsn spec.
Definition: specctra.h:582
void SetLayerId(const char *aLayerId)
Definition: specctra.h:599
void AppendPoint(const POINT &aPoint)
Definition: specctra.h:592
std::string layer_id
Definition: specctra.h:649
UNIT_RES * m_unit
Definition: specctra.h:3250
UNIT_RES * m_resolution
Definition: specctra.h:3249
NETWORK * m_network
Definition: specctra.h:3254
PLACEMENT * m_placement
Definition: specctra.h:3252
STRUCTURE * m_structure
Definition: specctra.h:3251
PARSER * m_parser
Definition: specctra.h:3248
WIRING * m_wiring
Definition: specctra.h:3255
LIBRARY * m_library
Definition: specctra.h:3253
COMPONENT * LookupCOMPONENT(const std::string &imageName)
Look up a COMPONENT by name.
Definition: specctra.h:1837
Implement a <placement_reference> in the specctra dsn spec.
Definition: specctra.h:1687
void SetVertex(const POINT &aVertex)
Definition: specctra.h:1715
void SetRotation(double aRotation)
Definition: specctra.h:1722
DSN_T m_side
Definition: specctra.h:1734
std::string m_part_number
Definition: specctra.h:1757
std::string m_component_id
reference designator
Definition: specctra.h:1732
void SetCorners(const POINT &aPoint0, const POINT &aPoint1)
Definition: specctra.h:451
void SetLayerId(const char *aLayerId)
Definition: specctra.h:446
A <rule_descriptor> in the specctra dsn spec.
Definition: specctra.h:491
STRINGS m_rules
rules are saved in std::string form.
Definition: specctra.h:529
A "(shape ..)" element in the specctra dsn spec.
Definition: specctra.h:1893
A DSN data tree, usually coming from a DSN file.
Definition: specctra.h:3641
int m_top_via_layer
specctra cu layers, 0 based index:
Definition: specctra.h:4001
IMAGE * makeIMAGE(BOARD *aBoard, FOOTPRINT *aFootprint)
Allocates an I::MAGE on the heap and creates all the PINs according to the PADs in the FOOTPRINT.
void buildLayerMaps(BOARD *aBoard)
Create a few data translation structures for layer name and number mapping between the DSN::PCB struc...
Definition: specctra.cpp:76
void ExportPCB(const wxString &aFilename, bool aNameChange=false)
Write the internal PCB instance out as a SPECTRA DSN format file.
Definition: specctra.cpp:3696
SHAPE_POLY_SET m_brd_outlines
Definition: specctra.h:3973
void FlipFOOTPRINTs(BOARD *aBoard)
Flip the footprints which are on the back side of the board to the front.
STRINGS m_layerIds
indexed by PCB layer number
Definition: specctra.h:3982
bool m_footprintsAreFlipped
Definition: specctra.h:3978
void deleteNETs()
Delete all the NETs that may be in here.
Definition: specctra.h:3938
void fillBOUNDARY(BOARD *aBoard, BOUNDARY *aBoundary)
Make the board perimeter for the DSN file by filling the BOUNDARY element in the specctra element tre...
PADSTACK * makePADSTACK(BOARD *aBoard, PAD *aPad)
Create a PADSTACK which matches the given pad.
void SetPCB(PCB *aPcb)
Delete any existing PCB and replaces it with the given one.
Definition: specctra.h:3681
static PCB * MakePCB()
Make a PCB with all the default ELEMs and parts on the heap.
Definition: specctra.cpp:3721
std::vector< PCB_LAYER_ID > m_pcbLayer2kicad
maps PCB layer number to BOARD layer numbers
Definition: specctra.h:3985
bool BuiltBoardOutlines(BOARD *aBoard)
Build the board outlines and store it in m_brd_outlines.
std::vector< int > m_kicadLayer2pcb
maps BOARD layer number to PCB layer numbers
Definition: specctra.h:3984
PADSTACK * makeVia(int aCopperDiameter, int aDrillDiameter, int aTopLayer, int aBotLayer)
Make a round through hole PADSTACK using the given KiCad diameter in deci-mils.
std::vector< NET * > m_nets
we don't want ownership here permanently, so we don't use boost::ptr_vector
Definition: specctra.h:3998
PADSTACKSET m_padstackset
Definition: specctra.h:3995
void exportNETCLASS(const std::shared_ptr< NETCLASS > &aNetClass, BOARD *aBoard)
Export aNetClass to the DSN file.
void FromBOARD(BOARD *aBoard)
Add the entire BOARD to the PCB but does not write it out.
void RevertFOOTPRINTs(BOARD *aBoard)
Flip the footprints which were on the back side of the board back to the back.
KEEPOUTS m_keepouts
Definition: specctra.h:1669
void SetBOUNDARY(BOUNDARY *aBoundary)
Definition: specctra.h:1581
COPPER_PLANES m_planes
Definition: specctra.h:1671
LAYERS m_layers
Definition: specctra.h:1659
RULE * m_rules
Definition: specctra.h:1667
DSN_T units
Definition: specctra.h:432
A <via_descriptor> in the specctra dsn spec.
Definition: specctra.h:1038
void AppendVia(const char *aViaName)
Definition: specctra.h:1046
void SetShape(ELEM *aShape)
Definition: specctra.h:857
A <wire_via_descriptor> in the specctra dsn spec.
Definition: specctra.h:2983
std::string m_net_id
Definition: specctra.h:3114
std::string m_padstack_id
Definition: specctra.h:3112
DSN_T m_via_type
Definition: specctra.h:3116
POINTS m_vertexes
Definition: specctra.h:3113
A <wire_shape_descriptor> in the specctra dsn spec.
Definition: specctra.h:2874
DSN_T m_wire_type
Definition: specctra.h:2968
void SetShape(ELEM *aShape)
Definition: specctra.h:2894
std::string m_net_id
Definition: specctra.h:2966
A <wiring_descriptor> in the specctra dsn spec.
Definition: specctra.h:3130
WIRES wires
Definition: specctra.h:3167
WIRE_VIAS wire_vias
Definition: specctra.h:3168
EDA_ANGLE Normalize()
Definition: eda_angle.h:255
double AsDegrees() const
Definition: eda_angle.h:155
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:663
int GetRadius() const
Definition: eda_shape.cpp:593
SHAPE_T GetShape() const
Definition: eda_shape.h:120
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:162
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:125
EDA_ANGLE GetOrientation() const
Definition: footprint.h:212
ZONES & Zones()
Definition: footprint.h:197
const LIB_ID & GetFPID() const
Definition: footprint.h:233
VECTOR2I GetPosition() const override
Definition: footprint.h:209
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:77
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
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
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:575
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:863
std::string Format(bool aCompactForm=false)
Definition: md5_hash.cpp:97
static const char Default[]
the name of the default NETCLASS
Definition: netclass.h:46
Handle the data for a net.
Definition: netinfo.h:56
const wxString & GetNetname() const
Definition: netinfo.h:114
Wrapper class, so you can iterate through NETINFO_ITEM*s, not std::pair<int/wxString,...
Definition: netinfo.h:401
Container for NETINFO_ITEM elements, which are the nets.
Definition: netinfo.h:342
iterator begin() const
Definition: netinfo.h:446
iterator end() const
Definition: netinfo.h:451
Definition: pad.h:53
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pad.h:386
const BOX2I GetBoundingBox() const override
The bounding box is cached, so this will be efficient most of the time.
Definition: pad.cpp:762
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pad.h:692
const VECTOR2I & GetDrillSize() const
Definition: pad.h:261
void MergePrimitivesAsPolygon(SHAPE_POLY_SET *aMergedPolygon, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Merge all basic shapes to a SHAPE_POLY_SET.
int GetRoundRectCornerRadius() const
Definition: pad.cpp:451
const VECTOR2I & GetOffset() const
Definition: pad.h:268
const VECTOR2I & GetDelta() const
Definition: pad.h:258
PAD_SHAPE GetShape() const
Definition: pad.h:187
int GetChamferPositions() const
Definition: pad.h:618
const VECTOR2I & GetSize() const
Definition: pad.h:250
double GetChamferRectRatio() const
Definition: pad.h:608
PCB_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
BOARD * GetBoard() const
bool ExportSpecctraFile(const wxString &aFullFilename)
Export the current BOARD to a specctra dsn file.
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:75
int GetWidth() const override
Definition: pcb_shape.cpp:367
int GetWidth() const
Definition: pcb_track.h:108
Collect all BOARD_ITEM objects of a given set of KICAD_T type(s).
Definition: collectors.h:523
void Collect(BOARD_ITEM *aBoard, const std::vector< KICAD_T > &aTypes)
Collect BOARD_ITEM objects using this class's Inspector method, which does the collection.
Definition: collectors.cpp:518
int GetDrillValue() const
Calculate the drill value for vias (m_drill if > 0, or default drill value for the board).
Definition: pcb_track.cpp:560
void LayerPair(PCB_LAYER_ID *top_layer, PCB_LAYER_ID *bottom_layer) const
Return the 2 layers used by the via (the via actually uses all layers between these 2 layers)
Definition: pcb_track.cpp:923
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.
void Clear()
Remove all points from the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Base class for iterating over all vertices in a given SHAPE_POLY_SET.
Represent a set of closed polygons.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
MD5_HASH GetHash() const
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
void Simplify(POLYGON_MODE aFastMode)
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFastMo...
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the reference to aHole-th hole in the aIndex-th outline.
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
const char * c_str() const
Definition: utf8.h:103
Handle a list of polygons defining a copper zone.
Definition: zone.h:72
SHAPE_POLY_SET::ITERATOR IterateWithHoles()
Return an iterator to visit all points of the zone's main outline with holes.
Definition: zone.h:534
void Rotate(const VECTOR2I &aCentre, const EDA_ANGLE &aAngle) override
Rotate the outlines.
Definition: zone.cpp:712
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:186
This file is part of the common library.
int ConvertArcToPolyline(SHAPE_LINE_CHAIN &aPolyline, VECTOR2I aCenter, int aRadius, const EDA_ANGLE &aStartAngleDeg, const EDA_ANGLE &aArcAngleDeg, double aAccuracy, ERROR_LOC aErrorLoc)
Generate a polyline to approximate a arc.
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
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.
void BuildConvexHull(std::vector< VECTOR2I > &aResult, const std::vector< VECTOR2I > &aPoly)
Calculate the convex hull of a list of points in counter-clockwise order.
Definition: convex_hull.cpp:87
static bool registered
Definition: coroutines.cpp:128
static bool empty(const wxTextEntryBase *aCtrl)
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:435
static constexpr EDA_ANGLE ANGLE_360
Definition: eda_angle.h:441
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:439
a few functions useful in geometry calculations.
@ ERROR_INSIDE
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
#define MAX_CU_LAYERS
Definition: layer_ids.h:142
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ B_Cu
Definition: layer_ids.h:95
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
This file contains miscellaneous commonly used macros and functions.
This source file implements export and import capabilities to the specctra dsn file format.
Definition: specctra.cpp:63
std::vector< std::string > STRINGS
Definition: specctra.h:165
std::map< wxString, int > PINMAP
data type used to ensure unique-ness of pin names, holding (wxString and int)
static double mapX(int x)
static double mapY(int y)
static double IU2um(int kicadDist)
static POINT mapPt(const VECTOR2I &pt)
Convert a KiCad point into a DSN file point.
static bool isRoundKeepout(PAD *aPad)
Decide if the pad is a copper-less through hole which needs to be made into a round keepout.
std::set< std::string > STRINGSET
void ExportBoardToSpecctraFile(BOARD *aBoard, const wxString &aFullFilename)
Helper method to export board to DSN file.
static PATH * makePath(const POINT &aStart, const POINT &aEnd, const std::string &aLayerName)
Create a PATH element with a single straight line, a pair of vertices.
std::pair< STRINGSET::iterator, bool > STRINGSET_PAIR
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:424
DSN::T DSN_T
Definition: specctra.h:48
const int scale
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:391
A <pin_reference> definition in the specctra dsn spec.
Definition: specctra.h:2449
std::string pin_id
Definition: specctra.h:2475
std::string component_id
Definition: specctra.h:2474
A point in the SPECCTRA DSN coordinate system.
Definition: specctra.h:102
void FixNegativeZero()
Change negative zero to positive zero in the IEEE floating point storage format.
Definition: specctra.h:143
double y
Definition: specctra.h:104
double x
Definition: specctra.h:103
const double IU_PER_MM
Definition: base_units.h:76
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:228
@ 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:97
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
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:118
VECTOR2< int > VECTOR2I
Definition: vector2d.h:602