KiCad PCB EDA Suite
Loading...
Searching...
No Matches
easypc_pcb_builder.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
22
29
30#include <board.h>
33#include <footprint.h>
34#include <ki_exception.h>
35#include <lib_id.h>
36#include <netclass.h>
37#include <netinfo.h>
38#include <pad.h>
39#include <pcb_group.h>
40#include <pcb_shape.h>
41#include <pcb_text.h>
42#include <pcb_track.h>
44#include <reporter.h>
45#include <string_utils.h>
46#include <title_block.h>
47#include <zone.h>
48
49#include <limits>
50#include <optional>
51#include <set>
52
53
54using namespace EASYPC;
55
56
57namespace EASYPC_PCB
58{
59
60namespace
61{
62
64 constexpr int PLANE_MAX_ERROR = 1000;
65
67 constexpr int PLANE_MIN_THICKNESS = 10000;
68
70 struct COPPER_KIND
71 {
72 int Kind;
73 const char* Name;
74 const char* Type;
75 };
76
77 constexpr COPPER_KIND COPPER_KINDS[] = { { SPACING_TRACK, "track", "'Track'" },
78 { SPACING_PAD, "pad", "'Pad'" },
79 { SPACING_VIA, "via", "'Via'" },
80 { SPACING_SHAPE, "shape", "'Graphic'" },
81 { SPACING_TEXT, "text", "'Text'" } };
82
83
84 wxString kindTest( const COPPER_KIND& aKind, const wxString& aItem )
85 {
86 wxString test = aItem + wxS( ".Type == " ) + aKind.Type;
87
88 if( aKind.Kind == SPACING_SHAPE )
89 return wxS( "(" ) + test + wxS( " || " ) + aItem + wxS( ".Type == 'Zone')" );
90
91 return test;
92 }
93
94
96 wxString mm( int64_t aDsu )
97 {
98 uint64_t magnitude = aDsu < 0 ? 0 - static_cast<uint64_t>( aDsu ) : static_cast<uint64_t>( aDsu );
99
100 return wxString::Format( wxS( "%s%llu.%04llumm" ), aDsu < 0 ? wxS( "-" ) : wxS( "" ),
101 static_cast<unsigned long long>( magnitude / 10000 ),
102 static_cast<unsigned long long>( magnitude % 10000 ) );
103 }
104
105
107 wxString exprLiteral( const wxString& aText )
108 {
109 wxString text = aText;
110 text.Replace( wxS( "'" ), wxS( "\\'" ) );
111 return wxS( "'" ) + text + wxS( "'" );
112 }
113
114
116 wxString quoteSexpr( const wxString& aText )
117 {
118 wxString text = aText;
119 text.Replace( wxS( "\\" ), wxS( "\\\\" ) );
120 text.Replace( wxS( "\"" ), wxS( "\\\"" ) );
121 return wxS( "\"" ) + text + wxS( "\"" );
122 }
123
124
126 int32_t spacingValue( const SOURCE_SPACINGS& aSpacings, int aTypeA, int aTypeB )
127 {
128 if( aSpacings.Bidirectional && aTypeB > aTypeA )
129 std::swap( aTypeA, aTypeB );
130
131 auto it = aSpacings.Values.find(
132 static_cast<int32_t>( ( static_cast<uint32_t>( aTypeA ) << 16 ) | ( aTypeB & 0xffff ) ) );
133 return it == aSpacings.Values.end() ? -1 : it->second;
134 }
135
136
137 class PCB_BUILDER
138 {
139 public:
140 PCB_BUILDER( const DESIGN_DOCUMENT& aDocument, BOARD& aBoard, REPORTER* aReporter );
141
142 void Build();
143
144 std::unique_ptr<LAYER_MAPPER> m_layers;
145 wxString m_customRules;
146
147 private:
148 void buildLayers();
149 void buildRules();
150 void buildNetClasses();
151 void buildFootprints();
152 void buildFreeItems();
153 void buildTracksAndVias();
154 void buildAreas();
155 void buildPlanes();
156 void buildGroups();
157
159 SHAPE_POLY_SET planeClearances( const SOURCE_LAYER& aLayer, PCB_LAYER_ID aKiCadLayer,
160 NETINFO_ITEM* aNet ) const;
161
163 bool becomesZone( const SOURCE_AREA& aArea ) const;
164
165 void placeValueTexts( FOOTPRINT& aFootprint, const SOURCE_COMPONENT_INSTANCE& aInstance,
166 const SOURCE_SYMBOL_INSTANCE& aSymbolInstance );
167
169 int32_t designValue( int aTypeA, int aTypeB ) const;
170
171 int32_t shapeSpacing( int aKind ) const
172 {
173 return m_spacings ? spacingValue( *m_spacings, SPACING_SHAPE, aKind ) : -1;
174 }
175
176 void addRule( const wxString& aName, const wxString& aConstraint, const wxString& aCondition );
177
178 NETINFO_ITEM* netOfNode( const OBJECT* aNode ) const;
179
180 const DESIGN_DOCUMENT& m_doc;
181 const SOURCE_DESIGN* m_design;
182 const SOURCE_SPACINGS* m_spacings;
183 BOARD& m_board;
184 REPORTER* m_reporter;
185 FRAME m_frame;
186 std::map<const OBJECT*, NETINFO_ITEM*> m_nets;
187 std::map<const OBJECT*, std::vector<BOARD_ITEM*>> m_created;
188 bool m_viaMask[2] = { false, false };
189 };
190
191} // namespace
192
193
194PCB_BUILDER::PCB_BUILDER( const DESIGN_DOCUMENT& aDocument, BOARD& aBoard, REPORTER* aReporter ) :
195 m_doc( aDocument ),
196 m_design( dynamic_cast<const SOURCE_DESIGN*>( aDocument.Design.get() ) ),
197 m_spacings( m_design ? dynamic_cast<const SOURCE_SPACINGS*>( m_design->Spacings ) : nullptr ),
198 m_board( aBoard ),
199 m_reporter( aReporter )
200{
201 if( !m_design )
202 THROW_IO_ERROR( _( "Easy-PC design has no design root." ) );
203
204 m_layers = std::make_unique<LAYER_MAPPER>( TypedItems<const SOURCE_LAYER>( m_design->Layers ) );
205
206 // Put the board's top left corner one inch in from the KiCad origin
207 bool any = false;
208
209 for( const SOURCE_BOARD* board : TypedItems<const SOURCE_BOARD>( m_design->Boards ) )
210 {
211 const SOURCE_SHAPE* shape = dynamic_cast<const SOURCE_SHAPE*>( board->Shape );
212
213 if( !shape )
214 continue;
215
216 for( const OBJECT* obj : shape->Segments )
217 {
218 if( const SOURCE_SEGMENT* seg = dynamic_cast<const SOURCE_SEGMENT*>( obj ) )
219 {
220 m_frame.Origin.X = any ? std::min( m_frame.Origin.X, seg->X ) : seg->X;
221 m_frame.Origin.Y = any ? std::max( m_frame.Origin.Y, seg->Y ) : seg->Y;
222 any = true;
223 }
224 }
225 }
226
227 m_frame.Offset = VECTOR2I( 25400000, 25400000 );
228}
229
230
231void PCB_BUILDER::Build()
232{
233 buildLayers();
234
235 for( const SOURCE_NET* net : TypedItems<const SOURCE_NET>( m_design->Nets ) )
236 {
237 // The schematic import names the net the same way, so an update from the schematic keeps it
238 NETINFO_ITEM* info = new NETINFO_ITEM( &m_board, ToKiCadMarkup( net->Name ) );
239 m_board.Add( info );
240 m_nets[net] = info;
241 }
242
243 buildRules();
244
245 for( const SOURCE_BOARD* board : TypedItems<const SOURCE_BOARD>( m_design->Boards ) )
246 AddShapeItem( m_board, *board, m_frame, *m_layers, Edge_Cuts );
247
248 buildFootprints();
249 buildFreeItems();
250 buildTracksAndVias();
251 buildAreas();
252 buildPlanes();
253 buildGroups();
254
255 TITLE_BLOCK titleBlock;
256 EASYPC::FillTitleBlock( m_doc, titleBlock );
257 m_board.SetTitleBlock( titleBlock );
258
259 // Items sit right of and below the page origin by the frame offset, so the page must reach back to it
260 BOX2L extents( VECTOR2L( 0, 0 ) );
261 extents.Merge( VECTOR2L( m_board.ComputeBoundingBox( false ).GetEnd() ) );
262 m_board.SetPageSettings( EASYPC::PageForExtents( extents ) );
263}
264
265
266int32_t PCB_BUILDER::designValue( int aTypeA, int aTypeB ) const
267{
268 if( !m_spacings || m_spacings->Bidirectional )
269 return m_spacings ? spacingValue( *m_spacings, aTypeA, aTypeB ) : -1;
270
271 // A one-way table depends on which item the caller names first; KiCad pairs are unordered
272 return std::max( spacingValue( *m_spacings, aTypeA, aTypeB ), spacingValue( *m_spacings, aTypeB, aTypeA ) );
273}
274
275
276void PCB_BUILDER::addRule( const wxString& aName, const wxString& aConstraint, const wxString& aCondition )
277{
278 if( m_customRules.IsEmpty() )
279 m_customRules = wxS( "(version 1)\n" );
280
281 m_customRules += wxS( "\n(rule " ) + quoteSexpr( aName ) + wxS( "\n " ) + aConstraint + wxS( "\n" );
282 m_customRules += wxS( " (condition " ) + quoteSexpr( aCondition ) + wxS( "))\n" );
283}
284
285
286void PCB_BUILDER::buildRules()
287{
289
290 // BOARD::SetProject keeps importer settings and net classes only when they are flagged as loaded
291 m_board.m_LegacyDesignSettingsLoaded = true;
292 m_board.m_LegacyNetclassesLoaded = true;
293
294 // Easy-PC has no board-wide clearance floor; every pair gets its own rule
295 bds.m_MinClearance = 0;
296 bds.m_HoleClearance = 0;
297 bds.m_HoleToHoleMin = 0;
298 bds.m_CopperEdgeClearance = 0;
299 bds.m_SolderMaskMinWidth = 0;
301
302 // Design parameters are only stored above engine 8000
303 auto param = [&]( int aId )
304 {
305 return m_design->Version > 8000 ? std::max( 0, m_design->DesignParameters.Param( aId ) ) : 0;
306 };
307
313
314 if( param( DP_MIN_ANNULAR_RING ) > 0 )
315 {
316 addRule( wxS( "Easy-PC minimum pad annular ring" ),
317 wxS( "(constraint annular_width (min " ) + mm( param( DP_MIN_ANNULAR_RING ) ) + wxS( "))" ),
318 wxS( "A.Type == 'Pad'" ) );
319 }
320
321 if( param( DP_MIN_TEXT_SIZE ) > 0 )
322 {
323 addRule( wxS( "Easy-PC minimum text size" ),
324 wxS( "(constraint text_height (min " ) + mm( param( DP_MIN_TEXT_SIZE ) ) + wxS( "))" ),
325 wxS( "A.Type == 'Text'" ) );
326 }
327
328 buildNetClasses();
329
330 if( !m_spacings )
331 return;
332
333 // A pair missing from the table is not checked (GetSpacing returns -1), which in KiCad needs an explicit 0
334 for( size_t i = 0; i < std::size( COPPER_KINDS ); ++i )
335 {
336 for( size_t j = i; j < std::size( COPPER_KINDS ); ++j )
337 {
338 const COPPER_KIND& a = COPPER_KINDS[i];
339 const COPPER_KIND& b = COPPER_KINDS[j];
340
341 addRule( wxString::Format( wxS( "Easy-PC %s to %s" ), a.Name, b.Name ),
342 wxS( "(constraint clearance (min " ) + mm( std::max( 0, designValue( a.Kind, b.Kind ) ) )
343 + wxS( "))" ),
344 kindTest( a, wxS( "A" ) ) + wxS( " && " ) + kindTest( b, wxS( "B" ) ) );
345 }
346 }
347
348 for( const COPPER_KIND& kind : COPPER_KINDS )
349 {
350 addRule( wxString::Format( wxS( "Easy-PC board to %s" ), kind.Name ),
351 wxS( "(constraint edge_clearance (min " )
352 + mm( std::max( 0, designValue( SPACING_BOARD, kind.Kind ) ) ) + wxS( "))" ),
353 kindTest( kind, wxS( "A" ) ) );
354 }
355}
356
357
358void PCB_BUILDER::buildNetClasses()
359{
360 std::shared_ptr<NET_SETTINGS>& netSettings = m_board.GetDesignSettings().m_NetSettings;
361
362 // A track to track spacing is the closest Easy-PC has to a netclass clearance; custom rules refine it
363 int32_t trackSpacing = designValue( SPACING_TRACK, SPACING_TRACK );
364
365 auto fill = [&]( NETCLASS& aClass, const SOURCE_NET_CLASS* aSource )
366 {
367 aClass.SetClearance( static_cast<int>( DsuToNm( std::max( 0, trackSpacing ) ) ) );
368
369 if( !aSource )
370 return;
371
372 int32_t nominal = 0;
373
374 for( const SOURCE_TRACK_STYLE* style : { aSource->TrackStyle1, aSource->TrackStyle2 } )
375 {
376 if( style )
377 nominal = std::max( nominal, style->Width );
378 }
379
380 if( nominal > 0 )
381 {
382 aClass.SetTrackWidth( static_cast<int>( DsuToNm( nominal ) ) );
383 aClass.SetDiffPairWidth( static_cast<int>( DsuToNm( nominal ) ) );
384 }
385
386 if( aSource->ViaStyle )
387 {
388 aClass.SetViaDiameter( static_cast<int>( DsuToNm( aSource->ViaStyle->Size ) ) );
389
390 if( aSource->ViaStyle->Drill > 0 )
391 aClass.SetViaDrill( static_cast<int>( DsuToNm( aSource->ViaStyle->Drill ) ) );
392 }
393
394 // The stored gap applies whether or not diff pairs are enabled
395 if( aSource->DiffPairGap > 0 )
396 aClass.SetDiffPairGap( static_cast<int>( DsuToNm( aSource->DiffPairGap ) ) );
397 };
398
399 std::map<const OBJECT*, wxString> classNames;
400 const SOURCE_NET_CLASS* defaultSource = nullptr;
401 wxString defaultName;
402
403 if( const SOURCE_DEFAULTS* defaults = dynamic_cast<const SOURCE_DEFAULTS*>( m_design->Defaults ) )
404 defaultName = defaults->NetClass;
405
407 {
408 if( !defaultName.IsEmpty() && source->Name.CmpNoCase( defaultName ) == 0 && !defaultSource )
409 defaultSource = source;
410
411 wxString name =
412 source->Name.CmpNoCase( NETCLASS::Default ) == 0 ? wxString( wxS( "EasyPC_Default" ) ) : source->Name;
413
414 std::shared_ptr<NETCLASS> netclass = std::make_shared<NETCLASS>( name );
415 fill( *netclass, source );
416 netSettings->SetNetclass( name, netclass );
417 classNames[source] = name;
418
419 // Only the minimum style is a DRC limit in Easy-PC's own checks, so it becomes a rule
420 int32_t minimum = std::numeric_limits<int32_t>::max();
421
422 for( const SOURCE_TRACK_STYLE* style : { source->TrackStyle1, source->TrackStyle2 } )
423 {
424 if( style )
425 minimum = std::min( minimum, style->Width );
426 }
427
428 if( minimum != std::numeric_limits<int32_t>::max() && minimum > 0 && !name.EndsWith( wxS( "\\" ) ) )
429 {
430 addRule( wxS( "Easy-PC net class " ) + source->Name + wxS( " minimum width" ),
431 wxS( "(constraint track_width (min " ) + mm( minimum ) + wxS( "))" ),
432 wxS( "A.NetClass == " ) + exprLiteral( name ) );
433 }
434 }
435
436 fill( *netSettings->GetDefaultNetclass(), defaultSource );
437
438 for( const auto& [net, kicadNet] : m_nets )
439 {
440 if( auto it = classNames.find( static_cast<const SOURCE_NET*>( net )->NetClass ); it != classNames.end() )
441 {
442 netSettings->SetNetclassPatternAssignment( kicadNet->GetNetname(), it->second );
443 kicadNet->SetNetClass( netSettings->GetNetClassByName( it->second ) );
444 }
445 }
446}
447
448
449SHAPE_POLY_SET PCB_BUILDER::planeClearances( const SOURCE_LAYER& aLayer, PCB_LAYER_ID aKiCadLayer,
450 NETINFO_ITEM* aNet ) const
451{
452 // The negative plane plot (Ampera_V1, Chademo_VCU_V3_ISA, GwizBluePill, router2 ODB++): every feature it draws
453 // is a clearance, and the plane is the board less them
455 int iso = m_frame.Length( m_design->IsolationGap );
456 int spoke = m_frame.Length( m_design->ThermalRelief );
457 EDA_ANGLE spokeAngle = m_design->ThermalAngled ? ANGLE_45 : ANGLE_0;
458
459 // A ring from the land out by isolation_gap, cut by four spokes; round for a round land and rectangular, in the
460 // land's orientation, for a rectangular one (GwizBluePill T116)
461 auto thermal = [&]( const VECTOR2I& aCentre, const VECTOR2I& aLand, bool aRectangular, const EDA_ANGLE& aAngle )
462 {
463 SHAPE_POLY_SET ring;
464 SHAPE_POLY_SET land;
465 SHAPE_POLY_SET spokes;
466 int reach = std::max( aLand.x, aLand.y );
467
468 if( aRectangular )
469 {
470 TransformTrapezoidToPolygon( ring, aCentre, aLand + VECTOR2I( 2 * iso, 2 * iso ), aAngle, 0, 0, 0,
471 PLANE_MAX_ERROR, ERROR_OUTSIDE );
472 TransformTrapezoidToPolygon( land, aCentre, aLand, aAngle, 0, 0, 0, PLANE_MAX_ERROR, ERROR_INSIDE );
473 }
474 else
475 {
476 TransformCircleToPolygon( ring, aCentre, aLand.x / 2 + iso, PLANE_MAX_ERROR, ERROR_OUTSIDE );
477 TransformCircleToPolygon( land, aCentre, aLand.x / 2, PLANE_MAX_ERROR, ERROR_INSIDE );
478 }
479
480 ring.BooleanSubtract( land );
481
482 // The spokes run from the centre past the ring with square ends
483 for( int i = 0; i < 4; ++i )
484 {
485 VECTOR2I dir( reach + iso + spoke, 0 );
486 VECTOR2I side( 0, spoke / 2 );
487 SHAPE_LINE_CHAIN rect;
488
489 RotatePoint( dir, spokeAngle + ANGLE_90 * i );
490 RotatePoint( side, spokeAngle + ANGLE_90 * i );
491 rect.Append( aCentre + side );
492 rect.Append( aCentre + dir + side );
493 rect.Append( aCentre + dir - side );
494 rect.Append( aCentre - side );
495 rect.SetClosed( true );
496 spokes.AddOutline( rect );
497 }
498
499 ring.BooleanSubtract( spokes );
500 clear.Append( ring );
501 };
502
503 for( FOOTPRINT* fp : m_board.Footprints() )
504 {
505 for( PAD* pad : fp->Pads() )
506 {
507 if( !pad->IsOnLayer( aKiCadLayer ) )
508 continue;
509
510 VECTOR2I size = pad->GetSize( aKiCadLayer );
511 ::PAD_SHAPE shape = pad->GetShape( aKiCadLayer );
512
513 if( pad->GetNet() == aNet && pad->GetAttribute() == PAD_ATTRIB::PTH )
514 {
515 thermal( pad->GetPosition(), size,
517 || shape == ::PAD_SHAPE::CHAMFERED_RECT,
518 pad->GetOrientation() );
519 continue;
520 }
521
522 // A surface pad on an outer plane's own net is part of its copper
523 if( pad->GetNet() == aNet && !pad->HasHole() )
524 continue;
525
526 if( shape == ::PAD_SHAPE::CUSTOM )
527 {
528 pad->TransformShapeToPolygon( clear, aKiCadLayer, iso, PLANE_MAX_ERROR, ERROR_OUTSIDE );
529 continue;
530 }
531
532 // The clearance grows each half size by the isolation, keeping a rectangle's corners square
533 PAD grown( *pad );
534 grown.SetSize( aKiCadLayer, size + VECTOR2I( 2 * iso, 2 * iso ) );
535 grown.TransformShapeToPolygon( clear, aKiCadLayer, 0, PLANE_MAX_ERROR, ERROR_OUTSIDE );
536 }
537 }
538
539 for( PCB_TRACK* track : m_board.Tracks() )
540 {
541 if( !track->IsOnLayer( aKiCadLayer ) )
542 continue;
543
544 PCB_VIA* via = dynamic_cast<PCB_VIA*>( track );
545
546 if( via && via->GetNet() == aNet )
547 {
548 int width = via->GetWidth( aKiCadLayer );
549 thermal( via->GetPosition(), VECTOR2I( width, width ), false, ANGLE_0 );
550 }
551 else if( via )
552 {
553 TransformCircleToPolygon( clear, via->GetPosition(), via->GetWidth( aKiCadLayer ) / 2 + iso,
554 PLANE_MAX_ERROR, ERROR_OUTSIDE );
555 }
556 else if( track->GetNet() != aNet )
557 {
558 track->TransformShapeToPolygon( clear, aKiCadLayer, iso, PLANE_MAX_ERROR, ERROR_OUTSIDE );
559 }
560 }
561
562 // A pour keepout is cleared as its region and its outline stroke
563 for( const SOURCE_AREA* area : TypedItems<const SOURCE_AREA>( m_design->Areas ) )
564 {
565 const SOURCE_DESIGN_SHAPE* shape = dynamic_cast<const SOURCE_DESIGN_SHAPE*>( area->Shape );
566
567 if( !shape || !( area->Flags & AREA_POUR_KEEPOUT ) || area->Layer != &aLayer )
568 continue;
569
570 clear.Append( ShapeItemArea( *area, m_frame, PLANE_MAX_ERROR ) );
571 SHAPE_POLY_SET region = ShapeToPolySet( *shape, m_frame );
572 region.ClearArcs();
573 clear.Append( region );
574 }
575
576 clear.Simplify();
577 return clear;
578}
579
580
581void PCB_BUILDER::buildPlanes()
582{
583 // A copper layer with a net is a power plane, which plots negative: the board, less a clearance
584 // of isolation_gap around foreign items and a thermal of isolation_gap with spokes of thermal_relief on its own
585 std::vector<ZONE*> planes;
586 std::optional<SHAPE_POLY_SET> outline;
587
588 for( const SOURCE_LAYER* layer : TypedItems<const SOURCE_LAYER>( m_design->Layers ) )
589 {
590 PCB_LAYER_ID kicadLayer = m_layers->Map( layer );
591 auto netIt = m_nets.find( layer->Net );
592
593 if( layer->Usage != LAYER_USAGE_ELECTRICAL || !layer->Net || kicadLayer == UNDEFINED_LAYER
594 || netIt == m_nets.end() )
595 {
596 continue;
597 }
598
599 // The plot clears the board edge by the board to shape spacing beyond the outline's half width
600 for( const SOURCE_BOARD* board : TypedItems<const SOURCE_BOARD>( outline ? nullptr : m_design->Boards ) )
601 {
602 const SOURCE_DESIGN_SHAPE* shape = dynamic_cast<const SOURCE_DESIGN_SHAPE*>( board->Shape );
603 const SOURCE_LINE_STYLE* style = dynamic_cast<const SOURCE_LINE_STYLE*>( board->Style );
604 SHAPE_POLY_SET region;
605
606 if( shape && shape->IsClosed() )
607 {
608 region = ShapeToPolySet( *shape, m_frame );
609 region.ClearArcs();
610 region.Deflate( m_frame.Length( std::max( shapeSpacing( SPACING_BOARD ), 0 ) )
611 + ( style ? m_frame.Length( style->Width ) / 2 : 0 ),
612 CORNER_STRATEGY::ROUND_ALL_CORNERS, PLANE_MAX_ERROR );
613 }
614
615 ( outline ? *outline : outline.emplace() ).BooleanAdd( region );
616 }
617
618 if( !outline || outline->IsEmpty() )
619 continue;
620
621 ZONE* zone = new ZONE( &m_board );
622 zone->SetLayer( kicadLayer );
623 zone->SetNet( netIt->second );
624 zone->SetZoneName( layer->Name );
625 *zone->Outline() = *outline;
626 zone->SetLocalClearance( m_frame.Length( m_design->IsolationGap ) );
628 zone->SetThermalReliefGap( m_frame.Length( m_design->IsolationGap ) );
629 zone->SetThermalReliefSpokeWidth( m_frame.Length( m_design->ThermalRelief ) );
630 zone->SetMinThickness( PLANE_MIN_THICKNESS );
632 m_board.Add( zone );
633 planes.push_back( zone );
634
635 // The fill is drawn as the plane plots; KiCad's filler has no via thermals and clears a
636 // dropped pad only around its hole, so its settings above serve a later refill
637 SHAPE_POLY_SET fill = *outline;
638 fill.BooleanSubtract( planeClearances( *layer, kicadLayer, netIt->second ) );
639 fill.Fracture();
640 zone->SetFilledPolysList( kicadLayer, fill );
641 zone->SetIsFilled( true );
642 zone->SetNeedRefill( false );
643 }
644
645 if( planes.empty() )
646 return;
647
648 wxString names;
649 LSET planeLayers;
650
651 for( ZONE* zone : planes )
652 {
653 names += ( names.IsEmpty() ? wxString() : wxString( wxS( ", " ) ) ) + zone->GetZoneName();
654 planeLayers.set( zone->GetLayer() );
655 }
656
657 if( m_reporter )
658 {
659 m_reporter->Report( wxString::Format( _( "Easy-PC power planes (%s) were imported with the copper the "
660 "program plots. KiCad cannot refill them the same way: a refill "
661 "makes via thermals solid and clears foreign through-hole lands "
662 "only around their holes." ),
663 names ),
665 }
666
667 // A plane plots a foreign pad or via as its clearance alone; with only planes inside, KiCad drops their
668 // unconnected inner copper the same way
669 bool onlyPlanesInside = ( m_board.GetEnabledLayers() & LSET::InternalCuMask() ) == planeLayers;
670
671 for( FOOTPRINT* fp : m_board.Footprints() )
672 {
673 for( PAD* pad : fp->Pads() )
674 {
675 if( onlyPlanesInside && pad->GetAttribute() == PAD_ATTRIB::PTH )
676 pad->Padstack().SetUnconnectedLayerMode( UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END );
677
678 // KiCad's filler records which lands a fill reaches; the stored fill reaches every land on its own net.
679 // The plot's thermal spokes run on the axes unless the design angles them
680 for( ZONE* zone : planes )
681 {
682 if( pad->GetNet() == zone->GetNet() )
683 pad->SetThermalSpokeAngle( m_design->ThermalAngled ? ANGLE_45 : ANGLE_0 );
684
685 if( pad->HasHole() && pad->IsOnLayer( zone->GetLayer() ) && pad->GetNet() == zone->GetNet() )
686 pad->SetZoneLayerOverride( zone->GetLayer(), ZLO_FORCE_FLASHED );
687 }
688 }
689 }
690
691 for( PCB_TRACK* track : m_board.Tracks() )
692 {
693 PCB_VIA* via = dynamic_cast<PCB_VIA*>( track );
694
695 if( !via )
696 continue;
697
698 if( onlyPlanesInside )
699 via->Padstack().SetUnconnectedLayerMode( UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END );
700
701 for( ZONE* zone : planes )
702 {
703 if( via->IsOnLayer( zone->GetLayer() ) && via->GetNet() == zone->GetNet() )
704 via->SetZoneLayerOverride( zone->GetLayer(), ZLO_FORCE_FLASHED );
705 }
706 }
707}
708
709
710void PCB_BUILDER::buildGroups()
711{
712 // A dimension is stored as a group of its lines, arrows and text, kept as drawn
713 for( const SOURCE_GROUP* group : TypedItems<const SOURCE_GROUP>( m_design->Groups ) )
714 {
715 PCB_GROUP* kgroup = new PCB_GROUP( &m_board );
716 kgroup->SetName( group->Name );
717
718 for( const OBJECT* member : group->Members )
719 {
720 auto it = m_created.find( member );
721
722 if( it == m_created.end() )
723 continue;
724
725 for( BOARD_ITEM* item : it->second )
726 {
727 if( !item->GetParentGroup() )
728 kgroup->AddItem( item );
729 }
730 }
731
732 if( kgroup->GetItems().empty() )
733 delete kgroup;
734 else
735 m_board.Add( kgroup );
736 }
737}
738
739
740void PCB_BUILDER::buildLayers()
741{
742 int copper = m_layers->CopperCount();
743 m_board.SetCopperLayerCount( copper );
744
745 LSET enabled = LSET::AllCuMask( copper ) | LSET( { Edge_Cuts } );
747
748 // Mask and paste openings are the pad grown by the layer type's oversize
749 for( const SOURCE_LAYER* layer : TypedItems<const SOURCE_LAYER>( m_design->Layers ) )
750 {
751 PCB_LAYER_ID id = m_layers->Map( layer );
752 const SOURCE_LAYER_TYPE* type = layer->Type;
753 bool mask = id == F_Mask || id == B_Mask;
754
755 if( id == UNDEFINED_LAYER )
756 continue;
757
758 enabled.set( id );
759
760 if( IsCopperLayer( id ) || IsUserLayer( id ) )
761 m_board.SetLayerName( id, layer->Name );
762
763 if( !type || ( !mask && id != F_Paste && id != B_Paste ) )
764 continue;
765
766 // Vias are plotted on a mask layer only when its type enables them
767 if( mask )
768 m_viaMask[id == F_Mask ? 0 : 1] = type->ViasEnabled;
769
770 if( type->OversizeType == LAYER_OVERSIZE_TYPE::FIXED && mask )
771 bds.m_SolderMaskExpansion = m_frame.Length( type->Oversize );
772 else if( type->OversizeType == LAYER_OVERSIZE_TYPE::FIXED )
773 bds.m_SolderPasteMargin = m_frame.Length( type->Oversize );
774 else if( type->OversizeType == LAYER_OVERSIZE_TYPE::PERCENT && !mask )
775 bds.m_SolderPasteMarginRatio = type->Oversize / 100.0;
776 }
777
778 m_board.SetEnabledLayers( enabled );
779 m_board.SetVisibleLayers( enabled );
780}
781
782
783NETINFO_ITEM* PCB_BUILDER::netOfNode( const OBJECT* aNode ) const
784{
785 const SOURCE_NODE* node = dynamic_cast<const SOURCE_NODE*>( aNode );
786 auto it = node ? m_nets.find( node->Parent ) : m_nets.end();
787
788 return it == m_nets.end() ? nullptr : it->second;
789}
790
791
792void PCB_BUILDER::buildFootprints()
793{
794 for( const SOURCE_COMPONENT_INSTANCE* ci :
796 {
797 const SOURCE_COMPONENT* component = dynamic_cast<const SOURCE_COMPONENT*>( ci->Component );
798 const SOURCE_PCB_COMPONENT* pcb = component ? component->Board : nullptr;
799
800 for( const SOURCE_SYMBOL_INSTANCE* si : TypedItems<const SOURCE_SYMBOL_INSTANCE>( ci->SymbolInstances ) )
801 {
802 const SOURCE_SYMBOL* symbol = dynamic_cast<const SOURCE_SYMBOL*>( si->Symbol );
803
804 if( si->Gate != -1 || !si->Placed || !symbol )
805 continue;
806
807 std::map<const SOURCE_FREE_PAD*, PAD*> padMap;
808 LIB_ID id( wxS( "easypc" ), EscapeString( symbol->Name, CTX_LIBID ) );
809 std::unique_ptr<FOOTPRINT> fp = ConvertFootprint( *symbol, *m_layers, id, pcb, &padMap );
810
811 fp->SetParent( &m_board );
812
813 // A pad instance's style exception replaces the definition's style for that one pad
814 FRAME symbolFrame{ POINT32{ symbol->OriginX, symbol->OriginY } };
815
817 {
818 const SOURCE_FREE_PAD* freePad = dynamic_cast<const SOURCE_FREE_PAD*>( pi->Pad );
819 const SOURCE_PAD_STYLE* exception = dynamic_cast<const SOURCE_PAD_STYLE*>( pi->StyleException );
820 auto it = padMap.find( freePad );
821
822 if( !exception || it == padMap.end() )
823 continue;
824
825 std::unique_ptr<PAD> pad = CreatePad( fp.get(), *freePad, *exception, symbolFrame, *m_layers );
826 pad->SetNumber( it->second->GetNumber() );
827 fp->Remove( it->second );
828 delete it->second;
829 it->second = pad.get();
830 fp->Add( pad.release() );
831 }
832
833 fp->SetReference( ci->Name );
834
835 if( component && component->Schematic )
836 fp->SetValue( component->Schematic->Name );
837
838 // Native placement is mirror in X, then rotate anticlockwise, then translate. A left-right flip
839 // records 180 degrees in the orientation, so the rotation is applied relative to it
840 if( si->Mirrored )
841 fp->Flip( VECTOR2I( 0, 0 ), FLIP_DIRECTION::LEFT_RIGHT );
842
843 fp->Rotate( VECTOR2I( 0, 0 ), FRAME::Angle( si->Angle ) );
844 fp->SetPosition( m_frame.ToKiCad( si->Position ) );
845
846 placeValueTexts( *fp, *ci, *si );
847
849 {
850 auto it = padMap.find( dynamic_cast<const SOURCE_FREE_PAD*>( pi->Pad ) );
851 NETINFO_ITEM* net = netOfNode( pi->Node );
852
853 if( it != padMap.end() && net )
854 it->second->SetNet( net );
855 }
856
857 m_board.Add( fp.release() );
858 }
859 }
860}
861
862
863void PCB_BUILDER::placeValueTexts( FOOTPRINT& aFootprint, const SOURCE_COMPONENT_INSTANCE& aInstance,
864 const SOURCE_SYMBOL_INSTANCE& aSymbolInstance )
865{
866 bool referencePlaced = false;
867 bool valuePlaced = false;
868
869 // The positions are in board coordinates, so they are applied after the footprint is placed. The first
870 // reference and value positions place the fields; a further reference position is a text of its own
872 {
873 if( ( vp->Types & VALUE_REFERENCE_NAME ) && !referencePlaced )
874 {
875 bool placed = ApplyTextPosition( aFootprint.Reference(), *vp, m_frame, *m_layers );
876 aFootprint.Reference().SetVisible( placed && vp->Displayed );
877 referencePlaced = true;
878 }
879 else if( ( vp->Types & VALUE_COMPONENT_NAME ) && !valuePlaced )
880 {
881 bool placed = ApplyTextPosition( aFootprint.Value(), *vp, m_frame, *m_layers );
882 aFootprint.Value().SetVisible( placed && vp->Displayed );
883 valuePlaced = true;
884 }
885 else if( ( vp->Types & VALUE_REFERENCE_NAME ) && vp->Displayed )
886 {
887 std::unique_ptr<PCB_TEXT> item = std::make_unique<PCB_TEXT>( &aFootprint );
888 item->SetText( aInstance.Name );
889
890 if( ApplyTextPosition( *item, *vp, m_frame, *m_layers ) )
891 aFootprint.Add( item.release() );
892 }
893 }
894
895 if( !referencePlaced )
896 aFootprint.Reference().SetVisible( false );
897
898 if( !valuePlaced )
899 aFootprint.Value().SetVisible( false );
900}
901
902
903void PCB_BUILDER::buildFreeItems()
904{
905 // The design list also holds every area's stored pour; buildAreas() makes those zone fills
906 std::set<const SOURCE_FREE_COPPER*> poured;
907
908 for( const SOURCE_AREA* area : TypedItems<const SOURCE_AREA>( m_design->Areas ) )
909 {
910 if( becomesZone( *area ) )
911 {
912 for( const SOURCE_FREE_COPPER* piece : TypedItems<const SOURCE_FREE_COPPER>( area->Copper ) )
913 poured.insert( piece );
914 }
915 }
916
917 for( const SOURCE_FREE_COPPER* copper : TypedItems<const SOURCE_FREE_COPPER>( m_design->Coppers ) )
918 {
919 if( poured.count( copper ) )
920 continue;
921
922 NETINFO_ITEM* net = nullptr;
923
924 for( const SOURCE_COPPER_TERMINAL* term : TypedItems<const SOURCE_COPPER_TERMINAL>( copper->Terminals ) )
925 {
926 if( ( net = netOfNode( term->Node ) ) != nullptr )
927 break;
928 }
929
930 for( PCB_SHAPE* shape : AddShapeItem( m_board, *copper, m_frame, *m_layers ) )
931 {
932 if( net && IsCopperLayer( shape->GetLayer() ) )
933 shape->SetNet( net );
934
935 m_created[copper].push_back( shape );
936 }
937 }
938
940 {
941 if( PCB_TEXT* ktext = AddText( m_board, *text, text->Text, m_frame, *m_layers ) )
942 m_created[text].push_back( ktext );
943 }
944
945 for( const SOURCE_FREE_PAD* freePad : TypedItems<const SOURCE_FREE_PAD>( m_design->Pads ) )
946 {
947 const SOURCE_PAD_STYLE* style = dynamic_cast<const SOURCE_PAD_STYLE*>( freePad->Style );
948
949 if( !style )
950 continue;
951
952 // KiCad pads live in footprints, so each free pad gets a footprint of its own at the pad position
953 FOOTPRINT* fp = new FOOTPRINT( &m_board );
954 fp->SetReference( wxEmptyString );
955 fp->Reference().SetVisible( false );
956 fp->Value().SetVisible( false );
957
958 std::unique_ptr<PAD> pad = CreatePad( fp, *freePad, *style, FRAME{ freePad->Position }, *m_layers );
959
960 if( NETINFO_ITEM* net = netOfNode( freePad->Node ) )
961 pad->SetNet( net );
962
963 fp->Add( pad.release() );
964 fp->SetPosition( m_frame.ToKiCad( freePad->Position ) );
965 m_board.Add( fp );
966 m_created[freePad].push_back( fp );
967 }
968}
969
970
971void PCB_BUILDER::buildTracksAndVias()
972{
973 std::set<const SOURCE_VIA*> vias;
974
975 for( const SOURCE_NET* net : TypedItems<const SOURCE_NET>( m_design->Nets ) )
976 {
977 NETINFO_ITEM* info = m_nets[net];
978
979 for( const SOURCE_NODE* node : TypedItems<const SOURCE_NODE>( net->Nodes ) )
980 {
981 const SOURCE_VIA* via = dynamic_cast<const SOURCE_VIA*>( node->Item );
982 const SOURCE_PAD_STYLE* style = via ? dynamic_cast<const SOURCE_PAD_STYLE*>( via->Style ) : nullptr;
983
984 if( !style || !vias.insert( via ).second )
985 continue;
986
987 PCB_VIA* kvia = new PCB_VIA( &m_board );
988 kvia->SetPosition( m_frame.ToKiCad( via->Position ) );
989 kvia->SetWidth( PADSTACK::ALL_LAYERS, m_frame.Length( style->Size ) );
990 kvia->SetDrill( m_frame.Length( style->Drill ) );
991 kvia->SetNet( info );
992
993 // The net class forces tenting (0 open, 1 tented) or, with 2, defers to the via
994 const SOURCE_NET_CLASS* netClass = dynamic_cast<const SOURCE_NET_CLASS*>( net->NetClass );
995 int rule = netClass ? netClass->TentedVias : 2;
996 bool tented = rule == 1 || ( rule == 2 && via->Tented );
997
998 kvia->SetFrontTentingMode( !tented && m_viaMask[0] ? TENTING_MODE::NOT_TENTED : TENTING_MODE::TENTED );
999 kvia->SetBackTentingMode( !tented && m_viaMask[1] ? TENTING_MODE::NOT_TENTED : TENTING_MODE::TENTED );
1000 m_board.Add( kvia );
1001 m_created[via].push_back( kvia );
1002 }
1003
1004 for( const SOURCE_CONNECTION* conn : TypedItems<const SOURCE_CONNECTION>( net->Connections ) )
1005 {
1006 const SOURCE_TRACK* track = dynamic_cast<const SOURCE_TRACK*>( conn->Track );
1007 PCB_LAYER_ID layer = track ? m_layers->Map( track->Layer ) : UNDEFINED_LAYER;
1008
1009 if( layer == UNDEFINED_LAYER )
1010 continue;
1011
1012 std::vector<const SOURCE_TRACK_SEGMENT*> verts;
1013
1014 for( const OBJECT* obj : track->Segments )
1015 {
1016 if( const SOURCE_TRACK_SEGMENT* seg = dynamic_cast<const SOURCE_TRACK_SEGMENT*>( obj ) )
1017 verts.push_back( seg );
1018 }
1019
1020 auto add = [&]( PCB_TRACK* aItem, int aWidth )
1021 {
1022 aItem->SetWidth( aWidth );
1023 aItem->SetLayer( layer );
1024 aItem->SetNet( info );
1025 m_board.Add( aItem );
1026 m_created[conn].push_back( aItem );
1027 };
1028
1029 for( size_t i = 0; i + 1 < verts.size(); ++i )
1030 {
1031 const SOURCE_TRACK_SEGMENT* s = verts[i];
1032 const SOURCE_TRACK_SEGMENT* e = verts[i + 1];
1033 const SOURCE_TRACK_STYLE* style = dynamic_cast<const SOURCE_TRACK_STYLE*>( s->Style );
1034
1035 // A span's width is its own style's, with no fallback to the track's default style
1036 int width = style ? m_frame.Length( style->Width ) : 0;
1037
1038 // A zero-length span plots as a dot of the track width, which a degenerate track also does
1039 int32_t angle = s->X != e->X || s->Y != e->Y ? s->ArcAngle : 0;
1040 ARC_FORM form =
1041 ArcForm( POINT32{ s->X, s->Y }, POINT32{ e->X, e->Y }, angle, s->Anticlockwise, m_frame );
1042
1043 if( form.IsArc() )
1044 {
1045 PCB_ARC* arc = new PCB_ARC( &m_board );
1046 arc->SetStart( form.Start );
1047 arc->SetMid( form.Mid );
1048 arc->SetEnd( form.End );
1049 add( arc, width );
1050 }
1051
1052 for( size_t k = 1; k < form.Points.size(); ++k )
1053 {
1054 PCB_TRACK* ktrack = new PCB_TRACK( &m_board );
1055 ktrack->SetStart( form.Points[k - 1] );
1056 ktrack->SetEnd( form.Points[k] );
1057 add( ktrack, width );
1058 }
1059 }
1060 }
1061 }
1062}
1063
1064
1065bool PCB_BUILDER::becomesZone( const SOURCE_AREA& aArea ) const
1066{
1067 return aArea.Type == AREA_COPPER && !( aArea.Flags & AREA_POUR_KEEPOUT )
1068 && dynamic_cast<const SOURCE_DESIGN_SHAPE*>( aArea.Shape )
1069 && m_layers->Map( aArea.Layer ) != UNDEFINED_LAYER;
1070}
1071
1072
1073void PCB_BUILDER::buildAreas()
1074{
1075 int routingAreas = 0;
1076 int ruleAreas = 0;
1077
1078 for( const SOURCE_AREA* area : TypedItems<const SOURCE_AREA>( m_design->Areas ) )
1079 {
1080 const SOURCE_DESIGN_SHAPE* shape = dynamic_cast<const SOURCE_DESIGN_SHAPE*>( area->Shape );
1081
1082 if( !shape )
1083 continue;
1084
1085 // A pour keepout keeps every zone fill out; KiCad has no way to limit that to one net
1086 if( area->Flags & AREA_POUR_KEEPOUT )
1087 {
1088 LSET copper = m_layers->PadLayers( area->Layer, false ) & LSET::AllCuMask();
1089
1090 if( copper.none() )
1091 continue;
1092
1093 SHAPE_POLY_SET outline = ShapeToPolySet( *shape, m_frame );
1094 outline.ClearArcs();
1095
1096 ZONE* zone = new ZONE( &m_board );
1097 ++ruleAreas;
1098 zone->SetIsRuleArea( true );
1099 zone->SetZoneName( area->Name.IsEmpty() ? wxString::Format( wxS( "Easy-PC area %d" ), ruleAreas )
1100 : area->Name );
1101 zone->SetLayerSet( copper );
1102 *zone->Outline() = outline;
1103 zone->SetDoNotAllowTracks( false );
1104 zone->SetDoNotAllowVias( false );
1105 zone->SetDoNotAllowZoneFills( true );
1106 zone->SetDoNotAllowPads( false );
1107 zone->SetDoNotAllowFootprints( false );
1108 m_board.Add( zone );
1109 continue;
1110 }
1111
1112 // The routing area check is off in every known design, so these restrict nothing
1113 if( area->Type == AREA_ROUTING )
1114 {
1115 PCB_GROUP* group = new PCB_GROUP( &m_board );
1116 group->SetName( area->Name.IsEmpty() ? wxString( wxS( "Easy-PC routing area" ) ) : area->Name );
1117
1118 for( PCB_SHAPE* outline : AddShapeItem( m_board, *area, m_frame, *m_layers, Cmts_User ) )
1119 group->AddItem( outline );
1120
1121 ++routingAreas;
1122
1123 if( group->GetItems().empty() )
1124 {
1125 delete group;
1126 continue;
1127 }
1128
1129 m_board.Add( group );
1130 m_created[area].push_back( group );
1131 continue;
1132 }
1133
1134 if( !becomesZone( *area ) )
1135 continue;
1136
1137 PCB_LAYER_ID layer = m_layers->Map( area->Layer );
1138 ZONE* zone = new ZONE( &m_board );
1139 zone->SetLayer( layer );
1140 *zone->Outline() = ShapeToPolySet( *shape, m_frame );
1141
1142 if( auto netIt = m_nets.find( area->Net ); netIt != m_nets.end() )
1143 zone->SetNet( netIt->second );
1144
1145 // Pours clear each item by the shape spacing plus the pour net's guard; the
1146 // smallest over the item kinds is the zone's own clearance, larger kinds are left to the custom rules
1147 const SOURCE_NET* net = dynamic_cast<const SOURCE_NET*>( area->Net );
1148 int32_t clearance = -1;
1149
1150 for( int kind : { SPACING_TRACK, SPACING_PAD, SPACING_VIA, SPACING_SHAPE } )
1151 {
1152 int32_t spacing = shapeSpacing( kind );
1153
1154 if( spacing >= 0 && ( clearance < 0 || spacing < clearance ) )
1155 clearance = spacing;
1156 }
1157
1158 if( clearance >= 0 )
1159 zone->SetLocalClearance( m_frame.Length( int64_t( clearance ) + ( net ? net->GuardSpacing : 0 ) ) );
1160
1161 // Keep the imported pour within a micron of its stored outline.
1162 SHAPE_POLY_SET fill;
1163 int maxError = std::min( m_board.GetDesignSettings().m_MaxError, 1000 );
1164
1165 // One union of all pieces; adding them one by one is quadratic on large pours
1166 for( const SOURCE_FREE_COPPER* poured : TypedItems<const SOURCE_FREE_COPPER>( area->Copper ) )
1167 {
1168 SHAPE_POLY_SET piece = ShapeItemArea( *poured, m_frame, maxError );
1169
1170 for( int i = 0; i < piece.OutlineCount(); ++i )
1171 fill.AddPolygon( piece.Polygon( i ) );
1172 }
1173
1174 if( !fill.IsEmpty() )
1175 {
1176 fill.Simplify();
1177 fill.Fracture();
1178 zone->SetFilledPolysList( layer, fill );
1179 zone->SetIsFilled( true );
1180 zone->SetNeedRefill( false );
1181 }
1182
1183 m_board.Add( zone );
1184 }
1185
1186 if( routingAreas && m_reporter )
1187 {
1188 m_reporter->Report( wxString::Format( _( "%d Easy-PC routing areas were imported as outlines on Cmts.User; "
1189 "the design does not run its routing area check, so they restrict "
1190 "nothing." ),
1191 routingAreas ),
1193 }
1194}
1195
1196
1197wxString BuildBoard( const DESIGN_DOCUMENT& aDocument, BOARD& aBoard, REPORTER* aReporter,
1198 const LAYER_MAPPING_HANDLER& aLayerMapping )
1199{
1200 PCB_BUILDER builder( aDocument, aBoard, aReporter );
1201
1202 if( aLayerMapping )
1203 builder.m_layers->ApplyUserMapping( aLayerMapping( builder.m_layers->Describe() ) );
1204
1205 builder.Build();
1206 return builder.m_customRules;
1207}
1208
1209} // namespace EASYPC_PCB
const char * name
@ ERROR_OUTSIDE
@ ERROR_INSIDE
@ ZLO_FORCE_FLASHED
Definition board_item.h:74
BOX2< VECTOR2L > BOX2L
Definition box2.h:916
BASE_SET & set(size_t pos)
Definition base_set.h:126
virtual void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
bool m_LegacyDesignSettingsLoaded
True if the legacy board design settings were loaded from a file.
Definition board.h:564
void SetVisibleLayers(const LSET &aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings changes the bit-mask of vis...
Definition board.cpp:1218
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1524
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition board.cpp:961
const FOOTPRINTS & Footprints() const
Definition board.h:464
const TRACKS & Tracks() const
Definition board.h:462
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition board.h:1019
bool m_LegacyNetclassesLoaded
True if netclasses were loaded from the file.
Definition board.h:568
void SetCopperLayerCount(int aCount)
Definition board.cpp:1144
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1301
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1185
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false, bool aPhysicalLayersOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition board.cpp:2742
void SetEnabledLayers(const LSET &aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:1205
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition board.h:1026
constexpr const Vec GetEnd() const
Definition box2.h:209
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:594
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:78
void AddItem(EDA_ITEM *aItem)
Add item to group.
Definition eda_group.cpp:58
void SetName(const wxString &aName)
Definition eda_group.h:76
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:116
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
void SetPosition(const VECTOR2I &aPos) override
PCB_FIELD & Value()
read/write accessors:
Definition footprint.h:947
void SetReference(const wxString &aReference)
Definition footprint.h:915
PCB_FIELD & Reference()
Definition footprint.h:948
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Adds an item to the container.
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
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
static const LSET & InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
Definition lset.cpp:573
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:43
void SetViaDiameter(int aDia)
Definition netclass.h:152
void SetViaDrill(int aSize)
Definition netclass.h:160
static const char Default[]
the name of the default NETCLASS
Definition netclass.h:45
void SetDiffPairWidth(int aSize)
Definition netclass.h:184
void SetDiffPairGap(int aSize)
Definition netclass.h:192
void SetClearance(int aClearance)
Definition netclass.h:136
void SetTrackWidth(int aWidth)
Definition netclass.h:144
Handle the data for a net.
Definition netinfo.h:50
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
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE, bool aIgnoreLineWidth=false) const override
Convert the pad shape to a closed polygon.
Definition pad.cpp:3046
void SetSize(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition pad.cpp:265
void SetMid(const VECTOR2I &aMid)
Definition pcb_track.h:294
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:94
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:97
virtual void SetWidth(int aWidth)
Definition pcb_track.h:91
void SetFrontTentingMode(TENTING_MODE aMode)
void SetDrill(int aDrill)
Definition pcb_track.h:798
void SetBackTentingMode(TENTING_MODE aMode)
void SetPosition(const VECTOR2I &aPoint) override
Definition pcb_track.h:600
void SetWidth(int aWidth) override
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
Represent a set of closed polygons.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the set.
int AddPolygon(const POLYGON &apolygon)
Adds a polygon to the set.
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
void Deflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError)
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.
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition title_block.h:38
Handle a list of polygons defining a copper zone.
Definition zone.h:70
void SetNeedRefill(bool aNeedRefill)
Definition zone.h:310
void SetDoNotAllowPads(bool aEnable)
Definition zone.h:851
void SetLocalClearance(std::optional< int > aClearance)
Definition zone.h:183
void SetMinThickness(int aMinThickness)
Definition zone.h:316
void SetThermalReliefSpokeWidth(int aThermalReliefSpokeWidth)
Definition zone.h:251
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:640
SHAPE_POLY_SET * Outline()
Definition zone.h:421
void SetIsRuleArea(bool aEnable)
Definition zone.h:833
void SetDoNotAllowTracks(bool aEnable)
Definition zone.h:850
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition zone.h:746
void SetIsFilled(bool isFilled)
Definition zone.h:307
void SetLayerSet(const LSET &aLayerSet) override
Definition zone.cpp:665
void SetDoNotAllowVias(bool aEnable)
Definition zone.h:849
void SetNet(NETINFO_ITEM *aNetInfo) override
Override that drops aNetInfo when this zone is in copper-thieving fill mode.
Definition zone.cpp:631
void SetThermalReliefGap(int aThermalReliefGap)
Definition zone.h:240
void SetDoNotAllowFootprints(bool aEnable)
Definition zone.h:852
void SetDoNotAllowZoneFills(bool aEnable)
Definition zone.h:848
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition zone.h:313
void SetZoneName(const wxString &aName)
Definition zone.h:161
void SetIslandRemovalMode(ISLAND_REMOVAL_MODE aRemove)
Definition zone.h:855
A type-safe container of any type.
Definition ki_any.h:92
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
void TransformTrapezoidToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aRotation, int aDeltaX, int aDeltaY, int aInflate, int aError, ERROR_LOC aErrorLoc)
Convert a rectangle or trapezoid to a polygon.
@ ROUND_ALL_CORNERS
All angles are rounded.
Placed instances and connectivity: component, symbol and pad instances, nets, nodes,...
Symbols, components and gates, embedded in designs or the single object of a library item stream.
The design document and the design parameter tables the importers use.
Title block and page size, shared by the board and schematic builders.
Loading designs, library items and projects.
Easy-PC / DesignSpark items to KiCad board items, shared by the board builder and the ....
Easy-PC's text measurement, so text lands where Easy-PC draws it.
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:450
static constexpr EDA_ANGLE ANGLE_45
Definition eda_angle.h:449
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
std::string source
bool IsUserLayer(PCB_LAYER_ID aLayerId)
Test whether a layer is a non copper and a non tech layer.
Definition layer_ids.h:785
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:703
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ Edge_Cuts
Definition layer_ids.h:108
@ F_Paste
Definition layer_ids.h:100
@ Cmts_User
Definition layer_ids.h:104
@ B_Mask
Definition layer_ids.h:94
@ F_Mask
Definition layer_ids.h:93
@ B_Paste
Definition layer_ids.h:101
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ LEFT_RIGHT
Flip left to right (around the Y axis)
Definition mirror.h:24
std::unique_ptr< FOOTPRINT > ConvertFootprint(const SOURCE_SYMBOL &aSymbol, const LAYER_MAPPER &aLayers, const LIB_ID &aId, const SOURCE_PCB_COMPONENT *aComponent, std::map< const SOURCE_FREE_PAD *, PAD * > *aPadMap)
A footprint in symbol coordinates.
SHAPE_POLY_SET ShapeToPolySet(const SOURCE_DESIGN_SHAPE &aShape, const FRAME &aFrame)
A closed shape and its cutouts as an outline with holes.
std::unique_ptr< PAD > CreatePad(FOOTPRINT *aParent, const SOURCE_FREE_PAD &aPad, const SOURCE_PAD_STYLE &aStyle, const FRAME &aFrame, const LAYER_MAPPER &aLayers)
A pad with its style's per-layer exceptions; a mask or paste exception becomes a separate opening on ...
PCB_TEXT * AddText(BOARD_ITEM_CONTAINER &aContainer, const SOURCE_TEXT_POSITION &aPosition, const wxString &aText, const FRAME &aFrame, const LAYER_MAPPER &aLayers)
ARC_FORM ArcForm(const POINT32 &aStart, const POINT32 &aEnd, int32_t aArcAngle, bool aAnticlockwise, const FRAME &aFrame)
std::vector< PCB_SHAPE * > AddShapeItem(BOARD_ITEM_CONTAINER &aContainer, const SOURCE_SHAPE_ITEM &aItem, const FRAME &aFrame, const LAYER_MAPPER &aLayers, PCB_LAYER_ID aLayer)
Add a shape item as one filled polygon, a circle, or one shape per span; aLayer overrides the item's ...
wxString BuildBoard(const DESIGN_DOCUMENT &aDocument, BOARD &aBoard, REPORTER *aReporter, const LAYER_MAPPING_HANDLER &aLayerMapping)
Fill aBoard from a loaded Easy-PC / DesignSpark PCB design.
SHAPE_POLY_SET ShapeItemArea(const SOURCE_SHAPE_ITEM &aItem, const FRAME &aFrame, int aMaxError)
The copper a shape item plots: its filled region and every span stroked at the line width with round ...
@ DP_MIN_SOLDER_MASK_TO_TRACK
PAGE_INFO PageForExtents(const BOX2L &aExtentsNm)
Easy-PC stores no page size, so the first of A4 to A0 then A to E, landscape before portrait,...
std::vector< T * > TypedItems(const OBJECT *aList)
The items of ListItems that are a T, in order.
@ FIXED
not ABSOLUTE, which wingdi.h defines
wxString ToKiCadMarkup(const wxString &aText)
A displayed string in KiCad's markup.
PAD_SHAPE
Pad style shape, also the drill and exception shape.
void ApplyTextPosition(EDA_TEXT &aText, const SOURCE_TEXT_POSITION &aPos, const SCH_FRAME &aFrame, bool aKeepUpright, const wxString &aMeasured)
Place any KiCad text as the source position says: anchor, angle, mirror, justification and style.
constexpr int64_t DsuToNm(int64_t aDsu)
void FillTitleBlock(const DESIGN_DOCUMENT &aDoc, TITLE_BLOCK &aTitleBlock)
Fill a title block from the design's SummaryInformation property set: title from Title,...
@ PTH
Plated through hole pad.
Definition padstack.h:97
@ CHAMFERED_RECT
Definition padstack.h:59
@ ROUNDRECT
Definition padstack.h:56
@ RECTANGLE
Definition padstack.h:53
Class to handle a set of BOARD_ITEMs.
std::function< std::map< wxString, PCB_LAYER_ID >(const std::vector< INPUT_LAYER_DESC > &)> LAYER_MAPPING_HANDLER
Pointer to a function that takes a map of source and KiCad layers and returns a re-mapped version.
@ RPT_SEVERITY_WARNING
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_LIBID
std::unique_ptr< OBJECT > Design
design root, not in the load array
Root of every deserialized object.
A point read as two int32 values.
SOURCE_SCM_COMPONENT * Schematic
OBJECT * Parent
null is the document in a design, nothing in a library item
int32_t Param(int aIndex, int32_t aDefault=0) const
The value at aIndex, or aDefault where the stored array is shorter, as the accessors do.
The document: OLE items, then the design.
SOURCE_DESIGN_PARAMETERS DesignParameters
after 8000
Every drawn shape, pour and dimension part.
A pad of a symbol definition.
Also the start of a dimension.
Layer type names determine silk, mask and paste roles.
int32_t TentedVias
0 none, 1 all, 2 per via
OBJECT * NetClass
the design's first class when the file stores none
The package half of a component.
int32_t ArcAngle
swept angle, 0 for a straight span
bool IsClosed() const
The last vertex links back to the first (formats 1 and 2) or the closed byte is set (format 3)
std::vector< OBJECT * > Segments
the vertex list as read
values maps (a << 16) | b of SPACING_ITEM bits to a clearance
std::map< int32_t, int32_t > Values
A placed footprint (gate -1) or a placed schematic gate.
OBJECT * ValuePositions
below 10003 built from the stored symbol name
Footprints and schematic symbols alike.
OBJECT * Style
track style of the span leaving this vertex
OBJECT * Layer
null in a schematic
A span as KiCad can hold it: a true arc, or a polyline that is the two ends of a straight span or poi...
std::vector< VECTOR2I > Points
Native to KiCad coordinates: scale by 100 nm, flip Y and move the native origin to the KiCad offset.
static EDA_ANGLE Angle(int32_t aMilliDegrees)
VECTOR2I ToKiCad(int32_t aX, int32_t aY) const
int Length(int64_t aLength) const
netlist clear()
int clearance
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
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< int64_t > VECTOR2L
Definition vector2d.h:709
@ THERMAL
Use thermal relief for pads.
Definition zones.h:46