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