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