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