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