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