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 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 VECTOR2I offset( aPad->GetOffset().x, aPad->GetOffset().y );
289
290 dsnOffset = mapPt( offset );
291 // Using () would cause padstack name to be quoted, and {} locks freerouter, so use [].
292 std::ostringstream oss;
293 oss.imbue( std::locale::classic() );
294 oss << std::fixed << std::setprecision( 6 )
295 << '[' << dsnOffset.x << ',' << dsnOffset.y << ']';
296 uniqifier += oss.str();
297 }
298
299 switch( aPad->GetShape() )
300 {
301 case PAD_SHAPE::CIRCLE:
302 {
303 double diameter = scale( aPad->GetSize().x );
304
305 for( int ndx = 0; ndx < reportedLayers; ++ndx )
306 {
307 SHAPE* shape = new SHAPE( padstack );
308
309 padstack->Append( shape );
310
311 CIRCLE* circle = new CIRCLE( shape );
312
313 shape->SetShape( circle );
314
315 circle->SetLayerId( layerName[ndx] );
316 circle->SetDiameter( diameter );
317 circle->SetVertex( dsnOffset );
318 }
319
320 std::ostringstream oss;
321 oss << "Round" << uniqifier << "Pad_" << std::fixed << std::setprecision(6)
322 << IU2um(aPad->GetSize().x) << "_um";
323
324 padstack->SetPadstackId( oss.str().c_str() );
325 break;
326 }
327
328 case PAD_SHAPE::RECTANGLE:
329 {
330 double dx = scale( aPad->GetSize().x ) / 2.0;
331 double dy = scale( aPad->GetSize().y ) / 2.0;
332
333 POINT lowerLeft( -dx, -dy );
334 POINT upperRight( dx, dy );
335
336 lowerLeft += dsnOffset;
337 upperRight += dsnOffset;
338
339 for( int ndx = 0; ndx < reportedLayers; ++ndx )
340 {
341 SHAPE* shape = new SHAPE( padstack );
342
343 padstack->Append( shape );
344
345 RECTANGLE* rect = new RECTANGLE( shape );
346
347 shape->SetShape( rect );
348
349 rect->SetLayerId( layerName[ndx] );
350 rect->SetCorners( lowerLeft, upperRight );
351 }
352
353 std::ostringstream oss;
354 oss << "Rect" << uniqifier << "Pad_" << std::fixed << std::setprecision(6)
355 << IU2um(aPad->GetSize().x) << "x" << IU2um(aPad->GetSize().y) << "_um";
356
357 padstack->SetPadstackId( oss.str().c_str() );
358 break;
359 }
360
361 case PAD_SHAPE::OVAL:
362 {
363 double dx = scale( aPad->GetSize().x ) / 2.0;
364 double dy = scale( aPad->GetSize().y ) / 2.0;
365 double dr = dx - dy;
366 double radius;
367 POINT pstart;
368 POINT pstop;
369
370 if( dr >= 0 ) // oval is horizontal
371 {
372 radius = dy;
373
374 pstart = POINT( -dr, 0.0 );
375 pstop = POINT( dr, 0.0 );
376 }
377 else // oval is vertical
378 {
379 radius = dx;
380 dr = -dr;
381
382 pstart = POINT( 0.0, -dr );
383 pstop = POINT( 0.0, dr );
384 }
385
386 pstart += dsnOffset;
387 pstop += dsnOffset;
388
389 for( int ndx = 0; ndx < reportedLayers; ++ndx )
390 {
391 SHAPE* shape;
392 PATH* path;
393
394 // see http://www.freerouting.net/usren/viewtopic.php?f=3&t=317#p408
395 shape = new SHAPE( padstack );
396
397 padstack->Append( shape );
398 path = makePath( pstart, pstop, layerName[ndx] );
399 shape->SetShape( path );
400 path->aperture_width = 2.0 * radius;
401 }
402
403 std::ostringstream oss;
404 oss << "Oval" << uniqifier << "Pad_" << std::fixed << std::setprecision(6)
405 << IU2um(aPad->GetSize().x) << "x" << IU2um(aPad->GetSize().y) << "_um";
406
407 padstack->SetPadstackId( oss.str().c_str() );
408 break;
409 }
410
411 case PAD_SHAPE::TRAPEZOID:
412 {
413 double dx = scale( aPad->GetSize().x ) / 2.0;
414 double dy = scale( aPad->GetSize().y ) / 2.0;
415
416 double ddx = scale( aPad->GetDelta().x ) / 2.0;
417 double ddy = scale( aPad->GetDelta().y ) / 2.0;
418
419 // see class_pad_draw_functions.cpp which draws the trapezoid pad
420 POINT lowerLeft( -dx - ddy, -dy - ddx );
421 POINT upperLeft( -dx + ddy, +dy + ddx );
422 POINT upperRight( +dx - ddy, +dy - ddx );
423 POINT lowerRight( +dx + ddy, -dy + ddx );
424
425 lowerLeft += dsnOffset;
426 upperLeft += dsnOffset;
427 upperRight += dsnOffset;
428 lowerRight += dsnOffset;
429
430 for( int ndx = 0; ndx < reportedLayers; ++ndx )
431 {
432 SHAPE* shape = new SHAPE( padstack );
433
434 padstack->Append( shape );
435
436 // a T_polygon exists as a PATH
437 PATH* polygon = new PATH( shape, T_polygon );
438
439 shape->SetShape( polygon );
440
441 polygon->SetLayerId( layerName[ndx] );
442
443 polygon->AppendPoint( lowerLeft );
444 polygon->AppendPoint( upperLeft );
445 polygon->AppendPoint( upperRight );
446 polygon->AppendPoint( lowerRight );
447 }
448
449 // this string _must_ be unique for a given physical shape
450 std::ostringstream oss;
451 oss << "Trapz" << uniqifier << "Pad_" << std::fixed << std::setprecision(6)
452 << IU2um(aPad->GetSize().x) << "x" << IU2um(aPad->GetSize().y) << "_"
453 << (aPad->GetDelta().x < 0 ? "n" : "p") << std::abs(IU2um(aPad->GetDelta().x)) << "x"
454 << (aPad->GetDelta().y < 0 ? "n" : "p") << std::abs(IU2um(aPad->GetDelta().y)) << "_um";
455
456 padstack->SetPadstackId( oss.str().c_str() );
457 break;
458 }
459
460 case PAD_SHAPE::CHAMFERED_RECT:
461 case PAD_SHAPE::ROUNDRECT:
462 {
463 // Export the shape as as polygon, round rect does not exist as primitive
464 const int circleToSegmentsCount = 36;
465 int rradius = aPad->GetRoundRectCornerRadius();
466 SHAPE_POLY_SET cornerBuffer;
467
468 // Use a slightly bigger shape because the round corners are approximated by
469 // segments, giving to the polygon a slightly smaller shape than the actual shape
470
471 /* calculates the coeff to compensate radius reduction of holes clearance
472 * due to the segment approx.
473 * For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2)
474 * correctionFactor is cos( PI/s_CircleToSegmentsCount )
475 */
476 double correctionFactor = cos( M_PI / (double) circleToSegmentsCount );
477 int extra_clearance = KiROUND( rradius * ( 1.0 - correctionFactor ) );
478 VECTOR2I psize = aPad->GetSize();
479 psize.x += extra_clearance * 2;
480 psize.y += extra_clearance * 2;
481 rradius += extra_clearance;
482 bool doChamfer = aPad->GetShape() == PAD_SHAPE::CHAMFERED_RECT;
483
484 TransformRoundChamferedRectToPolygon( cornerBuffer, VECTOR2I( 0, 0 ), psize, ANGLE_0,
485 rradius, aPad->GetChamferRectRatio(),
486 doChamfer ? aPad->GetChamferPositions() : 0,
487 0, aBoard->GetDesignSettings().m_MaxError,
488 ERROR_INSIDE );
489
490 SHAPE_LINE_CHAIN& polygonal_shape = cornerBuffer.Outline( 0 );
491
492 for( int ndx = 0; ndx < reportedLayers; ++ndx )
493 {
494 SHAPE* shape = new SHAPE( padstack );
495
496 padstack->Append( shape );
497
498 // a T_polygon exists as a PATH
499 PATH* polygon = new PATH( shape, T_polygon );
500
501 shape->SetShape( polygon );
502
503 polygon->SetLayerId( layerName[ndx] );
504
505 // append a closed polygon
506 POINT first_corner;
507
508 for( int idx = 0; idx < polygonal_shape.PointCount(); idx++ )
509 {
510 POINT corner( scale( polygonal_shape.CPoint( idx ).x ),
511 scale( -polygonal_shape.CPoint( idx ).y ) );
512 corner += dsnOffset;
513 polygon->AppendPoint( corner );
514
515 if( idx == 0 )
516 first_corner = corner;
517 }
518
519 polygon->AppendPoint( first_corner ); // Close polygon
520 }
521
522 // this string _must_ be unique for a given physical shape
523 std::ostringstream oss;
524 oss << "RoundRect" << uniqifier << "Pad_"
525 << std::fixed << std::setprecision(6)
526 << IU2um( aPad->GetSize().x ) << 'x'
527 << IU2um( aPad->GetSize().y ) << '_'
528 << IU2um( rradius ) << "_um_"
529 << ( doChamfer ? aPad->GetChamferRectRatio() : 0.0 ) << '_'
530 << std::hex << std::uppercase
531 << ( doChamfer ? aPad->GetChamferPositions() : 0 );
532
533 padstack->SetPadstackId( oss.str().c_str() );
534 break;
535 }
536
537 case PAD_SHAPE::CUSTOM:
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 const HASH_128 hash = pad_shape.GetHash();
579 const BOX2I rect = aPad->GetBoundingBox();
580
581 std::ostringstream oss;
582 oss << "Cust" << uniqifier << "Pad_"
583 << std::fixed << std::setprecision(6)
584 << IU2um( aPad->GetSize().x ) << 'x' << IU2um( aPad->GetSize().y ) << '_'
585 << IU2um( rect.GetWidth() ) << 'x' << IU2um( rect.GetHeight() ) << '_'
586 << polygonal_shape.size() << "_um_"
587 << hash.ToString();
588
589 padstack->SetPadstackId( oss.str().c_str() );
590 break;
591 }
592 }
593
594 return padstack;
595}
596
597
599typedef std::map<wxString, int> PINMAP;
600
601
603{
604 PINMAP pinmap;
605 wxString padNumber;
606
607 PCB_TYPE_COLLECTOR fpItems;
608
609 // get all the FOOTPRINT's pads.
610 fpItems.Collect( aFootprint, { PCB_PAD_T } );
611
612 IMAGE* image = new IMAGE( nullptr );
613
614 image->m_image_id = aFootprint->GetFPID().Format().c_str();
615
616 // from the pads, and make an IMAGE using collated padstacks.
617 for( int p = 0; p < fpItems.GetCount(); ++p )
618 {
619 PAD* pad = (PAD*) fpItems[p];
620
621 // see if this pad is a through hole with no copper on its perimeter
622 if( isRoundKeepout( pad ) )
623 {
624 double diameter = scale( pad->GetDrillSize().x );
625 POINT vertex = mapPt( pad->GetFPRelativePosition() );
626
627 diameter += scale( aBoard->GetDesignSettings().m_HoleClearance * 2 );
628
629 int layerCount = aBoard->GetCopperLayerCount();
630
631 for( int layer=0; layer<layerCount; ++layer )
632 {
633 KEEPOUT* keepout = new KEEPOUT( image, T_keepout );
634
635 image->m_keepouts.push_back( keepout );
636
637 CIRCLE* circle = new CIRCLE( keepout );
638
639 keepout->SetShape( circle );
640
641 circle->SetDiameter( diameter );
642 circle->SetVertex( vertex );
643 circle->SetLayerId( m_layerIds[layer].c_str() );
644 }
645 }
646 else // else if() could there be a square keepout here?
647 {
648 // Pads not on copper layers (i.e. only on tech layers) are ignored
649 // because they create invalid pads in .dsn file for freeroute
650 LSET mask_copper_layers = pad->GetLayerSet() & LSET::AllCuMask();
651
652 if( !mask_copper_layers.any() )
653 continue;
654
655 PADSTACK* padstack = makePADSTACK( aBoard, pad );
656 PADSTACKSET::iterator iter = m_padstackset.find( *padstack );
657
658 if( iter != m_padstackset.end() )
659 {
660 // padstack is a duplicate, delete it and use the original
661 delete padstack;
662 padstack = (PADSTACK*) *iter.base(); // folklore, be careful here
663 }
664 else
665 {
666 m_padstackset.insert( padstack );
667 }
668
669 PIN* pin = new PIN( image );
670
671 padNumber = pad->GetNumber();
672 pin->m_pin_id = TO_UTF8( padNumber );
673
674 if( padNumber != wxEmptyString && pinmap.find( padNumber ) == pinmap.end() )
675 {
676 pinmap[ padNumber ] = 0;
677 }
678 else // pad name is a duplicate within this footprint
679 {
680 int duplicates = ++pinmap[ padNumber ];
681
682 pin->m_pin_id +=
683 "@" + std::to_string( duplicates ); // append "@1" or "@2", etc. to pin name
684 }
685
686 pin->m_kiNetCode = pad->GetNetCode();
687
688 image->m_pins.push_back( pin );
689
690 pin->m_padstack_id = padstack->m_padstack_id;
691
692 EDA_ANGLE angle = pad->GetOrientation() - aFootprint->GetOrientation();
693 pin->SetRotation( angle.Normalize().AsDegrees() );
694
695 VECTOR2I pos( pad->GetFPRelativePosition() );
696
697 pin->SetVertex( mapPt( pos ) );
698 }
699 }
700
701 // get all the FOOTPRINT's SHAPEs and convert those to DSN outlines.
702 fpItems.Collect( aFootprint, { PCB_SHAPE_T } );
703
704 for( int i = 0; i < fpItems.GetCount(); ++i )
705 {
706 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( fpItems[i] );
707 SHAPE* outline;
708 PATH* path;
709
710 switch( graphic->GetShape() )
711 {
712 case SHAPE_T::SEGMENT:
713 outline = new SHAPE( image, T_outline );
714
715 image->Append( outline );
716 path = new PATH( outline );
717
718 outline->SetShape( path );
719 path->SetAperture( scale( graphic->GetWidth() ) );
720 path->SetLayerId( "signal" );
721 path->AppendPoint( mapPt( graphic->GetStart(), aFootprint ) );
722 path->AppendPoint( mapPt( graphic->GetEnd(), aFootprint ) );
723 break;
724
725 case SHAPE_T::CIRCLE:
726 {
727 // this is best done by 4 QARC's but freerouter does not yet support QARCs.
728 // for now, support by using line segments.
729 outline = new SHAPE( image, T_outline );
730 image->Append( outline );
731
732 path = new PATH( outline );
733
734 outline->SetShape( path );
735 path->SetAperture( scale( graphic->GetWidth() ) );
736 path->SetLayerId( "signal" );
737
738 double radius = graphic->GetRadius();
739 VECTOR2I circle_centre = graphic->GetStart();
740
741 SHAPE_LINE_CHAIN polyline;
742 ConvertArcToPolyline( polyline, VECTOR2I( circle_centre ), radius, ANGLE_0, ANGLE_360,
744
745 for( int ii = 0; ii < polyline.PointCount(); ++ii )
746 {
747 VECTOR2I corner( polyline.CPoint( ii ).x, polyline.CPoint( ii ).y );
748 path->AppendPoint( mapPt( corner, aFootprint ) );
749 }
750
751 break;
752 }
753
754 case SHAPE_T::RECTANGLE:
755 {
756 outline = new SHAPE( image, T_outline );
757
758 image->Append( outline );
759 path = new PATH( outline );
760
761 outline->SetShape( path );
762 path->SetAperture( scale( graphic->GetWidth() ) );
763 path->SetLayerId( "signal" );
764 VECTOR2I corner = graphic->GetStart();
765 path->AppendPoint( mapPt( corner, aFootprint ) );
766
767 corner.x = graphic->GetEnd().x;
768 path->AppendPoint( mapPt( corner, aFootprint ) );
769
770 corner.y = graphic->GetEnd().y;
771 path->AppendPoint( mapPt( corner, aFootprint ) );
772
773 corner.x = graphic->GetStart().x;
774 path->AppendPoint( mapPt( corner, aFootprint ) );
775 break;
776 }
777
778 case SHAPE_T::ARC:
779 {
780 // this is best done by QARC's but freerouter does not yet support QARCs.
781 // for now, support by using line segments.
782 // So we use a polygon (PATH) to create a approximate arc shape
783 outline = new SHAPE( image, T_outline );
784
785 image->Append( outline );
786 path = new PATH( outline );
787
788 outline->SetShape( path );
789 path->SetAperture( 0 );//scale( graphic->GetWidth() ) );
790 path->SetLayerId( "signal" );
791
792 VECTOR2I arc_centre = graphic->GetCenter();
793 double radius = graphic->GetRadius() + graphic->GetWidth()/2;
794 EDA_ANGLE arcAngle = graphic->GetArcAngle();
795
796 VECTOR2I startRadial = graphic->GetStart() - graphic->GetCenter();
797 EDA_ANGLE arcStart( startRadial );
798
799 arcStart.Normalize();
800
801 // For some obscure reason, FreeRouter does not show the same polygonal
802 // shape for polygons CW and CCW. So used only the order of corners
803 // giving the best look.
804 if( arcAngle < ANGLE_0 )
805 {
806 VECTOR2I endRadial = graphic->GetEnd() - graphic->GetCenter();
807 arcStart = EDA_ANGLE( endRadial );
808 arcStart.Normalize();
809
810 arcAngle = -arcAngle;
811 }
812
813 SHAPE_LINE_CHAIN polyline;
814 ConvertArcToPolyline( polyline, VECTOR2I( arc_centre ), radius, arcStart, arcAngle,
816
817 SHAPE_POLY_SET polyBuffer;
818 polyBuffer.AddOutline( polyline );
819
820 radius -= graphic->GetWidth();
821
822 if( radius > 0 )
823 {
824 polyline.Clear();
825 ConvertArcToPolyline( polyline, VECTOR2I( arc_centre ), radius, arcStart, arcAngle,
827
828 // Add points in reverse order, to create a closed polygon
829 for( int ii = polyline.PointCount() - 1; ii >= 0; --ii )
830 polyBuffer.Append( polyline.CPoint( ii ) );
831 }
832
833 // ensure the polygon is closed
834 polyBuffer.Append( polyBuffer.Outline( 0 ).CPoint( 0 ) );
835
836 VECTOR2I move = graphic->GetCenter() - arc_centre;
837
838 TransformCircleToPolygon( polyBuffer, graphic->GetStart() - move,
839 graphic->GetWidth() / 2, ARC_HIGH_DEF, ERROR_INSIDE );
840
841 TransformCircleToPolygon( polyBuffer, graphic->GetEnd() - move,
842 graphic->GetWidth() / 2, ARC_HIGH_DEF, ERROR_INSIDE );
843
844 polyBuffer.Simplify( SHAPE_POLY_SET::PM_FAST );
845 SHAPE_LINE_CHAIN& poly = polyBuffer.Outline( 0 );
846
847 for( int ii = 0; ii < poly.PointCount(); ++ii )
848 {
849 VECTOR2I corner( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
850 path->AppendPoint( mapPt( corner, aFootprint ) );
851 }
852
853 break;
854 }
855
856 default:
857 continue;
858 }
859 }
860
861 for( ZONE* zone : aFootprint->Zones() )
862 {
863 if( !zone->GetIsRuleArea() )
864 continue;
865
866 // IMAGE object coordinates are relative to the IMAGE not absolute board coordinates.
867 ZONE untransformedZone( *zone );
868
869 EDA_ANGLE angle = -aFootprint->GetOrientation();
870 angle.Normalize();
871 untransformedZone.Rotate( aFootprint->GetPosition(), angle );
872
873 // keepout areas have a type. types are
874 // T_place_keepout, T_via_keepout, T_wire_keepout,
875 // T_bend_keepout, T_elongate_keepout, T_keepout.
876 // Pcbnew knows only T_keepout, T_via_keepout and T_wire_keepout
877 DSN_T keepout_type;
878
879 if( zone->GetDoNotAllowVias() && zone->GetDoNotAllowTracks() )
880 keepout_type = T_keepout;
881 else if( zone->GetDoNotAllowVias() )
882 keepout_type = T_via_keepout;
883 else if( zone->GetDoNotAllowTracks() )
884 keepout_type = T_wire_keepout;
885 else
886 keepout_type = T_keepout;
887
888 // Now, build keepout polygon on each copper layer where the zone
889 // keepout is living (keepout zones can live on many copper layers)
890 const int copperCount = aBoard->GetCopperLayerCount();
891
892 for( int layer = 0; layer < copperCount; layer++ )
893 {
894 if( layer == copperCount-1 )
895 layer = B_Cu;
896
897 if( !zone->IsOnLayer( PCB_LAYER_ID( layer ) ) )
898 continue;
899
900 KEEPOUT* keepout = new KEEPOUT( m_pcb->m_structure, keepout_type );
901 image->m_keepouts.push_back( keepout );
902
903 PATH* mainPolygon = new PATH( keepout, T_polygon );
904 keepout->SetShape( mainPolygon );
905
906 mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
907
908 // Handle the main outlines
910 bool is_first_point = true;
911 VECTOR2I startpoint;
912
913 for( iterator = untransformedZone.IterateWithHoles(); iterator; iterator++ )
914 {
915 VECTOR2I point( iterator->x, iterator->y );
916
917 point -= aFootprint->GetPosition();
918
919 if( is_first_point )
920 {
921 startpoint = point;
922 is_first_point = false;
923 }
924
925 mainPolygon->AppendPoint( mapPt( point ) );
926
927 // this was the end of the main polygon
928 if( iterator.IsEndContour() )
929 {
930 mainPolygon->AppendPoint( mapPt( startpoint ) );
931 break;
932 }
933 }
934
935 WINDOW* window = nullptr;
936 PATH* cutout = nullptr;
937 bool isStartContour = true;
938
939 // handle the cutouts
940 for( iterator++; iterator; iterator++ )
941 {
942 if( isStartContour )
943 {
944 is_first_point = true;
945 window = new WINDOW( keepout );
946 keepout->AddWindow( window );
947
948 cutout = new PATH( window, T_polygon );
949
950 window->SetShape( cutout );
951
952 cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ zone->GetLayer() ] ];
953 }
954
955 isStartContour = iterator.IsEndContour();
956
957 wxASSERT( window );
958 wxASSERT( cutout );
959
960 VECTOR2I point( iterator->x, iterator->y );
961
962 point -= aFootprint->GetPosition();
963
964 if( is_first_point )
965 {
966 startpoint = point;
967 is_first_point = false;
968 }
969
970 cutout->AppendPoint( mapPt( point ) );
971
972 // Close the polygon
973 if( iterator.IsEndContour() )
974 cutout->AppendPoint( mapPt( startpoint ) );
975 }
976 }
977 }
978
979 return image;
980}
981
982
983PADSTACK* SPECCTRA_DB::makeVia( int aCopperDiameter, int aDrillDiameter,
984 int aTopLayer, int aBotLayer )
985{
986 char name[48];
987 PADSTACK* padstack = new PADSTACK();
988 double dsnDiameter = scale( aCopperDiameter );
989
990 for( int layer=aTopLayer; layer<=aBotLayer; ++layer )
991 {
992 SHAPE* shape = new SHAPE( padstack );
993
994 padstack->Append( shape );
995
996 CIRCLE* circle = new CIRCLE( shape );
997
998 shape->SetShape( circle );
999
1000 circle->SetDiameter( dsnDiameter );
1001 circle->SetLayerId( m_layerIds[layer].c_str() );
1002 }
1003
1004 snprintf( name, sizeof( name ), "Via[%d-%d]_%.6g:%.6g_um",
1005 aTopLayer, aBotLayer, dsnDiameter,
1006 // encode the drill value into the name for later import
1007 IU2um( aDrillDiameter ) );
1008
1009 name[ sizeof(name) - 1 ] = 0;
1010 padstack->SetPadstackId( name );
1011
1012 return padstack;
1013}
1014
1015
1017{
1018 PCB_LAYER_ID topLayerNum;
1019 PCB_LAYER_ID botLayerNum;
1020
1021 aVia->LayerPair( &topLayerNum, &botLayerNum );
1022
1023 int topLayer = m_kicadLayer2pcb[topLayerNum];
1024 int botLayer = m_kicadLayer2pcb[botLayerNum];
1025
1026 if( topLayer > botLayer )
1027 std::swap( topLayer, botLayer );
1028
1029 return makeVia( aVia->GetWidth(), aVia->GetDrillValue(), topLayer, botLayer );
1030}
1031
1032
1033void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
1034{
1035 for( int cnt = 0; cnt < m_brd_outlines.OutlineCount(); cnt++ ) // Should be one outline
1036 {
1037 PATH* path = new PATH( boundary );
1038 boundary->paths.push_back( path );
1039 path->layer_id = "pcb";
1040
1041 SHAPE_LINE_CHAIN& outline = m_brd_outlines.Outline( cnt );
1042
1043 for( int ii = 0; ii < outline.PointCount(); ii++ )
1044 {
1045 VECTOR2I pos( outline.CPoint( ii ).x, outline.CPoint( ii ).y );
1046 path->AppendPoint( mapPt( pos ) );
1047 }
1048
1049 // Close polygon:
1050 VECTOR2I pos0( outline.CPoint( 0 ).x, outline.CPoint( 0 ).y );
1051 path->AppendPoint( mapPt( pos0 ) );
1052
1053 // Generate holes as keepout:
1054 for( int ii = 0; ii < m_brd_outlines.HoleCount( cnt ); ii++ )
1055 {
1056 // emit a signal layers keepout for every interior polygon left...
1057 KEEPOUT* keepout = new KEEPOUT( nullptr, T_keepout );
1058 PATH* poly_ko = new PATH( nullptr, T_polygon );
1059
1060 keepout->SetShape( poly_ko );
1061 poly_ko->SetLayerId( "signal" );
1062 m_pcb->m_structure->m_keepouts.push_back( keepout );
1063
1064 SHAPE_LINE_CHAIN& hole = m_brd_outlines.Hole( cnt, ii );
1065
1066 for( int jj = 0; jj < hole.PointCount(); jj++ )
1067 {
1068 VECTOR2I pos( hole.CPoint( jj ).x, hole.CPoint( jj ).y );
1069 poly_ko->AppendPoint( mapPt( pos ) );
1070 }
1071
1072 // Close polygon:
1073 VECTOR2I pos( hole.CPoint( 0 ).x, hole.CPoint( 0 ).y );
1074 poly_ko->AppendPoint( mapPt( pos ) );
1075 }
1076 }
1077}
1078
1079
1080typedef std::set<std::string> STRINGSET;
1081typedef std::pair<STRINGSET::iterator, bool> STRINGSET_PAIR;
1082
1083
1085{
1086 std::shared_ptr<NET_SETTINGS>& netSettings = aBoard->GetDesignSettings().m_NetSettings;
1087
1088 // Not all boards are exportable. Check that all reference Ids are unique, or we won't be
1089 // able to import the session file which comes back to us later from the router.
1090 {
1091 STRINGSET refs; // holds footprint reference designators
1092
1093 for( FOOTPRINT* footprint : aBoard->Footprints() )
1094 {
1095 if( footprint->GetReference() == wxEmptyString )
1096 {
1097 THROW_IO_ERROR( wxString::Format( _( "Footprint with value of '%s' has an empty "
1098 "reference designator." ),
1099 footprint->GetValue() ) );
1100 }
1101
1102 // if we cannot insert OK, that means the reference has been seen before.
1103 STRINGSET_PAIR refpair = refs.insert( TO_UTF8( footprint->GetReference() ) );
1104
1105 if( !refpair.second ) // insert failed
1106 {
1107 THROW_IO_ERROR( wxString::Format( _( "Multiple footprints have the reference "
1108 "designator '%s'." ),
1109 footprint->GetReference() ) );
1110 }
1111 }
1112 }
1113
1114 if( !m_pcb )
1116
1117 //-----<layer_descriptor>-----------------------------------------------
1118 {
1119 // Specctra wants top physical layer first, then going down to the bottom most physical
1120 // layer in physical sequence.
1121
1122 buildLayerMaps( aBoard );
1123
1124 int layerCount = aBoard->GetCopperLayerCount();
1125
1126 for( int pcbNdx=0; pcbNdx<layerCount; ++pcbNdx )
1127 {
1128 LAYER* layer = new LAYER( m_pcb->m_structure );
1129
1130 m_pcb->m_structure->m_layers.push_back( layer );
1131
1132 layer->name = m_layerIds[pcbNdx];
1133
1134 DSN_T layerType;
1135
1136 switch( aBoard->GetLayerType( m_pcbLayer2kicad[pcbNdx] ) )
1137 {
1138 default:
1139 case LT_SIGNAL: layerType = T_signal; break;
1140 case LT_POWER: layerType = T_power; break;
1141
1142 // Freerouter does not support type "mixed", only signal and power.
1143 // Remap "mixed" to "signal".
1144 case LT_MIXED: layerType = T_signal; break;
1145 case LT_JUMPER: layerType = T_jumper; break;
1146 }
1147
1148 layer->layer_type = layerType;
1149
1150 layer->properties.push_back( PROPERTY() );
1151 PROPERTY* property = &layer->properties.back();
1152 property->name = "index";
1153 property->value = std::to_string( pcbNdx );
1154 }
1155 }
1156
1157 // a space in a quoted token is NOT a terminator, true establishes this.
1159
1160 //-----<unit_descriptor> & <resolution_descriptor>--------------------
1161 {
1162 // Tell freerouter to use "tenths of micrometers", which is 100 nm resolution. Possibly
1163 // more resolution is possible in freerouter, but it would need testing.
1164
1165 m_pcb->m_unit->units = T_um;
1166 m_pcb->m_resolution->units = T_um;
1167 m_pcb->m_resolution->value = 10; // tenths of a um
1168 }
1169
1170 //-----<boundary_descriptor>------------------------------------------
1171 {
1172 // Because fillBOUNDARY() can throw an exception, we link in an empty boundary so the
1173 // BOUNDARY does not get lost in the event of of an exception.
1174 BOUNDARY* boundary = new BOUNDARY( nullptr );
1175
1176 m_pcb->m_structure->SetBOUNDARY( boundary );
1177 fillBOUNDARY( aBoard, boundary );
1178 }
1179
1180 //-----<rules>--------------------------------------------------------
1181 {
1182 char rule[80];
1183 int defaultTrackWidth = netSettings->m_DefaultNetClass->GetTrackWidth();
1184 int defaultClearance = netSettings->m_DefaultNetClass->GetClearance();
1185 double clearance = scale( defaultClearance );
1186
1188
1189 std::snprintf( rule, sizeof( rule ), "(width %.6g)", scale( defaultTrackWidth ) );
1190 rules.push_back( rule );
1191
1192 std::snprintf( rule, sizeof( rule ), "(clearance %.6g)", clearance );
1193 rules.push_back( rule );
1194
1195 // On a high density board (4 mil tracks, 4 mil spacing) a typical solder mask clearance
1196 // will be 2-3 mils. This exposes 2 to 3 mils of bare board around each pad, and would
1197 // leave only 1 to 2 mils of solder mask between the solder mask's boundary and the edge of
1198 // any trace within "clearance" of the pad. So we need at least 2 mils *extra* clearance
1199 // for traces which would come near a pad on a different net. So if the baseline trace to
1200 // trace clearance was 4 mils, then the SMD to trace clearance should be at least 6 mils.
1201 double default_smd = clearance;
1202
1203 if( default_smd <= 6.0 )
1204 default_smd = 6.0;
1205
1206 std::snprintf( rule, sizeof( rule ), "(clearance %.6g (type default_smd))", default_smd );
1207
1208 rules.push_back( rule );
1209
1210 // Pad to pad spacing on a single SMT part can be closer than our clearance. We don't want
1211 // freerouter complaining about that, so output a significantly smaller pad to pad
1212 // clearance to freerouter.
1213 clearance = scale( defaultClearance ) / 4;
1214
1215 std::snprintf( rule, sizeof( rule ), "(clearance %.6g (type smd_smd))", clearance );
1216 rules.push_back( rule );
1217 }
1218
1219 //-----<zones (not keepout areas) become planes>--------------------------------
1220 // Note: only zones are output here, keepout areas are created later.
1221 {
1222 int netlessZones = 0;
1223
1224 for( ZONE* zone : aBoard->Zones() )
1225 {
1226 if( zone->GetIsRuleArea() )
1227 continue;
1228
1229 // Currently, we export only copper layers
1230 if( ! zone->IsOnCopperLayer() )
1231 continue;
1232
1233 // Now, build zone polygon on each copper layer where the zone
1234 // is living (zones can live on many copper layers)
1235 const int copperCount = aBoard->GetCopperLayerCount();
1236
1237 for( int layer = 0; layer < copperCount; layer++ )
1238 {
1239 if( layer == copperCount-1 )
1240 layer = B_Cu;
1241
1242 if( !zone->IsOnLayer( PCB_LAYER_ID( layer ) ) )
1243 continue;
1244
1245 COPPER_PLANE* plane = new COPPER_PLANE( m_pcb->m_structure );
1246
1247 m_pcb->m_structure->m_planes.push_back( plane );
1248
1249 PATH* mainPolygon = new PATH( plane, T_polygon );
1250
1251 plane->SetShape( mainPolygon );
1252 plane->m_name = TO_UTF8( zone->GetNetname() );
1253
1254 if( plane->m_name.size() == 0 )
1255 {
1256 // This is one of those no connection zones, netcode=0, and it has no name.
1257 // Create a unique, bogus netname.
1258 NET* no_net = new NET( m_pcb->m_network );
1259
1260
1261 no_net->m_net_id = "@:no_net_" + std::to_string( netlessZones++ );
1262
1263 // add the bogus net name to network->nets.
1264 m_pcb->m_network->m_nets.push_back( no_net );
1265
1266 // use the bogus net name in the netless zone.
1267 plane->m_name = no_net->m_net_id;
1268 }
1269
1270 mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
1271
1272 // Handle the main outlines
1273 SHAPE_POLY_SET::ITERATOR iterator;
1274 VECTOR2I startpoint;
1275 bool is_first_point = true;
1276
1277 for( iterator = zone->IterateWithHoles(); iterator; iterator++ )
1278 {
1279 VECTOR2I point( iterator->x, iterator->y );
1280
1281 if( is_first_point )
1282 {
1283 startpoint = point;
1284 is_first_point = false;
1285 }
1286
1287 mainPolygon->AppendPoint( mapPt( point ) );
1288
1289 // this was the end of the main polygon
1290 if( iterator.IsEndContour() )
1291 {
1292 // Close polygon
1293 mainPolygon->AppendPoint( mapPt( startpoint ) );
1294 break;
1295 }
1296 }
1297
1298 WINDOW* window = nullptr;
1299 PATH* cutout = nullptr;
1300
1301 bool isStartContour = true;
1302
1303 // handle the cutouts
1304 for( iterator++; iterator; iterator++ )
1305 {
1306 if( isStartContour )
1307 {
1308 is_first_point = true;
1309 window = new WINDOW( plane );
1310 plane->AddWindow( window );
1311
1312 cutout = new PATH( window, T_polygon );
1313 window->SetShape( cutout );
1314 cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
1315 }
1316
1317 // If the point in this iteration is the last of the contour, the next iteration
1318 // will start with a new contour.
1319 isStartContour = iterator.IsEndContour();
1320
1321 wxASSERT( window );
1322 wxASSERT( cutout );
1323
1324 VECTOR2I point( iterator->x, iterator->y );
1325
1326 if( is_first_point )
1327 {
1328 startpoint = point;
1329 is_first_point = false;
1330 }
1331
1332 cutout->AppendPoint( mapPt( point ) );
1333
1334 // Close the polygon
1335 if( iterator.IsEndContour() )
1336 cutout->AppendPoint( mapPt( startpoint ) );
1337 }
1338 } // end build zones by layer
1339 }
1340 }
1341
1342 //-----<zones flagged keepout areas become keepout>--------------------------------
1343 {
1344 for( ZONE* zone : aBoard->Zones() )
1345 {
1346 if( !zone->GetIsRuleArea() )
1347 continue;
1348
1349 // Keepout areas have a type: T_place_keepout, T_via_keepout, T_wire_keepout,
1350 // T_bend_keepout, T_elongate_keepout, T_keepout.
1351 // Pcbnew knows only T_keepout, T_via_keepout and T_wire_keepout
1352 DSN_T keepout_type;
1353
1354 if( zone->GetDoNotAllowVias() && zone->GetDoNotAllowTracks() )
1355 keepout_type = T_keepout;
1356 else if( zone->GetDoNotAllowVias() )
1357 keepout_type = T_via_keepout;
1358 else if( zone->GetDoNotAllowTracks() )
1359 keepout_type = T_wire_keepout;
1360 else
1361 keepout_type = T_keepout;
1362
1363 // Now, build keepout polygon on each copper layer where the zone
1364 // keepout is living (keepout zones can live on many copper layers)
1365 const int copperCount = aBoard->GetCopperLayerCount();
1366
1367 for( int layer = 0; layer < copperCount; layer++ )
1368 {
1369 if( layer == copperCount - 1 )
1370 layer = B_Cu;
1371
1372 if( !zone->IsOnLayer( PCB_LAYER_ID( layer ) ) )
1373 continue;
1374
1375 KEEPOUT* keepout = new KEEPOUT( m_pcb->m_structure, keepout_type );
1376 m_pcb->m_structure->m_keepouts.push_back( keepout );
1377
1378 PATH* mainPolygon = new PATH( keepout, T_polygon );
1379 keepout->SetShape( mainPolygon );
1380
1381 mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
1382
1383 // Handle the main outlines
1384 SHAPE_POLY_SET::ITERATOR iterator;
1385 bool is_first_point = true;
1386 VECTOR2I startpoint;
1387
1388 for( iterator = zone->IterateWithHoles(); iterator; iterator++ )
1389 {
1390 VECTOR2I point( iterator->x, iterator->y );
1391
1392 if( is_first_point )
1393 {
1394 startpoint = point;
1395 is_first_point = false;
1396 }
1397
1398 mainPolygon->AppendPoint( mapPt( point ) );
1399
1400 // this was the end of the main polygon
1401 if( iterator.IsEndContour() )
1402 {
1403 mainPolygon->AppendPoint( mapPt( startpoint ) );
1404 break;
1405 }
1406 }
1407
1408 WINDOW* window = nullptr;
1409 PATH* cutout = nullptr;
1410
1411 bool isStartContour = true;
1412
1413 // handle the cutouts
1414 for( iterator++; iterator; iterator++ )
1415 {
1416 if( isStartContour )
1417 {
1418 is_first_point = true;
1419 window = new WINDOW( keepout );
1420 keepout->AddWindow( window );
1421
1422 cutout = new PATH( window, T_polygon );
1423 window->SetShape( cutout );
1424 cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
1425 }
1426
1427 isStartContour = iterator.IsEndContour();
1428
1429 wxASSERT( window );
1430 wxASSERT( cutout );
1431
1432 VECTOR2I point( iterator->x, iterator->y );
1433
1434 if( is_first_point )
1435 {
1436 startpoint = point;
1437 is_first_point = false;
1438 }
1439
1440 cutout->AppendPoint( mapPt(point) );
1441
1442 // Close the polygon
1443 if( iterator.IsEndContour() )
1444 cutout->AppendPoint( mapPt( startpoint ) );
1445 }
1446 }
1447 }
1448 }
1449
1450 //-----<build the images, components, and netlist>-----------------------
1451 {
1453 std::string componentId;
1454 int highestNetCode = 0;
1455 const NETINFO_LIST& netInfo = aBoard->GetNetInfo();
1456
1457 // find the highest numbered netCode within the board.
1458 for( NETINFO_LIST::iterator i = netInfo.begin(); i != netInfo.end(); ++i )
1459 highestNetCode = std::max( highestNetCode, i->GetNetCode() );
1460
1461 deleteNETs();
1462
1463 // expand the net vector to highestNetCode+1, setting empty to NULL
1464 m_nets.resize( highestNetCode + 1, nullptr );
1465
1466 for( unsigned i = 1 /* skip "No Net" at [0] */; i < m_nets.size(); ++i )
1467 m_nets[i] = new NET( m_pcb->m_network );
1468
1469 for( NETINFO_LIST::iterator i = netInfo.begin(); i != netInfo.end(); ++i )
1470 {
1471 if( i->GetNetCode() > 0 )
1472 m_nets[i->GetNetCode()]->m_net_id = TO_UTF8( i->GetNetname() );
1473 }
1474
1475 m_padstackset.clear();
1476
1477 for( FOOTPRINT* footprint : aBoard->Footprints() )
1478 {
1479 IMAGE* image = makeIMAGE( aBoard, footprint );
1480
1481 componentId = TO_UTF8( footprint->GetReference() );
1482
1483 // Create a net list entry for all the actual pins in the current footprint.
1484 // Location of this code is critical because we fabricated some pin names to ensure
1485 // unique-ness within a footprint, and the life of this 'IMAGE* image' is not
1486 // necessarily long. The exported netlist will have some fabricated pin names in it.
1487 // If you don't like fabricated pin names, then make sure all pads within your
1488 // FOOTPRINTs are uniquely named!
1489 for( unsigned p = 0; p < image->m_pins.size(); ++p )
1490 {
1491 PIN* pin = &image->m_pins[p];
1492 int netcode = pin->m_kiNetCode;
1493
1494 if( netcode > 0 )
1495 {
1496 NET* net = m_nets[netcode];
1497
1498 net->m_pins.push_back( empty );
1499
1500 PIN_REF& pin_ref = net->m_pins.back();
1501
1502 pin_ref.component_id = componentId;
1503 pin_ref.pin_id = pin->m_pin_id;
1504 }
1505 }
1506
1508
1509 if( registered != image )
1510 {
1511 // If our new 'image' is not a unique IMAGE, delete it.
1512 // and use the registered one, known as 'image' after this.
1513 delete image;
1514 image = registered;
1515 }
1516
1517 COMPONENT* comp = m_pcb->m_placement->LookupCOMPONENT( image->GetImageId() );
1518 PLACE* place = new PLACE( comp );
1519
1520 comp->m_places.push_back( place );
1521
1522 place->SetRotation( footprint->GetOrientationDegrees() );
1523 place->SetVertex( mapPt( footprint->GetPosition() ) );
1524 place->m_component_id = componentId;
1525 place->m_part_number = TO_UTF8( footprint->GetValue() );
1526
1527 // footprint is flipped from bottom side, set side to T_back
1528 if( footprint->GetFlag() )
1529 {
1530 EDA_ANGLE angle = ANGLE_180 - footprint->GetOrientation();
1531 place->SetRotation( angle.Normalize().AsDegrees() );
1532
1533 place->m_side = T_back;
1534 }
1535 }
1536
1537 // copy the SPECCTRA_DB::padstackset to the LIBRARY. Since we are
1538 // removing, do not increment the iterator
1539 for( PADSTACKSET::iterator i = m_padstackset.begin(); i != m_padstackset.end();
1540 i = m_padstackset.begin() )
1541 {
1542 PADSTACKSET::auto_type ps = m_padstackset.release( i );
1543 PADSTACK* padstack = ps.release();
1544
1545 m_pcb->m_library->AddPadstack( padstack );
1546 }
1547
1548 // copy our SPECCTRA_DB::nets to the pcb->network
1549 for( unsigned n = 1; n < m_nets.size(); ++n )
1550 {
1551 NET* net = m_nets[n];
1552
1553 if( net->m_pins.size() )
1554 {
1555 // give ownership to pcb->network
1556 m_pcb->m_network->m_nets.push_back( net );
1557 m_nets[n] = nullptr;
1558 }
1559 }
1560 }
1561
1562 //-----< output vias used in netclasses >-----------------------------------
1563 {
1564 // Assume the netclass vias are all the same kind of thru, blind, or buried vias.
1565 // This is in lieu of either having each netclass via have its own layer pair in
1566 // the netclass dialog, or such control in the specctra export dialog.
1567
1568 m_top_via_layer = 0; // first specctra cu layer is number zero.
1570
1571 // Add the via from the Default netclass first. The via container
1572 // in pcb->library preserves the sequence of addition.
1573
1574 PADSTACK* via = makeVia( netSettings->m_DefaultNetClass->GetViaDiameter(),
1575 netSettings->m_DefaultNetClass->GetViaDrill(),
1577
1578 // we AppendVia() this first one, there is no way it can be a duplicate,
1579 // the pcb->library via container is empty at this point. After this,
1580 // we'll have to use LookupVia().
1581 wxASSERT( m_pcb->m_library->m_vias.size() == 0 );
1583
1584 // set the "spare via" index at the start of the
1585 // pcb->library->spareViaIndex = pcb->library->vias.size();
1586
1587 // output the non-Default netclass vias
1588 for( const auto& [ name, netclass ] : netSettings->m_NetClasses )
1589 {
1590 via = makeVia( netclass->GetViaDiameter(), netclass->GetViaDrill(),
1592
1593 // maybe add 'via' to the library, but only if unique.
1595
1596 if( registered != via )
1597 delete via;
1598 }
1599 }
1600
1601 //-----<create the wires from tracks>-----------------------------------
1602 {
1603 // export all of them for now, later we'll decide what controls we need on this.
1604 std::string netname;
1605 WIRING* wiring = m_pcb->m_wiring;
1606 PATH* path = nullptr;
1607
1608 int old_netcode = -1;
1609 int old_width = -1;
1610 int old_layer = UNDEFINED_LAYER;
1611
1612 for( PCB_TRACK* track : aBoard->Tracks() )
1613 {
1614 if( !track->IsType( { PCB_TRACE_T, PCB_ARC_T } ) )
1615 continue;
1616
1617 int netcode = track->GetNetCode();
1618
1619 if( netcode == 0 )
1620 continue;
1621
1622 if( old_netcode != netcode
1623 || old_width != track->GetWidth()
1624 || old_layer != track->GetLayer()
1625 || ( path && path->points.back() != mapPt( track->GetStart() ) ) )
1626 {
1627 old_width = track->GetWidth();
1628 old_layer = track->GetLayer();
1629
1630 if( old_netcode != netcode )
1631 {
1632 old_netcode = netcode;
1633 NETINFO_ITEM* net = aBoard->FindNet( netcode );
1634 wxASSERT( net );
1635 netname = TO_UTF8( net->GetNetname() );
1636 }
1637
1638 WIRE* wire = new WIRE( wiring );
1639
1640 wiring->wires.push_back( wire );
1641 wire->m_net_id = netname;
1642
1643 if( track->IsLocked() )
1644 wire->m_wire_type = T_fix; // tracks with fix property are not returned in .ses files
1645 else
1646 wire->m_wire_type = T_route; // could be T_protect
1647
1648 int kiLayer = track->GetLayer();
1649 int pcbLayer = m_kicadLayer2pcb[kiLayer];
1650
1651 path = new PATH( wire );
1652 wire->SetShape( path );
1653 path->layer_id = m_layerIds[pcbLayer];
1654 path->aperture_width = scale( old_width );
1655 path->AppendPoint( mapPt( track->GetStart() ) );
1656 }
1657
1658 if( path ) // Should not occur
1659 path->AppendPoint( mapPt( track->GetEnd() ) );
1660 }
1661 }
1662
1663 //-----<export the existing real BOARD instantiated vias>-----------------
1664 {
1665 // Export all vias, once per unique size and drill diameter combo.
1666 for( PCB_TRACK* track : aBoard->Tracks() )
1667 {
1668 if( track->Type() != PCB_VIA_T )
1669 continue;
1670
1671 PCB_VIA* via = static_cast<PCB_VIA*>( track );
1672 int netcode = via->GetNetCode();
1673
1674 if( netcode == 0 )
1675 continue;
1676
1677 PADSTACK* padstack = makeVia( via );
1678 PADSTACK* registered = m_pcb->m_library->LookupVia( padstack );
1679
1680 // if the one looked up is not our padstack, then delete our padstack
1681 // since it was a duplicate of one already registered.
1682 if( padstack != registered )
1683 delete padstack;
1684
1685 WIRE_VIA* dsnVia = new WIRE_VIA( m_pcb->m_wiring );
1686
1687 m_pcb->m_wiring->wire_vias.push_back( dsnVia );
1688
1689 dsnVia->m_padstack_id = registered->m_padstack_id;
1690 dsnVia->m_vertexes.push_back( mapPt( via->GetPosition() ) );
1691
1692 NETINFO_ITEM* net = aBoard->FindNet( netcode );
1693 wxASSERT( net );
1694
1695 dsnVia->m_net_id = TO_UTF8( net->GetNetname() );
1696
1697 if( via->IsLocked() )
1698 dsnVia->m_via_type = T_fix; // vias with fix property are not returned in .ses files
1699 else
1700 dsnVia->m_via_type = T_route; // could be T_protect
1701 }
1702 }
1703
1704 //-----<via_descriptor>-------------------------------------------------
1705 {
1706 // The pcb->library will output <padstack_descriptors> which is a combined list of part
1707 // padstacks and via padstacks. specctra dsn uses the <via_descriptors> to say which of
1708 // those padstacks are vias.
1709
1710 // Output the vias in the padstack list here, by name only. This must be done after
1711 // exporting existing vias as WIRE_VIAs.
1712 VIA* vias = m_pcb->m_structure->m_via;
1713
1714 for( unsigned viaNdx = 0; viaNdx < m_pcb->m_library->m_vias.size(); ++viaNdx )
1715 vias->AppendVia( m_pcb->m_library->m_vias[viaNdx].m_padstack_id.c_str() );
1716 }
1717
1718 //-----<output NETCLASSs>----------------------------------------------------
1719
1720 exportNETCLASS( netSettings->m_DefaultNetClass, aBoard );
1721
1722 for( const auto& [ name, netclass ] : netSettings->m_NetClasses )
1723 exportNETCLASS( netclass, aBoard );
1724}
1725
1726
1727void SPECCTRA_DB::exportNETCLASS( const std::shared_ptr<NETCLASS>& aNetClass, BOARD* aBoard )
1728{
1729 /* From page 11 of specctra spec:
1730 *
1731 * Routing and Placement Rule Hierarchies
1732 *
1733 * Routing and placement rules can be defined at multiple levels of design
1734 * specification. When a routing or placement rule is defined for an object at
1735 * multiple levels, a predefined routing or placement precedence order
1736 * automatically determines which rule to apply to the object. The routing rule
1737 * precedence order is
1738 *
1739 * pcb < layer < class < class layer < group_set < group_set layer < net <
1740 * net layer < group < group layer < fromto < fromto layer < class_class <
1741 * class_class layer < padstack < region < class region < net region <
1742 * class_class region
1743 *
1744 * A pcb rule (global rule for the PCB design) has the lowest precedence in the
1745 * hierarchy. A class-to-class region rule has the highest precedence. Rules
1746 * set at one level of the hierarchy override conflicting rules set at lower
1747 * levels. The placement rule precedence order is
1748 *
1749 * pcb < image_set < image < component < super cluster < room <
1750 * room_image_set < family_family < image_image
1751 *
1752 * A pcb rule (global rule for the PCB design) has the lowest precedence in the
1753 * hierarchy. An image-to-image rule has the highest precedence. Rules set at
1754 * one level of the hierarchy override conflicting rules set at lower levels.
1755 */
1756
1757 char text[256];
1758
1759 CLASS* clazz = new CLASS( m_pcb->m_network );
1760
1761 m_pcb->m_network->m_classes.push_back( clazz );
1762
1763 // Freerouter creates a class named 'default' anyway, and if we try to use that we end up
1764 // with two 'default' via rules so use something else as the name of our default class.
1765 clazz->m_class_id = TO_UTF8( aNetClass->GetName() );
1766
1767 for( NETINFO_ITEM* net : aBoard->GetNetInfo() )
1768 {
1769 if( net->GetNetClass()->GetName() == clazz->m_class_id )
1770 clazz->m_net_ids.push_back( TO_UTF8( net->GetNetname() ) );
1771 }
1772
1773 clazz->m_rules = new RULE( clazz, T_rule );
1774
1775 // output the track width.
1776 int trackWidth = aNetClass->GetTrackWidth();
1777 std::snprintf( text, sizeof( text ), "(width %.6g)", scale( trackWidth ) );
1778 clazz->m_rules->m_rules.push_back( text );
1779
1780 // output the clearance.
1781 int clearance = aNetClass->GetClearance();
1782 std::snprintf( text, sizeof( text ), "(clearance %.6g)", scale( clearance ) );
1783 clazz->m_rules->m_rules.push_back( text );
1784
1785 if( aNetClass->GetName() == NETCLASS::Default )
1786 clazz->m_class_id = "kicad_default";
1787
1788 // The easiest way to get the via name is to create a temporary via (which generates the
1789 // name internal to the PADSTACK), and then grab the name and delete the via. There are not
1790 // that many netclasses so this should never become a performance issue.
1791
1792 PADSTACK* via = makeVia( aNetClass->GetViaDiameter(), aNetClass->GetViaDrill(),
1794
1795 snprintf( text, sizeof(text), "(use_via %s)", via->GetPadstackId().c_str() );
1796 clazz->m_circuit.push_back( text );
1797
1798 delete via;
1799}
1800
1801
1803{
1804 // DSN Images (=KiCad FOOTPRINTs and PADs) must be presented from the top view.
1805 // Note: to export footprints, the footprints must be flipped around the X axis, otherwise
1806 // the rotation angle is not good.
1807 for( FOOTPRINT* footprint : aBoard->Footprints() )
1808 {
1809 footprint->SetFlag( 0 );
1810
1811 if( footprint->GetLayer() == B_Cu )
1812 {
1813 footprint->Flip( footprint->GetPosition(), false );
1814 footprint->SetFlag( 1 );
1815 }
1816 }
1817
1819}
1820
1821
1823{
1825 return;
1826
1827 // DSN Images (=KiCad FOOTPRINTs and PADs) must be presented from the
1828 // top view. Restore those that were flipped.
1829 // Note: to export footprints, the footprints were flipped around the X axis,
1830 for( FOOTPRINT* footprint : aBoard->Footprints() )
1831 {
1832 if( footprint->GetFlag() )
1833 {
1834 footprint->Flip( footprint->GetPosition(), false );
1835 footprint->SetFlag( 0 );
1836 }
1837 }
1838
1839 m_footprintsAreFlipped = false;
1840}
1841
1842} // 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:155
@ LT_MIXED
Definition: board.h:156
@ LT_JUMPER
Definition: board.h:157
@ LT_SIGNAL
Definition: board.h:154
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
bool any() const
Definition: base_set.h:55
std::shared_ptr< NET_SETTINGS > m_NetSettings
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:289
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:2473
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:864
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1900
const ZONES & Zones() const
Definition: board.h:334
LAYER_T GetLayerType(PCB_LAYER_ID aLayer) const
Return the type of the copper layer given by aLayer.
Definition: board.cpp:610
void SynchronizeNetsAndNetClasses(bool aResetTrackAndViaSizes)
Copy NETCLASS info to each NET, based on NET membership in a NETCLASS.
Definition: board.cpp:2033
int GetCopperLayerCount() const
Definition: board.cpp:733
const FOOTPRINTS & Footprints() const
Definition: board.h:330
const TRACKS & Tracks() const
Definition: board.h:328
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:874
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:221
double AsDegrees() const
Definition: eda_angle.h:113
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:682
int GetRadius() const
Definition: eda_shape.cpp:612
SHAPE_T GetShape() const
Definition: eda_shape.h:125
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:167
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:130
EDA_ANGLE GetOrientation() const
Definition: footprint.h:216
ZONES & Zones()
Definition: footprint.h:201
const LIB_ID & GetFPID() const
Definition: footprint.h:237
VECTOR2I GetPosition() const override
Definition: footprint.h:213
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: lset.h:35
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:732
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:407
Container for NETINFO_ITEM elements, which are the nets.
Definition: netinfo.h:346
iterator begin() const
Definition: netinfo.h:452
iterator end() const
Definition: netinfo.h:457
Definition: pad.h:54
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pad.h:392
const BOX2I GetBoundingBox() const override
The bounding box is cached, so this will be efficient most of the time.
Definition: pad.cpp:736
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pad.h:698
const VECTOR2I & GetDrillSize() const
Definition: pad.h:267
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:425
const VECTOR2I & GetOffset() const
Definition: pad.h:274
const VECTOR2I & GetDelta() const
Definition: pad.h:264
PAD_SHAPE GetShape() const
Definition: pad.h:193
int GetChamferPositions() const
Definition: pad.h:624
const VECTOR2I & GetSize() const
Definition: pad.h:256
double GetChamferRectRatio() const
Definition: pad.h:614
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:369
int GetWidth() const
Definition: pcb_track.h:116
Collect all BOARD_ITEM objects of a given set of KICAD_T type(s).
Definition: collectors.h:524
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:544
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:960
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.
HASH_128 GetHash() const
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
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:73
SHAPE_POLY_SET::ITERATOR IterateWithHoles()
Return an iterator to visit all points of the zone's main outline with holes.
Definition: zone.h:535
void Rotate(const VECTOR2I &aCentre, const EDA_ANGLE &aAngle) override
Rotate the outlines.
Definition: zone.cpp:715
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:195
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:401
static constexpr EDA_ANGLE ANGLE_360
Definition: eda_angle.h:407
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:405
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:390
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
A storage class for 128-bit hash value.
Definition: hash_128.h:36
std::string ToString() const
Definition: hash_128.h:47
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:121
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:673