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 The 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> // DisplayErrorMessage()
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();
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 LSET layerset = aBoard->GetEnabledLayers() & zone->GetLayerSet() & LSET::AllCuMask();
893
894 for( PCB_LAYER_ID layer : layerset.CuStack() )
895 {
896 KEEPOUT* keepout = new KEEPOUT( m_pcb->m_structure, keepout_type );
897 image->m_keepouts.push_back( keepout );
898
899 PATH* mainPolygon = new PATH( keepout, T_polygon );
900 keepout->SetShape( mainPolygon );
901
902 mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
903
904 // Handle the main outlines
906 bool is_first_point = true;
907 VECTOR2I startpoint;
908
909 for( iterator = untransformedZone.IterateWithHoles(); iterator; iterator++ )
910 {
911 VECTOR2I point( iterator->x, iterator->y );
912
913 point -= aFootprint->GetPosition();
914
915 if( is_first_point )
916 {
917 startpoint = point;
918 is_first_point = false;
919 }
920
921 mainPolygon->AppendPoint( mapPt( point ) );
922
923 // this was the end of the main polygon
924 if( iterator.IsEndContour() )
925 {
926 mainPolygon->AppendPoint( mapPt( startpoint ) );
927 break;
928 }
929 }
930
931 WINDOW* window = nullptr;
932 PATH* cutout = nullptr;
933 bool isStartContour = true;
934
935 // handle the cutouts
936 for( iterator++; iterator; iterator++ )
937 {
938 if( isStartContour )
939 {
940 is_first_point = true;
941 window = new WINDOW( keepout );
942 keepout->AddWindow( window );
943
944 cutout = new PATH( window, T_polygon );
945
946 window->SetShape( cutout );
947
948 cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ zone->GetLayer() ] ];
949 }
950
951 isStartContour = iterator.IsEndContour();
952
953 wxASSERT( window );
954 wxASSERT( cutout );
955
956 VECTOR2I point( iterator->x, iterator->y );
957
958 point -= aFootprint->GetPosition();
959
960 if( is_first_point )
961 {
962 startpoint = point;
963 is_first_point = false;
964 }
965
966 cutout->AppendPoint( mapPt( point ) );
967
968 // Close the polygon
969 if( iterator.IsEndContour() )
970 cutout->AppendPoint( mapPt( startpoint ) );
971 }
972 }
973 }
974
975 return image;
976}
977
978
979PADSTACK* SPECCTRA_DB::makeVia( int aCopperDiameter, int aDrillDiameter,
980 int aTopLayer, int aBotLayer )
981{
982 char name[48];
983 PADSTACK* padstack = new PADSTACK();
984 double dsnDiameter = scale( aCopperDiameter );
985
986 for( int layer=aTopLayer; layer<=aBotLayer; ++layer )
987 {
988 SHAPE* shape = new SHAPE( padstack );
989
990 padstack->Append( shape );
991
992 CIRCLE* circle = new CIRCLE( shape );
993
994 shape->SetShape( circle );
995
996 circle->SetDiameter( dsnDiameter );
997 circle->SetLayerId( m_layerIds[layer] );
998 }
999
1000 snprintf( name, sizeof( name ), "Via[%d-%d]_%.6g:%.6g_um",
1001 aTopLayer, aBotLayer, dsnDiameter,
1002 // encode the drill value into the name for later import
1003 IU2um( aDrillDiameter ) );
1004
1005 name[ sizeof(name) - 1 ] = 0;
1006 padstack->SetPadstackId( name );
1007
1008 return padstack;
1009}
1010
1011
1013{
1014 PCB_LAYER_ID topLayerNum;
1015 PCB_LAYER_ID botLayerNum;
1016
1017 aVia->LayerPair( &topLayerNum, &botLayerNum );
1018
1019 int topLayer = m_kicadLayer2pcb[topLayerNum];
1020 int botLayer = m_kicadLayer2pcb[botLayerNum];
1021
1022 if( topLayer > botLayer )
1023 std::swap( topLayer, botLayer );
1024
1025 // TODO(JE) padstacks
1026 return makeVia( aVia->GetWidth( ::PADSTACK::ALL_LAYERS ), aVia->GetDrillValue(),
1027 topLayer, botLayer );
1028}
1029
1030
1031void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
1032{
1033 for( int cnt = 0; cnt < m_brd_outlines.OutlineCount(); cnt++ ) // Should be one outline
1034 {
1035 PATH* path = new PATH( boundary );
1036 boundary->paths.push_back( path );
1037 path->layer_id = "pcb";
1038
1039 SHAPE_LINE_CHAIN& outline = m_brd_outlines.Outline( cnt );
1040
1041 for( int ii = 0; ii < outline.PointCount(); ii++ )
1042 {
1043 VECTOR2I pos( outline.CPoint( ii ).x, outline.CPoint( ii ).y );
1044 path->AppendPoint( mapPt( pos ) );
1045 }
1046
1047 // Close polygon:
1048 VECTOR2I pos0( outline.CPoint( 0 ).x, outline.CPoint( 0 ).y );
1049 path->AppendPoint( mapPt( pos0 ) );
1050
1051 // Generate holes as keepout:
1052 for( int ii = 0; ii < m_brd_outlines.HoleCount( cnt ); ii++ )
1053 {
1054 // emit a signal layers keepout for every interior polygon left...
1055 KEEPOUT* keepout = new KEEPOUT( nullptr, T_keepout );
1056 PATH* poly_ko = new PATH( nullptr, T_polygon );
1057
1058 keepout->SetShape( poly_ko );
1059 poly_ko->SetLayerId( "signal" );
1060 m_pcb->m_structure->m_keepouts.push_back( keepout );
1061
1062 SHAPE_LINE_CHAIN& hole = m_brd_outlines.Hole( cnt, ii );
1063
1064 for( int jj = 0; jj < hole.PointCount(); jj++ )
1065 {
1066 VECTOR2I pos( hole.CPoint( jj ).x, hole.CPoint( jj ).y );
1067 poly_ko->AppendPoint( mapPt( pos ) );
1068 }
1069
1070 // Close polygon:
1071 VECTOR2I pos( hole.CPoint( 0 ).x, hole.CPoint( 0 ).y );
1072 poly_ko->AppendPoint( mapPt( pos ) );
1073 }
1074 }
1075}
1076
1077
1078typedef std::set<std::string> STRINGSET;
1079typedef std::pair<STRINGSET::iterator, bool> STRINGSET_PAIR;
1080
1081
1083{
1084 std::shared_ptr<NET_SETTINGS>& netSettings = aBoard->GetDesignSettings().m_NetSettings;
1085
1086 // Not all boards are exportable. Check that all reference Ids are unique, or we won't be
1087 // able to import the session file which comes back to us later from the router.
1088 {
1089 STRINGSET refs; // holds footprint reference designators
1090
1091 for( FOOTPRINT* footprint : aBoard->Footprints() )
1092 {
1093 if( footprint->GetReference() == wxEmptyString )
1094 {
1095 THROW_IO_ERROR( wxString::Format( _( "Footprint with value of '%s' has an empty "
1096 "reference designator." ),
1097 footprint->GetValue() ) );
1098 }
1099
1100 // if we cannot insert OK, that means the reference has been seen before.
1101 STRINGSET_PAIR refpair = refs.insert( TO_UTF8( footprint->GetReference() ) );
1102
1103 if( !refpair.second ) // insert failed
1104 {
1105 THROW_IO_ERROR( wxString::Format( _( "Multiple footprints have the reference "
1106 "designator '%s'." ),
1107 footprint->GetReference() ) );
1108 }
1109 }
1110 }
1111
1112 if( !m_pcb )
1114
1115 //-----<layer_descriptor>-----------------------------------------------
1116 {
1117 // Specctra wants top physical layer first, then going down to the bottom most physical
1118 // layer in physical sequence.
1119
1120 buildLayerMaps( aBoard );
1121
1122 int layerCount = aBoard->GetCopperLayerCount();
1123
1124 for( int pcbNdx=0; pcbNdx<layerCount; ++pcbNdx )
1125 {
1126 LAYER* layer = new LAYER( m_pcb->m_structure );
1127
1128 m_pcb->m_structure->m_layers.push_back( layer );
1129
1130 layer->name = m_layerIds[pcbNdx];
1131
1132 DSN_T layerType;
1133
1134 switch( aBoard->GetLayerType( m_pcbLayer2kicad[pcbNdx] ) )
1135 {
1136 default:
1137 case LT_SIGNAL: layerType = T_signal; break;
1138 case LT_POWER: layerType = T_power; break;
1139
1140 // Freerouter does not support type "mixed", only signal and power.
1141 // Remap "mixed" to "signal".
1142 case LT_MIXED: layerType = T_signal; break;
1143 case LT_JUMPER: layerType = T_jumper; break;
1144 }
1145
1146 layer->layer_type = layerType;
1147
1148 layer->properties.push_back( PROPERTY() );
1149 PROPERTY* property = &layer->properties.back();
1150 property->name = "index";
1151 property->value = std::to_string( pcbNdx );
1152 }
1153 }
1154
1155 // a space in a quoted token is NOT a terminator, true establishes this.
1157
1158 //-----<unit_descriptor> & <resolution_descriptor>--------------------
1159 {
1160 // Tell freerouter to use "tenths of micrometers", which is 100 nm resolution. Possibly
1161 // more resolution is possible in freerouter, but it would need testing.
1162
1163 m_pcb->m_unit->units = T_um;
1164 m_pcb->m_resolution->units = T_um;
1165 m_pcb->m_resolution->value = 10; // tenths of a um
1166 }
1167
1168 //-----<boundary_descriptor>------------------------------------------
1169 {
1170 // Because fillBOUNDARY() can throw an exception, we link in an empty boundary so the
1171 // BOUNDARY does not get lost in the event of of an exception.
1172 BOUNDARY* boundary = new BOUNDARY( nullptr );
1173
1174 m_pcb->m_structure->SetBOUNDARY( boundary );
1175 fillBOUNDARY( aBoard, boundary );
1176 }
1177
1178 //-----<rules>--------------------------------------------------------
1179 {
1180 char rule[80];
1181 int defaultTrackWidth = netSettings->GetDefaultNetclass()->GetTrackWidth();
1182 int defaultClearance = netSettings->GetDefaultNetclass()->GetClearance();
1183 double clearance = scale( defaultClearance );
1184
1186
1187 std::snprintf( rule, sizeof( rule ), "(width %.6g)", scale( defaultTrackWidth ) );
1188 rules.push_back( rule );
1189
1190 std::snprintf( rule, sizeof( rule ), "(clearance %.6g)", clearance );
1191 rules.push_back( rule );
1192
1193 // Pad to pad spacing on a single SMT part can be closer than our clearance. We don't want
1194 // freerouter complaining about that, so output a significantly smaller pad to pad
1195 // clearance to freerouter.
1196 clearance = scale( defaultClearance ) / 4;
1197
1198 std::snprintf( rule, sizeof( rule ), "(clearance %.6g (type smd_smd))", clearance );
1199 rules.push_back( rule );
1200 }
1201
1202 //-----<zones (not keepout areas) become planes>--------------------------------
1203 // Note: only zones are output here, keepout areas are created later.
1204 {
1205 int netlessZones = 0;
1206
1207 for( ZONE* zone : aBoard->Zones() )
1208 {
1209 if( zone->GetIsRuleArea() )
1210 continue;
1211
1212 // Currently, we export only copper layers
1213 if( ! zone->IsOnCopperLayer() )
1214 continue;
1215
1216 // Now, build zone polygon on each copper layer where the zone
1217 // is living (zones can live on many copper layers)
1218 LSET layerset = aBoard->GetEnabledLayers() & zone->GetLayerSet() & LSET::AllCuMask();
1219
1220 for( PCB_LAYER_ID layer : layerset )
1221 {
1222 COPPER_PLANE* plane = new COPPER_PLANE( m_pcb->m_structure );
1223
1224 m_pcb->m_structure->m_planes.push_back( plane );
1225
1226 PATH* mainPolygon = new PATH( plane, T_polygon );
1227
1228 plane->SetShape( mainPolygon );
1229 plane->m_name = TO_UTF8( zone->GetNetname() );
1230
1231 if( plane->m_name.size() == 0 )
1232 {
1233 // This is one of those no connection zones, netcode=0, and it has no name.
1234 // Create a unique, bogus netname.
1235 NET* no_net = new NET( m_pcb->m_network );
1236
1237
1238 no_net->m_net_id = "@:no_net_" + std::to_string( netlessZones++ );
1239
1240 // add the bogus net name to network->nets.
1241 m_pcb->m_network->m_nets.push_back( no_net );
1242
1243 // use the bogus net name in the netless zone.
1244 plane->m_name = no_net->m_net_id;
1245 }
1246
1247 mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
1248
1249 // Handle the main outlines
1250 SHAPE_POLY_SET::ITERATOR iterator;
1251 VECTOR2I startpoint;
1252 bool is_first_point = true;
1253
1254 for( iterator = zone->IterateWithHoles(); iterator; iterator++ )
1255 {
1256 VECTOR2I point( iterator->x, iterator->y );
1257
1258 if( is_first_point )
1259 {
1260 startpoint = point;
1261 is_first_point = false;
1262 }
1263
1264 mainPolygon->AppendPoint( mapPt( point ) );
1265
1266 // this was the end of the main polygon
1267 if( iterator.IsEndContour() )
1268 {
1269 // Close polygon
1270 mainPolygon->AppendPoint( mapPt( startpoint ) );
1271 break;
1272 }
1273 }
1274
1275 WINDOW* window = nullptr;
1276 PATH* cutout = nullptr;
1277
1278 bool isStartContour = true;
1279
1280 // handle the cutouts
1281 for( iterator++; iterator; iterator++ )
1282 {
1283 if( isStartContour )
1284 {
1285 is_first_point = true;
1286 window = new WINDOW( plane );
1287 plane->AddWindow( window );
1288
1289 cutout = new PATH( window, T_polygon );
1290 window->SetShape( cutout );
1291 cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
1292 }
1293
1294 // If the point in this iteration is the last of the contour, the next iteration
1295 // will start with a new contour.
1296 isStartContour = iterator.IsEndContour();
1297
1298 wxASSERT( window );
1299 wxASSERT( cutout );
1300
1301 VECTOR2I point( iterator->x, iterator->y );
1302
1303 if( is_first_point )
1304 {
1305 startpoint = point;
1306 is_first_point = false;
1307 }
1308
1309 cutout->AppendPoint( mapPt( point ) );
1310
1311 // Close the polygon
1312 if( iterator.IsEndContour() )
1313 cutout->AppendPoint( mapPt( startpoint ) );
1314 }
1315 } // end build zones by layer
1316 }
1317 }
1318
1319 //-----<zones flagged keepout areas become keepout>--------------------------------
1320 {
1321 for( ZONE* zone : aBoard->Zones() )
1322 {
1323 if( !zone->GetIsRuleArea() )
1324 continue;
1325
1326 // Keepout areas have a type: T_place_keepout, T_via_keepout, T_wire_keepout,
1327 // T_bend_keepout, T_elongate_keepout, T_keepout.
1328 // Pcbnew knows only T_keepout, T_via_keepout and T_wire_keepout
1329 DSN_T keepout_type;
1330
1331 if( zone->GetDoNotAllowVias() && zone->GetDoNotAllowTracks() )
1332 keepout_type = T_keepout;
1333 else if( zone->GetDoNotAllowVias() )
1334 keepout_type = T_via_keepout;
1335 else if( zone->GetDoNotAllowTracks() )
1336 keepout_type = T_wire_keepout;
1337 else
1338 keepout_type = T_keepout;
1339
1340 // Now, build keepout polygon on each copper layer where the zone
1341 // keepout is living (keepout zones can live on many copper layers)
1342 LSET layerset = aBoard->GetEnabledLayers() & zone->GetLayerSet() & LSET::AllCuMask();
1343
1344 for( PCB_LAYER_ID layer : layerset )
1345 {
1346 KEEPOUT* keepout = new KEEPOUT( m_pcb->m_structure, keepout_type );
1347 m_pcb->m_structure->m_keepouts.push_back( keepout );
1348
1349 PATH* mainPolygon = new PATH( keepout, T_polygon );
1350 keepout->SetShape( mainPolygon );
1351
1352 mainPolygon->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
1353
1354 // Handle the main outlines
1355 SHAPE_POLY_SET::ITERATOR iterator;
1356 bool is_first_point = true;
1357 VECTOR2I startpoint;
1358
1359 for( iterator = zone->IterateWithHoles(); iterator; iterator++ )
1360 {
1361 VECTOR2I point( iterator->x, iterator->y );
1362
1363 if( is_first_point )
1364 {
1365 startpoint = point;
1366 is_first_point = false;
1367 }
1368
1369 mainPolygon->AppendPoint( mapPt( point ) );
1370
1371 // this was the end of the main polygon
1372 if( iterator.IsEndContour() )
1373 {
1374 mainPolygon->AppendPoint( mapPt( startpoint ) );
1375 break;
1376 }
1377 }
1378
1379 WINDOW* window = nullptr;
1380 PATH* cutout = nullptr;
1381
1382 bool isStartContour = true;
1383
1384 // handle the cutouts
1385 for( iterator++; iterator; iterator++ )
1386 {
1387 if( isStartContour )
1388 {
1389 is_first_point = true;
1390 window = new WINDOW( keepout );
1391 keepout->AddWindow( window );
1392
1393 cutout = new PATH( window, T_polygon );
1394 window->SetShape( cutout );
1395 cutout->layer_id = m_layerIds[ m_kicadLayer2pcb[ layer ] ];
1396 }
1397
1398 isStartContour = iterator.IsEndContour();
1399
1400 wxASSERT( window );
1401 wxASSERT( cutout );
1402
1403 VECTOR2I point( iterator->x, iterator->y );
1404
1405 if( is_first_point )
1406 {
1407 startpoint = point;
1408 is_first_point = false;
1409 }
1410
1411 cutout->AppendPoint( mapPt(point) );
1412
1413 // Close the polygon
1414 if( iterator.IsEndContour() )
1415 cutout->AppendPoint( mapPt( startpoint ) );
1416 }
1417 }
1418 }
1419 }
1420
1421 //-----<build the images, components, and netlist>-----------------------
1422 {
1424 std::string componentId;
1425 int highestNetCode = 0;
1426 const NETINFO_LIST& netInfo = aBoard->GetNetInfo();
1427
1428 // find the highest numbered netCode within the board.
1429 for( NETINFO_LIST::iterator i = netInfo.begin(); i != netInfo.end(); ++i )
1430 highestNetCode = std::max( highestNetCode, i->GetNetCode() );
1431
1432 deleteNETs();
1433
1434 // expand the net vector to highestNetCode+1, setting empty to NULL
1435 m_nets.resize( highestNetCode + 1, nullptr );
1436
1437 for( unsigned i = 1 /* skip "No Net" at [0] */; i < m_nets.size(); ++i )
1438 m_nets[i] = new NET( m_pcb->m_network );
1439
1440 for( NETINFO_LIST::iterator i = netInfo.begin(); i != netInfo.end(); ++i )
1441 {
1442 if( i->GetNetCode() > 0 )
1443 m_nets[i->GetNetCode()]->m_net_id = TO_UTF8( i->GetNetname() );
1444 }
1445
1446 m_padstackset.clear();
1447
1448 for( FOOTPRINT* footprint : aBoard->Footprints() )
1449 {
1450 IMAGE* image = makeIMAGE( aBoard, footprint );
1451
1452 componentId = TO_UTF8( footprint->GetReference() );
1453
1454 // Create a net list entry for all the actual pins in the current footprint.
1455 // Location of this code is critical because we fabricated some pin names to ensure
1456 // unique-ness within a footprint, and the life of this 'IMAGE* image' is not
1457 // necessarily long. The exported netlist will have some fabricated pin names in it.
1458 // If you don't like fabricated pin names, then make sure all pads within your
1459 // FOOTPRINTs are uniquely named!
1460 for( unsigned p = 0; p < image->m_pins.size(); ++p )
1461 {
1462 PIN* pin = &image->m_pins[p];
1463 int netcode = pin->m_kiNetCode;
1464
1465 if( netcode > 0 )
1466 {
1467 NET* net = m_nets[netcode];
1468
1469 net->m_pins.push_back( empty );
1470
1471 PIN_REF& pin_ref = net->m_pins.back();
1472
1473 pin_ref.component_id = componentId;
1474 pin_ref.pin_id = pin->m_pin_id;
1475 }
1476 }
1477
1479
1480 if( registered != image )
1481 {
1482 // If our new 'image' is not a unique IMAGE, delete it.
1483 // and use the registered one, known as 'image' after this.
1484 delete image;
1485 image = registered;
1486 }
1487
1488 COMPONENT* comp = m_pcb->m_placement->LookupCOMPONENT( image->GetImageId() );
1489 PLACE* place = new PLACE( comp );
1490
1491 comp->m_places.push_back( place );
1492
1493 place->SetRotation( footprint->GetOrientationDegrees() );
1494 place->SetVertex( mapPt( footprint->GetPosition() ) );
1495 place->m_component_id = componentId;
1496 place->m_part_number = TO_UTF8( footprint->GetValue() );
1497
1498 // footprint is flipped from bottom side, set side to T_back
1499 if( footprint->GetFlag() )
1500 {
1501 EDA_ANGLE angle = ANGLE_180 - footprint->GetOrientation();
1502 place->SetRotation( angle.Normalize().AsDegrees() );
1503
1504 place->m_side = T_back;
1505 }
1506 }
1507
1508 // copy the SPECCTRA_DB::padstackset to the LIBRARY. Since we are
1509 // removing, do not increment the iterator
1510 for( PADSTACKSET::iterator i = m_padstackset.begin(); i != m_padstackset.end();
1511 i = m_padstackset.begin() )
1512 {
1513 PADSTACKSET::auto_type ps = m_padstackset.release( i );
1514 PADSTACK* padstack = ps.release();
1515
1516 m_pcb->m_library->AddPadstack( padstack );
1517 }
1518
1519 // copy our SPECCTRA_DB::nets to the pcb->network
1520 for( unsigned n = 1; n < m_nets.size(); ++n )
1521 {
1522 NET* net = m_nets[n];
1523
1524 if( net->m_pins.size() )
1525 {
1526 // give ownership to pcb->network
1527 m_pcb->m_network->m_nets.push_back( net );
1528 m_nets[n] = nullptr;
1529 }
1530 }
1531 }
1532
1533 //-----< output vias used in netclasses >-----------------------------------
1534 {
1535 // Assume the netclass vias are all the same kind of thru, blind, or buried vias.
1536 // This is in lieu of either having each netclass via have its own layer pair in
1537 // the netclass dialog, or such control in the specctra export dialog.
1538
1539 m_top_via_layer = 0; // first specctra cu layer is number zero.
1541
1542 // Add the via from the Default netclass first. The via container
1543 // in pcb->library preserves the sequence of addition.
1544
1545 PADSTACK* via = makeVia( netSettings->GetDefaultNetclass()->GetViaDiameter(),
1546 netSettings->GetDefaultNetclass()->GetViaDrill(), m_top_via_layer,
1548
1549 // we AppendVia() this first one, there is no way it can be a duplicate,
1550 // the pcb->library via container is empty at this point. After this,
1551 // we'll have to use LookupVia().
1552 wxASSERT( m_pcb->m_library->m_vias.size() == 0 );
1554
1555 // set the "spare via" index at the start of the
1556 // pcb->library->spareViaIndex = pcb->library->vias.size();
1557
1558 // output the non-Default netclass vias
1559 for( const auto& [name, netclass] : netSettings->GetNetclasses() )
1560 {
1561 via = makeVia( netclass->GetViaDiameter(), netclass->GetViaDrill(),
1563
1564 // maybe add 'via' to the library, but only if unique.
1566
1567 if( registered != via )
1568 delete via;
1569 }
1570 }
1571
1572 //-----<create the wires from tracks>-----------------------------------
1573 {
1574 // export all of them for now, later we'll decide what controls we need on this.
1575 std::string netname;
1576 WIRING* wiring = m_pcb->m_wiring;
1577 PATH* path = nullptr;
1578
1579 int old_netcode = -1;
1580 int old_width = -1;
1581 int old_layer = UNDEFINED_LAYER;
1582
1583 for( PCB_TRACK* track : aBoard->Tracks() )
1584 {
1585 if( !track->IsType( { PCB_TRACE_T, PCB_ARC_T } ) )
1586 continue;
1587
1588 int netcode = track->GetNetCode();
1589
1590 if( netcode == 0 )
1591 continue;
1592
1593 if( old_netcode != netcode
1594 || old_width != track->GetWidth()
1595 || old_layer != track->GetLayer()
1596 || ( path && path->points.back() != mapPt( track->GetStart() ) ) )
1597 {
1598 old_width = track->GetWidth();
1599 old_layer = track->GetLayer();
1600
1601 if( old_netcode != netcode )
1602 {
1603 old_netcode = netcode;
1604 NETINFO_ITEM* net = aBoard->FindNet( netcode );
1605 wxASSERT( net );
1606 netname = TO_UTF8( net->GetNetname() );
1607 }
1608
1609 WIRE* wire = new WIRE( wiring );
1610
1611 wiring->wires.push_back( wire );
1612 wire->m_net_id = netname;
1613
1614 if( track->IsLocked() )
1615 wire->m_wire_type = T_fix; // tracks with fix property are not returned in .ses files
1616 else
1617 wire->m_wire_type = T_route; // could be T_protect
1618
1619 PCB_LAYER_ID kiLayer = track->GetLayer();
1620 int pcbLayer = m_kicadLayer2pcb[kiLayer];
1621
1622 path = new PATH( wire );
1623 wire->SetShape( path );
1624 path->layer_id = m_layerIds[pcbLayer];
1625 path->aperture_width = scale( old_width );
1626 path->AppendPoint( mapPt( track->GetStart() ) );
1627 }
1628
1629 if( path ) // Should not occur
1630 path->AppendPoint( mapPt( track->GetEnd() ) );
1631 }
1632 }
1633
1634 //-----<export the existing real BOARD instantiated vias>-----------------
1635 {
1636 // Export all vias, once per unique size and drill diameter combo.
1637 for( PCB_TRACK* track : aBoard->Tracks() )
1638 {
1639 if( track->Type() != PCB_VIA_T )
1640 continue;
1641
1642 PCB_VIA* via = static_cast<PCB_VIA*>( track );
1643 int netcode = via->GetNetCode();
1644
1645 if( netcode == 0 )
1646 continue;
1647
1648 PADSTACK* padstack = makeVia( via );
1649 PADSTACK* registered = m_pcb->m_library->LookupVia( padstack );
1650
1651 // if the one looked up is not our padstack, then delete our padstack
1652 // since it was a duplicate of one already registered.
1653 if( padstack != registered )
1654 delete padstack;
1655
1656 WIRE_VIA* dsnVia = new WIRE_VIA( m_pcb->m_wiring );
1657
1658 m_pcb->m_wiring->wire_vias.push_back( dsnVia );
1659
1660 dsnVia->m_padstack_id = registered->m_padstack_id;
1661 dsnVia->m_vertexes.push_back( mapPt( via->GetPosition() ) );
1662
1663 NETINFO_ITEM* net = aBoard->FindNet( netcode );
1664 wxASSERT( net );
1665
1666 dsnVia->m_net_id = TO_UTF8( net->GetNetname() );
1667
1668 if( via->IsLocked() )
1669 dsnVia->m_via_type = T_fix; // vias with fix property are not returned in .ses files
1670 else
1671 dsnVia->m_via_type = T_route; // could be T_protect
1672 }
1673 }
1674
1675 //-----<via_descriptor>-------------------------------------------------
1676 {
1677 // The pcb->library will output <padstack_descriptors> which is a combined list of part
1678 // padstacks and via padstacks. specctra dsn uses the <via_descriptors> to say which of
1679 // those padstacks are vias.
1680
1681 // Output the vias in the padstack list here, by name only. This must be done after
1682 // exporting existing vias as WIRE_VIAs.
1683 VIA* vias = m_pcb->m_structure->m_via;
1684
1685 for( unsigned viaNdx = 0; viaNdx < m_pcb->m_library->m_vias.size(); ++viaNdx )
1686 vias->AppendVia( m_pcb->m_library->m_vias[viaNdx].m_padstack_id.c_str() );
1687 }
1688
1689 //-----<output NETCLASSs>----------------------------------------------------
1690
1691 exportNETCLASS( netSettings->GetDefaultNetclass(), aBoard );
1692
1693 for( const auto& [name, netclass] : netSettings->GetNetclasses() )
1694 exportNETCLASS( netclass, aBoard );
1695}
1696
1697
1698void SPECCTRA_DB::exportNETCLASS( const std::shared_ptr<NETCLASS>& aNetClass, BOARD* aBoard )
1699{
1700 /* From page 11 of specctra spec:
1701 *
1702 * Routing and Placement Rule Hierarchies
1703 *
1704 * Routing and placement rules can be defined at multiple levels of design
1705 * specification. When a routing or placement rule is defined for an object at
1706 * multiple levels, a predefined routing or placement precedence order
1707 * automatically determines which rule to apply to the object. The routing rule
1708 * precedence order is
1709 *
1710 * pcb < layer < class < class layer < group_set < group_set layer < net <
1711 * net layer < group < group layer < fromto < fromto layer < class_class <
1712 * class_class layer < padstack < region < class region < net region <
1713 * class_class region
1714 *
1715 * A pcb rule (global rule for the PCB design) has the lowest precedence in the
1716 * hierarchy. A class-to-class region rule has the highest precedence. Rules
1717 * set at one level of the hierarchy override conflicting rules set at lower
1718 * levels. The placement rule precedence order is
1719 *
1720 * pcb < image_set < image < component < super cluster < room <
1721 * room_image_set < family_family < image_image
1722 *
1723 * A pcb rule (global rule for the PCB design) has the lowest precedence in the
1724 * hierarchy. An image-to-image rule has the highest precedence. Rules set at
1725 * one level of the hierarchy override conflicting rules set at lower levels.
1726 */
1727
1728 char text[256];
1729
1730 CLASS* clazz = new CLASS( m_pcb->m_network );
1731
1732 m_pcb->m_network->m_classes.push_back( clazz );
1733
1734 // Freerouter creates a class named 'default' anyway, and if we try to use that we end up
1735 // with two 'default' via rules so use something else as the name of our default class.
1736 clazz->m_class_id = TO_UTF8( aNetClass->GetName() );
1737
1738 for( NETINFO_ITEM* net : aBoard->GetNetInfo() )
1739 {
1740 if( net->GetNetClass()->GetName() == clazz->m_class_id )
1741 clazz->m_net_ids.push_back( TO_UTF8( net->GetNetname() ) );
1742 }
1743
1744 clazz->m_rules = new RULE( clazz, T_rule );
1745
1746 // output the track width.
1747 int trackWidth = aNetClass->GetTrackWidth();
1748 std::snprintf( text, sizeof( text ), "(width %.6g)", scale( trackWidth ) );
1749 clazz->m_rules->m_rules.push_back( text );
1750
1751 // output the clearance.
1752 int clearance = aNetClass->GetClearance();
1753 std::snprintf( text, sizeof( text ), "(clearance %.6g)", scale( clearance ) );
1754 clazz->m_rules->m_rules.push_back( text );
1755
1756 if( aNetClass->GetName() == NETCLASS::Default )
1757 clazz->m_class_id = "kicad_default";
1758
1759 // The easiest way to get the via name is to create a temporary via (which generates the
1760 // name internal to the PADSTACK), and then grab the name and delete the via. There are not
1761 // that many netclasses so this should never become a performance issue.
1762
1763 PADSTACK* via = makeVia( aNetClass->GetViaDiameter(), aNetClass->GetViaDrill(),
1765
1766 snprintf( text, sizeof( text ), "(use_via \"%s\")", via->GetPadstackId().c_str() );
1767 clazz->m_circuit.push_back( text );
1768
1769 delete via;
1770}
1771
1772
1774{
1775 // DSN Images (=KiCad FOOTPRINTs and PADs) must be presented from the top view.
1776 // Note: to export footprints, the footprints must be flipped around the X axis, otherwise
1777 // the rotation angle is not good.
1778 for( FOOTPRINT* footprint : aBoard->Footprints() )
1779 {
1780 footprint->SetFlag( 0 );
1781
1782 if( footprint->GetLayer() == B_Cu )
1783 {
1784 footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
1785 footprint->SetFlag( 1 );
1786 }
1787 }
1788
1790}
1791
1792
1794{
1796 return;
1797
1798 // DSN Images (=KiCad FOOTPRINTs and PADs) must be presented from the
1799 // top view. Restore those that were flipped.
1800 // Note: to export footprints, the footprints were flipped around the X axis,
1801 for( FOOTPRINT* footprint : aBoard->Footprints() )
1802 {
1803 if( footprint->GetFlag() )
1804 {
1805 footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
1806 footprint->SetFlag( 0 );
1807 }
1808 }
1809
1810 m_footprintsAreFlipped = false;
1811}
1812
1813} // namespace DSN
const char * name
Definition: DXF_plotter.cpp:59
@ 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:161
@ LT_MIXED
Definition: board.h:162
@ LT_JUMPER
Definition: board.h:163
@ LT_SIGNAL
Definition: board.h:160
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:295
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:2536
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:879
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:817
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1961
const ZONES & Zones() const
Definition: board.h:340
LAYER_T GetLayerType(PCB_LAYER_ID aLayer) const
Return the type of the copper layer given by aLayer.
Definition: board.cpp:651
void SynchronizeNetsAndNetClasses(bool aResetTrackAndViaSizes)
Copy NETCLASS info to each NET, based on NET membership in a NETCLASS.
Definition: board.cpp:2094
int GetCopperLayerCount() const
Definition: board.cpp:780
const FOOTPRINTS & Footprints() const
Definition: board.h:336
const TRACKS & Tracks() const
Definition: board.h:334
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:934
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:729
void SetLayerId(const std::string &aLayerId)
Definition: specctra.h:757
void SetVertex(const POINT &aVertex)
Definition: specctra.h:767
void SetDiameter(double aDiameter)
Definition: specctra.h:762
The <class_descriptor> in the specctra spec.
Definition: specctra.h:2748
std::string m_class_id
Definition: specctra.h:2823
RULE * m_rules
Definition: specctra.h:2830
STRINGS m_net_ids
Definition: specctra.h:2825
STRINGS m_circuit
circuit descriptor list
Definition: specctra.h:2828
Implement a <component_descriptor> in the specctra dsn spec.
Definition: specctra.h:1768
PLACES m_places
Definition: specctra.h:1809
A <plane_descriptor> in the specctra dsn spec.
Definition: specctra.h:1353
void Append(ELEM *aElem)
Definition: specctra.h:322
Used for <keepout_descriptor> and <plane_descriptor>.
Definition: specctra.h:906
void AddWindow(WINDOW *aWindow)
Definition: specctra.h:947
void SetShape(ELEM *aShape)
Definition: specctra.h:930
std::string m_name
Definition: specctra.h:1012
PROPERTIES properties
Definition: specctra.h:1288
DSN_T layer_type
one of: T_signal, T_power, T_mixed, T_jumper
Definition: specctra.h:1279
std::string name
Definition: specctra.h:1278
PADSTACK * LookupVia(PADSTACK *aVia)
Add the via only if one exactly like it does not already exist in the padstack container.
Definition: specctra.h:2381
void AppendVia(PADSTACK *aVia)
Add aVia to the internal via container.
Definition: specctra.h:2359
IMAGE * LookupIMAGE(IMAGE *aImage)
Add the image only if one exactly like it does not already exist in the image container.
Definition: specctra.h:2327
PADSTACKS m_vias
Definition: specctra.h:2442
void AddPadstack(PADSTACK *aPadstack)
Definition: specctra.h:2266
CLASSLIST m_classes
Definition: specctra.h:2861
A <net_descriptor> in the DSN spec.
Definition: specctra.h:2591
std::string m_net_id
Definition: specctra.h:2689
PIN_REFS m_pins
Definition: specctra.h:2694
Hold either a via or a pad definition.
Definition: specctra.h:2121
std::string m_padstack_id
Definition: specctra.h:2220
void SetPadstackId(const char *aPadstackId)
Definition: specctra.h:2154
bool space_in_quoted_tokens
Definition: specctra.h:381
Support both the <path_descriptor> and the <polygon_descriptor> per the specctra dsn spec.
Definition: specctra.h:583
void SetLayerId(const std::string &aLayerId)
Definition: specctra.h:600
void AppendPoint(const POINT &aPoint)
Definition: specctra.h:593
std::string layer_id
Definition: specctra.h:650
UNIT_RES * m_unit
Definition: specctra.h:3255
UNIT_RES * m_resolution
Definition: specctra.h:3254
NETWORK * m_network
Definition: specctra.h:3259
PLACEMENT * m_placement
Definition: specctra.h:3257
STRUCTURE * m_structure
Definition: specctra.h:3256
PARSER * m_parser
Definition: specctra.h:3253
WIRING * m_wiring
Definition: specctra.h:3260
LIBRARY * m_library
Definition: specctra.h:3258
COMPONENT * LookupCOMPONENT(const std::string &imageName)
Look up a COMPONENT by name.
Definition: specctra.h:1838
Implement a <placement_reference> in the specctra dsn spec.
Definition: specctra.h:1688
void SetVertex(const POINT &aVertex)
Definition: specctra.h:1716
void SetRotation(double aRotation)
Definition: specctra.h:1723
DSN_T m_side
Definition: specctra.h:1735
std::string m_part_number
Definition: specctra.h:1758
std::string m_component_id
reference designator
Definition: specctra.h:1733
void SetLayerId(std::string &aLayerId)
Definition: specctra.h:447
void SetCorners(const POINT &aPoint0, const POINT &aPoint1)
Definition: specctra.h:452
A <rule_descriptor> in the specctra dsn spec.
Definition: specctra.h:492
STRINGS m_rules
rules are saved in std::string form.
Definition: specctra.h:530
A "(shape ..)" element in the specctra dsn spec.
Definition: specctra.h:1894
A DSN data tree, usually coming from a DSN file.
Definition: specctra.h:3646
int m_top_via_layer
specctra cu layers, 0 based index:
Definition: specctra.h:4011
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
std::map< int, PCB_LAYER_ID > m_pcbLayer2kicad
maps PCB layer number to BOARD layer numbers
Definition: specctra.h:3995
void ExportPCB(const wxString &aFilename, bool aNameChange=false)
Write the internal PCB instance out as a SPECTRA DSN format file.
Definition: specctra.cpp:3704
SHAPE_POLY_SET m_brd_outlines
Definition: specctra.h:3983
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:3992
bool m_footprintsAreFlipped
Definition: specctra.h:3988
void deleteNETs()
Delete all the NETs that may be in here.
Definition: specctra.h:3943
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:3686
static PCB * MakePCB()
Make a PCB with all the default ELEMs and parts on the heap.
Definition: specctra.cpp:3729
bool BuiltBoardOutlines(BOARD *aBoard)
Build the board outlines and store it in m_brd_outlines.
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::map< PCB_LAYER_ID, int > m_kicadLayer2pcb
maps BOARD layer number to PCB layer numbers
Definition: specctra.h:3994
std::vector< NET * > m_nets
we don't want ownership here permanently, so we don't use boost::ptr_vector
Definition: specctra.h:4008
PADSTACKSET m_padstackset
Definition: specctra.h:4005
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:1670
void SetBOUNDARY(BOUNDARY *aBoundary)
Definition: specctra.h:1582
COPPER_PLANES m_planes
Definition: specctra.h:1672
LAYERS m_layers
Definition: specctra.h:1660
RULE * m_rules
Definition: specctra.h:1668
DSN_T units
Definition: specctra.h:433
A <via_descriptor> in the specctra dsn spec.
Definition: specctra.h:1039
void AppendVia(const char *aViaName)
Definition: specctra.h:1047
void SetShape(ELEM *aShape)
Definition: specctra.h:858
A <wire_via_descriptor> in the specctra dsn spec.
Definition: specctra.h:2988
std::string m_net_id
Definition: specctra.h:3119
std::string m_padstack_id
Definition: specctra.h:3117
DSN_T m_via_type
Definition: specctra.h:3121
POINTS m_vertexes
Definition: specctra.h:3118
A <wire_shape_descriptor> in the specctra dsn spec.
Definition: specctra.h:2879
DSN_T m_wire_type
Definition: specctra.h:2973
void SetShape(ELEM *aShape)
Definition: specctra.h:2899
std::string m_net_id
Definition: specctra.h:2971
A <wiring_descriptor> in the specctra dsn spec.
Definition: specctra.h:3135
WIRES wires
Definition: specctra.h:3172
WIRE_VIAS wire_vias
Definition: specctra.h:3173
EDA_ANGLE Normalize()
Definition: eda_angle.h:221
double AsDegrees() const
Definition: eda_angle.h:113
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:912
int GetRadius() const
Definition: eda_shape.cpp:840
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:225
ZONES & Zones()
Definition: footprint.h:210
const LIB_ID & GetFPID() const
Definition: footprint.h:246
VECTOR2I GetPosition() const override
Definition: footprint.h:222
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:37
LSEQ CuStack() const
Return a sequence of copper layers in starting from the front/top and extending to the back/bottom.
Definition: lset.cpp:245
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:562
static const char Default[]
the name of the default NETCLASS
Definition: netclass.h:47
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:435
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:442
const BOX2I GetBoundingBox() const override
The bounding box is cached, so this will be efficient most of the time.
Definition: pad.cpp:826
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pad.h:780
const VECTOR2I & GetDrillSize() const
Definition: pad.h:303
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition: pad.h:297
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition: pad.h:193
const VECTOR2I & GetOffset(PCB_LAYER_ID aLayer) const
Definition: pad.h:315
int GetChamferPositions(PCB_LAYER_ID aLayer) const
Definition: pad.h:703
double GetChamferRectRatio(PCB_LAYER_ID aLayer) const
Definition: pad.h:686
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition: pad.h:262
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()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
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:945
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:166
std::map< wxString, int > PINMAP
data type used to ensure unique-ness of pin names, holding (wxString and int)
static double mapX(int x)
static double mapY(int y)
static double IU2um(int kicadDist)
static POINT mapPt(const VECTOR2I &pt)
Convert a KiCad point into a DSN file point.
static bool isRoundKeepout(PAD *aPad)
Decide if the pad is a copper-less through hole which needs to be made into a round keepout.
std::set< std::string > STRINGSET
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:49
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:2450
std::string pin_id
Definition: specctra.h:2476
std::string component_id
Definition: specctra.h:2475
A point in the SPECCTRA DSN coordinate system.
Definition: specctra.h:103
void FixNegativeZero()
Change negative zero to positive zero in the IEEE floating point storage format.
Definition: specctra.h:144
double y
Definition: specctra.h:105
double x
Definition: specctra.h:104
const double IU_PER_MM
Definition: base_units.h: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:695