KiCad PCB EDA Suite
Loading...
Searching...
No Matches
target3001_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
21
22#include <algorithm>
23#include <cmath>
24
25#include <base_units.h>
26#include <board.h>
29#include <eda_units.h>
30#include <footprint.h>
32#include <ki_exception.h>
33#include <macros.h>
34#include <netinfo.h>
35#include <pad.h>
36#include <pcb_shape.h>
37#include <pcb_text.h>
38#include <pcb_track.h>
39#include <reporter.h>
40#include <zone.h>
41
42#include <wx/tokenzr.h>
43
48using TARGET3001::ARC;
61
62namespace
63{
64
65// Corner chamfer of an octagonal pad as a fraction of its smaller side
66constexpr double OCTAGON_CHAMFER_RATIO = 0.25;
67
68EDA_ANGLE angle( float aDegrees )
69{
70 return EDA_ANGLE( double( aDegrees ), DEGREES_T );
71}
72
73
74LINE_STYLE lineStyle( uint8_t aDash )
75{
76 switch( aDash )
77 {
78 case 1: return LINE_STYLE::DASH;
79 case 2: return LINE_STYLE::DASHDOT;
80 case 3: return LINE_STYLE::DASHDOTDOT;
81 case 4: return LINE_STYLE::DOT;
82 default: return LINE_STYLE::SOLID;
83 }
84}
85
86
88std::vector<VECTOR2I> parsePolyPad( const std::string& aValue )
89{
90 std::vector<VECTOR2I> pts;
91 wxStringTokenizer tokens( wxString( aValue ), wxS( ";" ), wxTOKEN_STRTOK );
92
93 while( tokens.HasMoreTokens() )
94 {
95 wxString pair = tokens.GetNextToken();
96 long x = 0;
97 long y = 0;
98
99 if( !pair.BeforeFirst( ',' ).ToLong( &x ) || !pair.AfterFirst( ',' ).ToLong( &y ) )
100 return {};
101
102 pts.emplace_back( int( x ), int( y ) );
103 }
104
105 return pts;
106}
107
108} // namespace
109
110
112 REPORTER* aReporter ) :
113 m_board( aBoard ),
114 m_project( aProject ),
115 m_reporter( aReporter )
116{
117}
118
119
121{
123 buildNets();
130
131 // A pour runs up to a contour stroke without clearance; KiCad has one edge clearance per board
133 m_board->GetDesignSettings().m_CopperEdgeClearance = 0;
134
135 // BOARD::SetProject keeps importer settings only when they are flagged as loaded
136 m_board->m_LegacyDesignSettingsLoaded = true;
137
138 // TARGET 3001! places the board around its own origin with Y up; move it onto the sheet
139 BOX2I bbox = m_board->GetBoardEdgesBoundingBox();
140
141 if( bbox.GetWidth() == 0 || bbox.GetHeight() == 0 )
142 bbox = m_board->ComputeBoundingBox( false );
143
144 const int margin = pcbIUScale.mmToIU( 20 );
145 m_board->Move( VECTOR2I( margin - bbox.GetX(), margin - bbox.GetY() ) );
146}
147
148
150{
151 if( aLayer < 0 || aLayer >= BOARD_LAYER_COUNT )
152 return LAYER_FUNCTION::OTHER;
153
154 return LAYER_FUNCTION( m_project.layerFunctions[aLayer] );
155}
156
157
158bool TARGET3001_PCB_BUILDER::isCopper( int aLayer ) const
159{
160 LAYER_FUNCTION f = LayerFunction( aLayer );
161
162 return f == LAYER_FUNCTION::COPPER_BOTTOM || f == LAYER_FUNCTION::COPPER_INSIDE
163 || f == LAYER_FUNCTION::COPPER_TOP;
164}
165
166
168static wxString ruleNetLiteral( const wxString& aNetName )
169{
170 wxString text = aNetName;
171 text.Replace( wxS( "\\" ), wxS( "\\\\" ) );
172 text.Replace( wxS( "'" ), wxS( "\\\\'" ) );
173 text.Replace( wxS( "\"" ), wxS( "\\\"" ) );
174 return wxS( "'" ) + text + wxS( "'" );
175}
176
177
179{
180 if( m_pourLayers.empty() )
181 return;
182
183 auto mostCommon = []( const std::map<int, int>& aCounts )
184 {
185 return std::max_element( aCounts.begin(), aCounts.end(),
186 []( const auto& a, const auto& b )
187 {
188 return a.second < b.second;
189 } )->first;
190 };
191
192 std::map<int, int> boardCounts;
193 std::map<int, std::vector<wxString>> netsByClearance;
194
195 for( const auto& [signal, counts] : m_signalClearances )
196 {
197 NETINFO_ITEM* item = net( signal );
198
199 if( !item || item->GetNetCode() <= 0 || counts.empty() )
200 continue;
201
202 netsByClearance[mostCommon( counts )].push_back( item->GetNetname() );
203
204 for( const auto& [clearance, count] : counts )
205 boardCounts[clearance] += count;
206 }
207
208 if( boardCounts.empty() )
209 return;
210
211 auto mm = []( int aValue )
212 {
213 return wxString::FromUTF8( EDA_UNIT_UTILS::FormatInternalUnits( pcbIUScale, aValue ) ) + wxS( "mm" );
214 };
215
216 auto addRule = [&]( const wxString& aName, int aClearance, const wxString& aCondition )
217 {
218 m_designRules += wxString::Format( wxS( "\n(rule \"%s\"\n (constraint clearance (min %s))\n"
219 " (condition \"%s\"))\n" ),
220 aName, mm( aClearance ), aCondition );
221 };
222
223 // An area keeps each element's own clearance, which KiCad tracks and vias cannot hold
224 int base = mostCommon( boardCounts );
225 m_designRules = wxS( "(version 1)\n" );
226 addRule( wxS( "TARGET 3001! copper areas" ), base, wxS( "A.Type == 'Zone'" ) );
227
228 for( const auto& [clearance, nets] : netsByClearance )
229 {
230 if( clearance == base )
231 continue;
232
233 wxString condition;
234
235 for( const wxString& name : nets )
236 {
237 if( !condition.IsEmpty() )
238 condition += wxS( " || " );
239
240 condition += wxS( "B.NetName == " ) + ruleNetLiteral( name );
241 }
242
243 addRule( wxS( "TARGET 3001! copper areas to " ) + mm( clearance ) + wxS( " signals" ), clearance,
244 wxS( "A.Type == 'Zone' && (" ) + condition + wxS( ")" ) );
245 }
246}
247
248
250{
251 for( const AURA_PAD& entry : m_auraPads )
252 {
253 bool hasArea = false;
254 bool joined = false;
255
256 for( PCB_LAYER_ID layer : entry.pad->GetLayerSet().CuStack() )
257 {
258 if( !m_pourLayers.count( { entry.signal, layer } ) )
259 continue;
260
261 hasArea = true;
262 joined |= entry.noAura.count( layer ) > 0;
263 }
264
265 // An isolated pad reaches its area only through the signal's own tracks; a pad joined on
266 // some layers stays joined on all so no connection is lost
267 if( hasArea )
268 entry.pad->SetLocalZoneConnection( joined ? ZONE_CONNECTION::FULL : ZONE_CONNECTION::NONE );
269 }
270}
271
272
274{
275 std::vector<int> top;
276 std::vector<int> inner;
277 std::vector<int> bottom;
278
279 for( int layer = 0; layer < BOARD_LAYER_COUNT; layer++ )
280 {
281 switch( LayerFunction( layer ) )
282 {
283 case LAYER_FUNCTION::COPPER_TOP: top.push_back( layer ); break;
284 case LAYER_FUNCTION::COPPER_INSIDE: inner.push_back( layer ); break;
285 case LAYER_FUNCTION::COPPER_BOTTOM: bottom.push_back( layer ); break;
286 default: break;
287 }
288 }
289
290 m_copperStack.clear();
291 m_copperStack.insert( m_copperStack.end(), top.begin(), top.end() );
292 // Inner copper layer numbers increase towards the top side
293 m_copperStack.insert( m_copperStack.end(), inner.rbegin(), inner.rend() );
294 m_copperStack.insert( m_copperStack.end(), bottom.begin(), bottom.end() );
295
296 if( m_copperStack.size() > MAX_CU_LAYERS )
297 {
298 THROW_IO_ERROR( wxString::Format( _( "TARGET 3001! board has %zu copper layers; at most %d are supported." ),
299 m_copperStack.size(), MAX_CU_LAYERS ) );
300 }
301
302 int copperCount = std::max<int>( 2, m_copperStack.size() );
303
304 if( copperCount % 2 )
305 copperCount++;
306
307 m_board->SetCopperLayerCount( copperCount );
308
309 for( size_t i = 0; i < m_copperStack.size(); i++ )
310 {
311 PCB_LAYER_ID id;
312
313 // A board with only bottom copper keeps it on the bottom
314 if( i + 1 == m_copperStack.size() && !bottom.empty() )
315 id = B_Cu;
316 else if( i == 0 )
317 id = F_Cu;
318 else
319 id = BoardLayerFromLegacyId( int( i ) );
320
321 m_layerMap[m_copperStack[i]] = id;
322 }
323
324 for( int layer = 0; layer < BOARD_LAYER_COUNT && !m_contourLayer; layer++ )
325 {
326 if( LayerFunction( layer ) == LAYER_FUNCTION::OUTLINE )
327 m_contourLayer = layer;
328 }
329
330 int nextUser = 0;
331
332 for( int layer = 0; layer < BOARD_LAYER_COUNT; layer++ )
333 {
334 if( m_layerMap.count( layer ) )
335 continue;
336
337 std::optional<PCB_LAYER_ID> id;
338
339 switch( LayerFunction( layer ) )
340 {
341 case LAYER_FUNCTION::POSITION_BOTTOM: id = B_SilkS; break;
342 case LAYER_FUNCTION::POSITION_TOP: id = F_SilkS; break;
343 case LAYER_FUNCTION::NAMES_BOTTOM: id = B_SilkS; break;
344 case LAYER_FUNCTION::NAMES_TOP: id = F_SilkS; break;
345 case LAYER_FUNCTION::VALUES_BOTTOM: id = B_Fab; break;
346 case LAYER_FUNCTION::VALUES_TOP: id = F_Fab; break;
347 case LAYER_FUNCTION::MASK_BOTTOM: id = B_Mask; break;
348 case LAYER_FUNCTION::MASK_TOP: id = F_Mask; break;
349 case LAYER_FUNCTION::PASTE_BOTTOM: id = B_Paste; break;
350 case LAYER_FUNCTION::PASTE_TOP: id = F_Paste; break;
351 case LAYER_FUNCTION::GLUE_BOTTOM: id = B_Adhes; break;
352 case LAYER_FUNCTION::GLUE_TOP: id = F_Adhes; break;
353 // Milling is a tool path around the outline, so it stays on its own user layer
354 case LAYER_FUNCTION::OUTLINE: id = Edge_Cuts; break;
355 case LAYER_FUNCTION::DIMENSIONING: id = Dwgs_User; break;
356 case LAYER_FUNCTION::DRILL_PLAN: id = Cmts_User; break;
357
358 // Pour areas and keepouts become zones; air wires are derived data
359 case LAYER_FUNCTION::AREA_BOTTOM:
360 case LAYER_FUNCTION::AREA_INSIDE:
361 case LAYER_FUNCTION::AREA_TOP:
362 case LAYER_FUNCTION::DELETION_BOTTOM:
363 case LAYER_FUNCTION::DELETION_INSIDE:
364 case LAYER_FUNCTION::DELETION_TOP:
365 case LAYER_FUNCTION::AIR_WIRES:
366 break;
367
368 default:
369 if( nextUser < 9 )
370 id = static_cast<PCB_LAYER_ID>( User_1 + 2 * nextUser++ );
371 else
372 id = Cmts_User;
373
374 break;
375 }
376
377 if( id )
378 m_layerMap[layer] = *id;
379 }
380
381 LSET enabled = m_board->GetEnabledLayers();
382
383 for( const auto& [target, id] : m_layerMap )
384 enabled.set( id );
385
386 m_board->SetEnabledLayers( enabled );
387 m_board->SetVisibleLayers( enabled );
388
389 for( const auto& [target, id] : m_layerMap )
390 {
391 if( id >= User_1 && id <= User_9 )
392 m_board->SetLayerName( id, wxString::Format( wxS( "Target layer %d" ), target ) );
393 }
394}
395
396
397std::optional<PCB_LAYER_ID> TARGET3001_PCB_BUILDER::mapLayer( int aLayer ) const
398{
399 auto it = m_layerMap.find( aLayer );
400
401 if( it == m_layerMap.end() )
402 return std::nullopt;
403
404 return it->second;
405}
406
407
408std::optional<PCB_LAYER_ID> TARGET3001_PCB_BUILDER::pourCopperLayer( int aLayer ) const
409{
410 int defaultCopper;
411
412 switch( LayerFunction( aLayer ) )
413 {
414 case LAYER_FUNCTION::AREA_BOTTOM:
415 case LAYER_FUNCTION::AREA_INSIDE:
416 case LAYER_FUNCTION::AREA_TOP:
417 defaultCopper = aLayer + 2;
418 break;
419
420 case LAYER_FUNCTION::DELETION_BOTTOM:
421 case LAYER_FUNCTION::DELETION_INSIDE:
422 case LAYER_FUNCTION::DELETION_TOP:
423 defaultCopper = aLayer + 1;
424 break;
425
426 default:
427 return std::nullopt;
428 }
429
430 int copper = m_project.layerAssociations[aLayer];
431
432 // An association that names no copper layer is unset and the default pairing applies
433 if( copper == NO_ASSOCIATION || !isCopper( copper ) )
434 copper = defaultCopper;
435
436 if( copper >= BOARD_LAYER_COUNT || !isCopper( copper ) )
437 return std::nullopt;
438
439 return mapLayer( copper );
440}
441
442
444{
445 for( size_t i = 0; i < m_project.signals.size(); i++ )
446 {
447 const ELEMENT& e = m_project.signals[i];
448
449 if( e.type != ELEMENT_TYPE::SIGNAL || !e.signal )
450 continue;
451
452 wxString name = ToWx( e.signal->name );
453
454 if( name.IsEmpty() )
455 name = wxString::Format( wxS( "Net-%zu" ), i + 1 );
456
457 // Net names must be unique in KiCad
458 if( m_board->FindNet( name ) )
459 name = wxString::Format( wxS( "%s_%zu" ), name, i + 1 );
460
463 m_nets[e.base.signal] = net;
464 }
465}
466
467
469{
470 auto it = m_nets.find( aSignal );
471 return it == m_nets.end() ? nullptr : it->second;
472}
473
474
476{
477 for( const ELEMENT& e : m_project.components )
478 {
479 if( e.type == ELEMENT_TYPE::COMPONENT && e.component )
480 addFootprint( e );
481 }
482}
483
484
486{
487 const TARGET3001::COMPONENT& comp = *aComponent.component;
488
489 // A component is on the board only when its package heads the list and sits on a position layer;
490 // anything else exists only in the schematic
491 if( comp.packageItems.empty() )
492 return;
493
494 const ELEMENT* placement = &comp.packageItems.front();
495
496 if( placement->type != ELEMENT_TYPE::PACKAGE || !placement->package )
497 return;
498
499 LAYER_FUNCTION placementLayer = LayerFunction( placement->base.layer );
500
501 if( placementLayer != LAYER_FUNCTION::POSITION_TOP && placementLayer != LAYER_FUNCTION::POSITION_BOTTOM )
502 return;
503
504 const PACKAGE& pkg = *placement->package;
505
506 FOOTPRINT* fp = new FOOTPRINT( m_board );
507 m_board->Add( fp, ADD_MODE::APPEND );
508
509 fp->SetFPIDAsString( wxS( "Target3001:" ) + ToWx( pkg.package ) );
510 fp->SetPosition( toKi( placement->base.x, placement->base.y ) );
511 fp->SetOrientation( angle( pkg.direction ) );
512
513 // Items are stored already flipped, so only the side changes here
514 if( pkg.bottomSide )
515 fp->SetLayer( B_Cu );
516
517 wxString reference = ToWx( comp.prefix ).Trim().Trim( false );
518 uint16_t number = comp.referenceExtension.value_or( comp.referenceNumber );
519
520 if( number )
521 reference << number;
522
523 fp->SetReference( reference );
524 fp->SetValue( ToWx( comp.value ) );
525 fp->Reference().SetVisible( false );
526 fp->Value().SetVisible( false );
527
528 for( const ELEMENT& item : comp.packageItems )
529 {
530 if( &item != placement )
531 addItem( item, fp, fp, false );
532 }
533}
534
535
537{
538 for( const ELEMENT& e : m_project.signals )
539 {
540 if( e.type != ELEMENT_TYPE::SIGNAL || !e.signal )
541 continue;
542
543 for( const ELEMENT& t : e.signal->tracks )
544 addItem( t, m_board, nullptr, true );
545 }
546
547 for( auto& [key, polys] : m_copperFills )
548 {
549 polys.Simplify();
550
551 for( int i = 0; i < polys.OutlineCount(); i++ )
552 {
553 PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::POLY );
554 shape->SetPolyShape( SHAPE_POLY_SET( polys.Polygon( i ) ) );
555 shape->SetFilled( true );
557 shape->SetLayer( PCB_LAYER_ID( key.second ) );
558 shape->SetNet( net( uint16_t( key.first ) ) );
559 m_board->Add( shape, ADD_MODE::APPEND );
560 }
561 }
562}
563
564
566{
567 for( const ELEMENT& e : m_project.boardItems )
568 addItem( e, m_board, nullptr, false );
569}
570
571
573{
574 std::map<std::pair<uint16_t, PCB_LAYER_ID>, SHAPE_POLY_SET> pours;
575
576 for( POUR_AREA& area : m_pourAreas )
577 {
578 // TARGET 3001! leaves an area unfilled when its anchor lies outside the contour box
579 if( m_contourBox.IsValid() && !m_contourBox.Contains( area.anchor ) )
580 continue;
581
582 pours[{ area.signal, area.layer }].Append( area.outline );
583 }
584
585 for( auto& [key, outline] : pours )
586 {
587 outline.Simplify();
588
589 for( int i = 0; i < outline.OutlineCount(); i++ )
590 {
591 ZONE* zone = new ZONE( m_board );
592 zone->SetLayer( key.second );
593 zone->SetNet( net( key.first ) );
594
595 // Unconnected islands of an area stay filled
597
598 // Each foreign element carves the fill by its own clearance, which only rules can hold; a zero
599 // local clearance defers to them
600 zone->SetLocalClearance( 0 );
601 m_pourLayers.insert( key );
602
603 // Keep holes; a ring of edge strokes must not become a solid area
604 *zone->Outline() = SHAPE_POLY_SET( outline.Polygon( i ) );
605 m_board->Add( zone, ADD_MODE::APPEND );
606 }
607 }
608
609 for( auto& [layer, keepout] : m_pourKeepouts )
610 {
611 keepout.Simplify();
612
613 for( int i = 0; i < keepout.OutlineCount(); i++ )
614 {
615 ZONE* zone = new ZONE( m_board );
616 zone->SetIsRuleArea( true );
617 zone->SetDoNotAllowZoneFills( true );
618 zone->SetDoNotAllowTracks( false );
619 zone->SetDoNotAllowVias( false );
620 zone->SetDoNotAllowPads( false );
621 zone->SetDoNotAllowFootprints( false );
622 zone->SetLayer( PCB_LAYER_ID( layer ) );
623 *zone->Outline() = SHAPE_POLY_SET( keepout.Polygon( i ) );
624 m_board->Add( zone, ADD_MODE::APPEND );
625 }
626 }
627}
628
629
631{
632 const ELEMENT_BASE& b = aElem.base;
633 VECTOR2I p0 = toKi( b.x, b.y );
634
635 switch( aElem.type )
636 {
637 case ELEMENT_TYPE::TRIANGLE:
638 {
639 const TRIANGLE& t = aElem.triangle;
640 aOut.NewOutline();
641 aOut.Append( p0 );
642 aOut.Append( toKi( t.x2, t.y2 ) );
643 aOut.Append( toKi( t.x3, t.y3 ) );
644 return true;
645 }
646
647 case ELEMENT_TYPE::RECTANGLE:
648 {
649 const RECTANGLE& r = aElem.rectangle;
650 EDA_ANGLE rot = angle( r.direction );
651 VECTOR2D corners[4] = { { 0.0, 0.0 }, { double( r.width ), 0.0 },
652 { double( r.width ), double( r.height ) }, { 0.0, double( r.height ) } };
653
654 aOut.NewOutline();
655
656 for( const VECTOR2D& c : corners )
657 {
658 VECTOR2D rotated = c;
659 RotatePoint( rotated, -rot );
660 aOut.Append( toKi( b.x + KiROUND( rotated.x ), b.y + KiROUND( rotated.y ) ) );
661 }
662
663 return true;
664 }
665
666 case ELEMENT_TYPE::DISC:
668 return true;
669
670 case ELEMENT_TYPE::LINE:
671 TransformOvalToPolygon( aOut, p0, toKi( aElem.line.x2, aElem.line.y2 ), std::max( 1, aElem.line.width ),
673 return true;
674
675 case ELEMENT_TYPE::FIDUCIAL:
676 for( const std::unique_ptr<PCB_SHAPE>& shape : fiducialShapes( aElem, m_board ) )
677 shape->TransformShapeToPolygon( aOut, F_Cu, 0, ARC_HIGH_DEF, ERROR_INSIDE );
678
679 return true;
680
681 case ELEMENT_TYPE::ARC:
682 {
683 const ARC& a = aElem.arc;
685 int width = std::max( 1, a.width );
686 double sweep = a.Sweep();
687
688 if( sweep >= 360.0 - 1e-6 )
689 {
691 return true;
692 }
693
694 auto onArc = [&]( double aDeg )
695 {
696 return center + VECTOR2I( KiROUND( a.radius * cos( DEG2RAD( aDeg ) ) ),
697 -KiROUND( a.radius * sin( DEG2RAD( aDeg ) ) ) );
698 };
699
700 TransformArcToPolygon( aOut, onArc( a.startAngle ), onArc( a.startAngle + sweep / 2.0 ),
701 onArc( a.startAngle + sweep ), width, ARC_HIGH_DEF, ERROR_INSIDE );
702 return true;
703 }
704
705 default:
706 return false;
707 }
708}
709
710
712{
713 const ELEMENT_BASE& b = aElem.base;
714
715 m_contourBox.Merge( toKi( b.x, b.y ) );
716
717 if( ( aElem.type == ELEMENT_TYPE::LINE && aElem.line.clearance == 0 )
718 || ( aElem.type == ELEMENT_TYPE::ARC && aElem.arc.clearance == 0 )
719 || ( aElem.type == ELEMENT_TYPE::SPLINE && aElem.spline.clearance == 0 ) )
720 {
722 }
723
724 switch( aElem.type )
725 {
726 case ELEMENT_TYPE::LINE:
727 m_contourBox.Merge( toKi( aElem.line.x2, aElem.line.y2 ) );
728 break;
729
730 case ELEMENT_TYPE::ARC:
731 {
732 const ARC& a = aElem.arc;
733 double start = std::fmod( double( a.startAngle ), 360.0 );
734 double end = start + a.Sweep();
735
736 auto onArc = [&]( double aDeg )
737 {
738 return VECTOR2I( a.centerX + KiROUND( a.radius * cos( DEG2RAD( aDeg ) ) ),
739 a.centerY + KiROUND( a.radius * sin( DEG2RAD( aDeg ) ) ) );
740 };
741
742 // Every axis crossing inside the sweep extends the box
743 for( double axis = std::ceil( start / 90.0 ) * 90.0; axis <= end; axis += 90.0 )
744 {
745 VECTOR2I p = onArc( axis );
746 m_contourBox.Merge( toKi( p.x, p.y ) );
747 }
748
749 VECTOR2I p0 = onArc( start );
750 VECTOR2I p1 = onArc( end );
751 m_contourBox.Merge( toKi( p0.x, p0.y ) );
752 m_contourBox.Merge( toKi( p1.x, p1.y ) );
753 m_contourBox.Merge( toKi( a.endX, a.endY ) );
754 break;
755 }
756
757 case ELEMENT_TYPE::TRIANGLE:
758 case ELEMENT_TYPE::RECTANGLE:
759 {
760 SHAPE_POLY_SET outline;
761
762 if( elementOutline( aElem, outline ) )
763 m_contourBox.Merge( outline.BBox() );
764
765 break;
766 }
767
768 default:
769 break;
770 }
771}
772
773
775 bool aIsTrack )
776{
777 const ELEMENT_BASE& b = aElem.base;
778
779 if( b.layer == m_contourLayer && b.flags == 0 && aElem.type != ELEMENT_TYPE::TEXT )
780 mergeContour( aElem );
781
782 // Hidden board elements are not drawn; hidden text is kept as invisible text
783 if( ( b.flags & 1 ) && aElem.type != ELEMENT_TYPE::TEXT )
784 return;
785
786 if( aElem.type == ELEMENT_TYPE::PAD )
787 {
788 if( aFootprint )
789 addPad( aElem, aFootprint );
790 else if( aIsTrack )
791 addVia( aElem );
792 else
793 addPad( aElem, nullptr );
794
795 return;
796 }
797
798 // Pour areas and keepouts are collected and turned into zones at the end
799 if( std::optional<PCB_LAYER_ID> pourLayer = pourCopperLayer( b.layer ) )
800 {
801 SHAPE_POLY_SET outline;
802
803 if( aElem.type == ELEMENT_TYPE::TEXT )
804 {
805 if( ( b.flags & 1 ) || aElem.text.weight == 0 )
806 return;
807
808 // Text on these layers cuts or fills by its glyph strokes
809 PCB_TEXT glyphs( m_board );
810 glyphs.SetText( m_project.FunctionText( aElem.text, false ).value_or( ToWx( aElem.text.text ) ) );
811 configureText( &glyphs, aElem, *pourLayer );
812 glyphs.TransformTextToPolySet( outline, 0, ARC_HIGH_DEF, ERROR_INSIDE );
813 }
814 else if( !elementOutline( aElem, outline ) )
815 {
816 return;
817 }
818
820 bool isArea = f == LAYER_FUNCTION::AREA_BOTTOM || f == LAYER_FUNCTION::AREA_INSIDE
821 || f == LAYER_FUNCTION::AREA_TOP;
822
823 // Flagged areas take no part in the fill
824 if( isArea && b.signal && b.flags == 0 )
825 m_pourAreas.push_back( { b.signal, *pourLayer, toKi( b.x, b.y ), std::move( outline ) } );
826 else if( !isArea )
827 m_pourKeepouts[*pourLayer].Append( outline );
828
829 return;
830 }
831
832 std::vector<PCB_LAYER_ID> layers;
833
834 // The all-copper layer draws the same element, unmirrored, on every copper layer
835 if( b.layer == ALL_COPPER_LAYER )
836 {
837 for( PCB_LAYER_ID cu : m_board->GetEnabledLayers().CuStack() )
838 layers.push_back( cu );
839 }
840 else if( std::optional<PCB_LAYER_ID> layer = mapLayer( b.layer ) )
841 {
842 layers.push_back( *layer );
843 }
844
845 for( PCB_LAYER_ID layer : layers )
846 {
847 if( aElem.type == ELEMENT_TYPE::TEXT )
848 addText( aElem, aParent, aFootprint, layer );
849 else
850 addShape( aElem, aParent, aFootprint, aIsTrack, layer );
851 }
852}
853
854
856 bool aIsTrack, PCB_LAYER_ID aLayer )
857{
858 const ELEMENT_BASE& b = aElem.base;
859 bool copper = IsCopperLayer( aLayer );
860
861 if( copper )
862 {
863 std::optional<int> clearance;
864
865 if( aElem.type == ELEMENT_TYPE::LINE )
866 clearance = aElem.line.clearance;
867 else if( aElem.type == ELEMENT_TYPE::ARC )
868 clearance = aElem.arc.clearance;
869 else if( aElem.type == ELEMENT_TYPE::SPLINE )
870 clearance = aElem.spline.clearance;
871
872 // A zero clearance keeps the netclass clearance rather than letting another signal's area touch
873 if( clearance && *clearance > 0 && b.signal )
875 }
876
877 switch( aElem.type )
878 {
879 case ELEMENT_TYPE::LINE:
880 {
881 const LINE& l = aElem.line;
882
883 if( copper && aIsTrack && !aFootprint )
884 {
885 PCB_TRACK* track = new PCB_TRACK( m_board );
886 track->SetStart( toKi( b.x, b.y ) );
887 track->SetEnd( toKi( l.x2, l.y2 ) );
888 track->SetWidth( l.width );
889 track->SetLayer( aLayer );
890 track->SetNet( net( b.signal ) );
891 m_board->Add( track, ADD_MODE::APPEND );
892 return;
893 }
894
895 PCB_SHAPE* shape = new PCB_SHAPE( aParent, SHAPE_T::SEGMENT );
896 shape->SetStart( toKi( b.x, b.y ) );
897 shape->SetEnd( toKi( l.x2, l.y2 ) );
898 shape->SetStroke( STROKE_PARAMS( l.width, lineStyle( l.dashStyle ) ) );
899 shape->SetLayer( aLayer );
900
901 if( copper )
902 shape->SetNet( net( b.signal ) );
903
904 aParent->Add( shape, ADD_MODE::APPEND );
905 return;
906 }
907
908 case ELEMENT_TYPE::ARC:
909 {
910 const ARC& a = aElem.arc;
912 double sweep = a.Sweep();
913
914 bool full = sweep >= 360.0 - 1e-6;
915
916 if( full )
917 {
918 PCB_SHAPE* shape = new PCB_SHAPE( aParent, SHAPE_T::CIRCLE );
919 shape->SetCenter( center );
920 shape->SetRadius( a.radius );
922 shape->SetLayer( aLayer );
923
924 if( copper )
925 shape->SetNet( net( b.signal ) );
926
927 aParent->Add( shape, ADD_MODE::APPEND );
928 return;
929 }
930
931 double midDeg = double( a.startAngle ) + sweep / 2.0;
932 VECTOR2I start = center + VECTOR2I( KiROUND( a.radius * cos( DEG2RAD( a.startAngle ) ) ),
933 -KiROUND( a.radius * sin( DEG2RAD( a.startAngle ) ) ) );
934 VECTOR2I mid = center + VECTOR2I( KiROUND( a.radius * cos( DEG2RAD( midDeg ) ) ),
935 -KiROUND( a.radius * sin( DEG2RAD( midDeg ) ) ) );
936 VECTOR2I end = center + VECTOR2I( KiROUND( a.radius * cos( DEG2RAD( a.startAngle + sweep ) ) ),
937 -KiROUND( a.radius * sin( DEG2RAD( a.startAngle + sweep ) ) ) );
938
939 if( copper && aIsTrack && !aFootprint )
940 {
941 PCB_ARC* arc = new PCB_ARC( m_board );
942 arc->SetStart( start );
943 arc->SetMid( mid );
944 arc->SetEnd( end );
945 arc->SetWidth( a.width );
946 arc->SetLayer( aLayer );
947 arc->SetNet( net( b.signal ) );
948 m_board->Add( arc, ADD_MODE::APPEND );
949 return;
950 }
951
952 PCB_SHAPE* shape = new PCB_SHAPE( aParent, SHAPE_T::ARC );
953 shape->SetArcGeometry( start, mid, end );
955 shape->SetLayer( aLayer );
956
957 if( copper )
958 shape->SetNet( net( b.signal ) );
959
960 aParent->Add( shape, ADD_MODE::APPEND );
961 return;
962 }
963
964 case ELEMENT_TYPE::SPLINE:
965 {
966 const SPLINE& s = aElem.spline;
967 VECTOR2I p0 = toKi( b.x, b.y );
968 VECTOR2I c = toKi( s.controlX, s.controlY );
969 VECTOR2I p2 = toKi( s.endX, s.endY );
970
971 // Elevate the quadratic curve to the cubic form KiCad stores
972 PCB_SHAPE* shape = new PCB_SHAPE( aParent, SHAPE_T::BEZIER );
973 shape->SetStart( p0 );
974 shape->SetBezierC1( p0 + ( c - p0 ) * 2 / 3 );
975 shape->SetBezierC2( p2 + ( c - p2 ) * 2 / 3 );
976 shape->SetEnd( p2 );
978 shape->SetLayer( aLayer );
980
981 if( copper )
982 shape->SetNet( net( b.signal ) );
983
984 aParent->Add( shape, ADD_MODE::APPEND );
985 return;
986 }
987
988 case ELEMENT_TYPE::TRIANGLE:
989 case ELEMENT_TYPE::RECTANGLE:
990 case ELEMENT_TYPE::DISC:
991 {
992 SHAPE_POLY_SET outline;
993 elementOutline( aElem, outline );
994
995 // Stored copper fills are triangulated; merge them back into polygons per net and layer
996 if( copper && aIsTrack && !aFootprint && aElem.type == ELEMENT_TYPE::TRIANGLE )
997 {
998 m_copperFills[{ b.signal, aLayer }].Append( outline );
999 return;
1000 }
1001
1002 PCB_SHAPE* shape;
1003
1004 if( aElem.type == ELEMENT_TYPE::DISC )
1005 {
1006 shape = new PCB_SHAPE( aParent, SHAPE_T::CIRCLE );
1007 shape->SetCenter( toKi( b.x, b.y ) );
1008 shape->SetRadius( aElem.disc.radius );
1009 }
1010 else
1011 {
1012 shape = new PCB_SHAPE( aParent, SHAPE_T::POLY );
1013 shape->SetPolyShape( outline );
1014 }
1015
1016 shape->SetFilled( true );
1018 shape->SetLayer( aLayer );
1019
1020 if( copper )
1021 shape->SetNet( net( b.signal ) );
1022
1023 aParent->Add( shape, ADD_MODE::APPEND );
1024 return;
1025 }
1026
1027 case ELEMENT_TYPE::FIDUCIAL:
1028 for( std::unique_ptr<PCB_SHAPE>& shape : fiducialShapes( aElem, aParent ) )
1029 {
1030 shape->SetLayer( aLayer );
1031
1032 if( copper )
1033 shape->SetNet( net( b.signal ) );
1034
1035 aParent->Add( shape.release(), ADD_MODE::APPEND );
1036 }
1037
1038 return;
1039
1040 default:
1041 return;
1042 }
1043}
1044
1045
1046std::vector<std::unique_ptr<PCB_SHAPE>> TARGET3001_PCB_BUILDER::fiducialShapes( const ELEMENT& aElem,
1047 BOARD_ITEM_CONTAINER* aParent ) const
1048{
1049 const FIDUCIAL& f = aElem.fiducial;
1050 const ELEMENT_BASE& b = aElem.base;
1051 std::vector<std::unique_ptr<PCB_SHAPE>> shapes;
1052
1053 auto circle = [&]( int aRadius, int aWidth, bool aFilled )
1054 {
1055 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aParent, SHAPE_T::CIRCLE );
1056 shape->SetCenter( toKi( b.x, b.y ) );
1057 shape->SetRadius( aRadius );
1058 shape->SetStroke( STROKE_PARAMS( aWidth, LINE_STYLE::SOLID ) );
1059 shape->SetFilled( aFilled );
1060 shapes.push_back( std::move( shape ) );
1061 };
1062
1064 {
1065 circle( f.radius, 0, true );
1066 return shapes;
1067 }
1068
1069 int ring = std::max( 0, f.radius - f.width / 2 );
1070 circle( ring, f.width, false );
1071
1073 return shapes;
1074
1075 circle( KiROUND( ring * 2.0 / 3.0 ), f.width, false );
1076 circle( KiROUND( ring / 3.0 ), f.width, false );
1077
1078 // Cross arms reach the outer ring's centreline along the mark's own axes
1079 for( double axis : { 0.0, 90.0 } )
1080 {
1081 double rad = DEG2RAD( f.direction + axis );
1082 int32_t dx = KiROUND( ring * cos( rad ) );
1083 int32_t dy = KiROUND( ring * sin( rad ) );
1084
1085 std::unique_ptr<PCB_SHAPE> arm = std::make_unique<PCB_SHAPE>( aParent, SHAPE_T::SEGMENT );
1086 arm->SetStart( toKi( b.x + dx, b.y + dy ) );
1087 arm->SetEnd( toKi( b.x - dx, b.y - dy ) );
1088 arm->SetStroke( STROKE_PARAMS( f.width, LINE_STYLE::SOLID ) );
1089 shapes.push_back( std::move( arm ) );
1090 }
1091
1092 return shapes;
1093}
1094
1095
1096void TARGET3001_PCB_BUILDER::addPad( const ELEMENT& aElem, FOOTPRINT* aFootprint )
1097{
1098 const TARGET3001::PAD& src = aElem.pad;
1099 const ELEMENT_BASE& b = aElem.base;
1100
1101 FOOTPRINT* fp = aFootprint;
1102
1103 // Free pads (mounting holes, test points) get a footprint of their own
1104 if( !fp )
1105 {
1106 fp = new FOOTPRINT( m_board );
1107 fp->SetPosition( toKi( b.x, b.y ) );
1108 fp->SetReference( wxString::Format( wxS( "H%d" ), ++m_freePadCount ) );
1109 fp->Reference().SetVisible( false );
1110 fp->Value().SetVisible( false );
1112 m_board->Add( fp, ADD_MODE::APPEND );
1113 }
1114
1115 ::PAD* pad = new ::PAD( fp );
1116 pad->SetPosition( toKi( b.x, b.y ) );
1117 pad->SetOrientation( angle( src.direction ) );
1118
1119 VECTOR2I size( std::max( 1, src.width ), std::max( 1, src.height ) );
1120 std::optional<LSET> padstack = padstackLayers( b.layer );
1121
1122 pad->SetSize( PADSTACK::ALL_LAYERS, size );
1123
1124 switch( TARGET3001::PAD_SHAPE( src.shape ) )
1125 {
1127 if( size.x == size.y )
1128 {
1130 }
1131 else
1132 {
1133 // Unequal round pads are true ellipses, which KiCad only has as a custom shape
1134 bool wide = size.x > size.y;
1135 SHAPE_ELLIPSE ellipse( VECTOR2I( 0, 0 ), std::max( size.x, size.y ) / 2,
1136 std::min( size.x, size.y ) / 2, wide ? ANGLE_0 : ANGLE_90 );
1137 SHAPE_POLY_SET outline( ellipse.ConvertToPolyline( m_board->GetDesignSettings().m_MaxError ) );
1138 int anchor = std::min( size.x, size.y );
1139
1141 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, ::PAD_SHAPE::CIRCLE );
1143 pad->AddPrimitivePoly( PADSTACK::ALL_LAYERS, outline, 0, true );
1144 }
1145
1146 break;
1147
1149 {
1150 const std::string* prop = b.FindProp( "POLY_PAD" );
1151 std::vector<VECTOR2I> pts = prop ? parsePolyPad( *prop ) : std::vector<VECTOR2I>();
1152
1153 if( pts.size() >= 3 )
1154 {
1155 std::vector<VECTOR2I> local;
1156
1157 for( const VECTOR2I& pt : pts )
1158 local.emplace_back( pt.x, -pt.y );
1159
1160 // The custom shape carries all the copper; the anchor only needs to sit inside it
1161 int anchor = pcbIUScale.mmToIU( 0.001 );
1162
1164 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, ::PAD_SHAPE::CIRCLE );
1166 pad->AddPrimitivePoly( PADSTACK::ALL_LAYERS, local, 0, true );
1167 break;
1168 }
1169
1170 // Fewer than three vertices falls back to an octagon
1172 }
1173
1176 pad->SetChamferRectRatio( PADSTACK::ALL_LAYERS, OCTAGON_CHAMFER_RATIO );
1177 pad->SetChamferPositions( PADSTACK::ALL_LAYERS, RECT_CHAMFER_ALL );
1178 break;
1179
1182 break;
1183
1185 default:
1186 {
1187 long rounded = 0;
1188
1189 // RECT_ROUNDED is the corner diameter in percent of the smaller side
1190 if( const std::string* prop = b.FindProp( "RECT_ROUNDED" ) )
1191 wxString( *prop ).ToLong( &rounded );
1192
1193 if( rounded > 0 )
1194 {
1196 pad->SetRoundRectRadiusRatio( PADSTACK::ALL_LAYERS, std::min( 0.5, rounded / 200.0 ) );
1197 }
1198 else
1199 {
1201 }
1202
1203 break;
1204 }
1205 }
1206
1207 pad->SetNumber( TARGET3001::PadNumber( aElem ) );
1208
1209 bool hasHole = src.drill > 0;
1210
1211 if( hasHole )
1212 {
1213 pad->SetDrillSize( VECTOR2I( src.drill, src.drill ) );
1214
1215 if( const std::string* slot = b.FindProp( "SLOT_HOLE" ) )
1216 {
1217 long length = 0;
1218
1219 if( wxString( *slot ).ToLong( &length ) && length > src.drill )
1220 {
1221 pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
1222 pad->SetDrillSize( VECTOR2I( int( length ), src.drill ) );
1223 }
1224 }
1225
1226 // A hole is unplated only when it is larger than its copper
1227 bool plated = std::max( src.width, src.height ) >= src.drill;
1228
1229 pad->SetAttribute( plated ? PAD_ATTRIB::PTH : PAD_ATTRIB::NPTH );
1230
1231 if( !plated )
1232 pad->SetLayerSet( ::PAD::UnplatedHoleMask() );
1233 else if( padstack )
1234 pad->SetLayerSet( *padstack );
1235 else
1236 pad->SetLayerSet( ::PAD::PTHMask() );
1237
1238 if( !plated )
1239 {
1240 VECTOR2I hole = pad->GetDrillSize();
1241
1242 pad->DeletePrimitivesList();
1243 pad->SetShape( PADSTACK::ALL_LAYERS, hole.x == hole.y ? ::PAD_SHAPE::CIRCLE : ::PAD_SHAPE::OVAL );
1244 pad->SetSize( PADSTACK::ALL_LAYERS, hole );
1245 }
1246 }
1247 else if( padstack )
1248 {
1249 pad->SetAttribute( PAD_ATTRIB::SMD );
1250 pad->SetLayerSet( *padstack );
1251 }
1252 else
1253 {
1254 std::optional<PCB_LAYER_ID> copper = mapLayer( b.layer );
1255 PCB_LAYER_ID side = copper && IsCopperLayer( *copper ) ? *copper : F_Cu;
1256 LSET layers( { side } );
1257
1258 if( side == F_Cu )
1259 layers |= LSET( { F_Mask, F_Paste } );
1260 else if( side == B_Cu )
1261 layers |= LSET( { B_Mask, B_Paste } );
1262
1263 pad->SetAttribute( PAD_ATTRIB::SMD );
1264 pad->SetLayerSet( layers );
1265 }
1266
1267 if( src.clearance > 0 )
1268 pad->SetLocalClearance( src.clearance );
1269
1270 pad->SetNet( net( b.signal ) );
1271
1272 // The aura isolates a pad from areas of its own signal too, except on the layers NO_AURA lists
1273 if( b.signal )
1274 {
1275 AURA_PAD entry{ pad, b.signal, {} };
1276
1277 if( src.clearance == 0 )
1278 {
1279 for( PCB_LAYER_ID cu : pad->GetLayerSet().CuStack() )
1280 entry.noAura.insert( cu );
1281 }
1282 else if( const std::string* layers = b.FindProp( "NO_AURA" ) )
1283 {
1284 wxStringTokenizer tokens( wxString::FromUTF8( *layers ), wxS( "," ) );
1285
1286 while( tokens.HasMoreTokens() )
1287 {
1288 long layer = 0;
1289
1290 if( tokens.GetNextToken().Trim().Trim( false ).ToLong( &layer ) && isCopper( int( layer ) ) )
1291 {
1292 if( std::optional<PCB_LAYER_ID> cu = mapLayer( int( layer ) ) )
1293 entry.noAura.insert( *cu );
1294 }
1295 }
1296 }
1297
1298 m_auraPads.push_back( std::move( entry ) );
1299 }
1300
1301 fp->Add( pad, ADD_MODE::APPEND );
1302
1303 if( padstack )
1305}
1306
1307
1308void TARGET3001_PCB_BUILDER::addPadstackCuts( const BOARD_ITEM& aItem, int aPadstack )
1309{
1310 const TARGET3001::PADSTACK_DEF& def = m_project.settings.padstacks[aPadstack];
1311
1312 // A padstack flagged on a deletion layer cuts the computed copper there, grown by its oversize
1313 for( int layer = 0; layer < TARGET3001::BOARD_LAYER_COUNT; layer++ )
1314 {
1315 LAYER_FUNCTION f = LayerFunction( layer );
1316
1317 if( !def.onLayer[layer]
1318 || ( f != LAYER_FUNCTION::DELETION_BOTTOM && f != LAYER_FUNCTION::DELETION_INSIDE
1319 && f != LAYER_FUNCTION::DELETION_TOP ) )
1320 {
1321 continue;
1322 }
1323
1324 if( std::optional<PCB_LAYER_ID> copper = pourCopperLayer( layer ) )
1325 {
1326 aItem.TransformShapeToPolygon( m_pourKeepouts[*copper], *copper, std::max( 0, def.oversize[layer] ),
1328 }
1329 }
1330}
1331
1332
1333std::optional<LSET> TARGET3001_PCB_BUILDER::padstackLayers( int aLayer ) const
1334{
1335 const std::vector<TARGET3001::PADSTACK_DEF>& padstacks = m_project.settings.padstacks;
1336 size_t index = size_t( aLayer - TARGET3001::FIRST_PADSTACK_LAYER );
1337
1338 if( aLayer < TARGET3001::FIRST_PADSTACK_LAYER || index >= padstacks.size() )
1339 return std::nullopt;
1340
1341 LSET layers;
1342
1343 for( int layer = 0; layer < TARGET3001::BOARD_LAYER_COUNT; layer++ )
1344 {
1345 if( !padstacks[index].onLayer[layer] )
1346 continue;
1347
1348 std::optional<PCB_LAYER_ID> mapped = mapLayer( layer );
1349
1350 if( mapped && ( IsCopperLayer( *mapped ) || IsSolderMaskLayer( *mapped ) || *mapped == F_Paste
1351 || *mapped == B_Paste ) )
1352 {
1353 layers.set( *mapped );
1354 }
1355 }
1356
1357 return layers;
1358}
1359
1360
1362{
1363 const TARGET3001::PAD& src = aElem.pad;
1364 const ELEMENT_BASE& b = aElem.base;
1365
1367 bool slotted = b.FindProp( "SLOT_HOLE" ) != nullptr;
1368 std::optional<LSET> padstack = padstackLayers( b.layer );
1369 LSET allCopper = LSET::AllCuMask( m_board->GetCopperLayerCount() );
1370
1371 // Copper without a hole is a pad-shaped piece of copper; a hole larger than its copper is unplated.
1372 // Slots, oval or rectangular copper and padstacks missing a copper layer have no via equivalent.
1373 if( src.drill <= 0 || std::max( src.width, src.height ) < src.drill || slotted
1375 || ( padstack && ( *padstack & allCopper ) != allCopper ) )
1376 {
1377 addPad( aElem, nullptr );
1378 return;
1379 }
1380
1381 PCB_VIA* via = new PCB_VIA( m_board );
1382 via->SetPosition( toKi( b.x, b.y ) );
1383 via->SetWidth( PADSTACK::ALL_LAYERS, std::max( src.width, src.height ) );
1384 via->SetDrill( src.drill );
1385 via->SetViaType( VIATYPE::THROUGH );
1386 via->SetLayerPair( F_Cu, B_Cu );
1387 via->SetNet( net( b.signal ) );
1388 m_board->Add( via, ADD_MODE::APPEND );
1389
1390 if( b.signal && src.clearance > 0 )
1392
1393 if( padstack )
1395}
1396
1397
1399{
1400 const TEXT& t = aElem.text;
1401 const ELEMENT_BASE& b = aElem.base;
1402
1403 aText->SetLayer( aLayer );
1404 m_fonts.Apply( aText, aElem, 1 );
1405 aText->SetTextAngle( angle( t.direction ) );
1406 aText->SetMirrored( t.mirrored != 0 );
1409 aText->SetTextPos( toKi( b.x, b.y ) );
1410
1411 // Right-aligned text also stores its right end, which keeps the alignment when font metrics differ
1412 if( std::optional<std::pair<int32_t, int32_t>> end = b.TextRightEnd() )
1413 {
1414 // Mirroring flips both ends, so the stored end stays the right-justified one
1415 aText->SetTextPos( toKi( end->first, end->second ) );
1417 }
1418
1419 aText->SetTextPos( aText->GetTextPos() + TARGET3001::FONTS::BaselineShift( *aText ) );
1420 aText->SetVisible( !( b.flags & 1 ) && t.weight != 0 );
1421}
1422
1423
1425 FOOTPRINT* aFootprint, PCB_LAYER_ID aLayer )
1426{
1427 const TEXT& t = aElem.text;
1428
1429 if( aFootprint && t.function == uint8_t( TEXT_FUNCTION::REFERENCE ) )
1430 {
1431 aFootprint->Reference().SetText( ToWx( t.text ) );
1432 configureText( &aFootprint->Reference(), aElem, aLayer );
1433 return;
1434 }
1435
1436 if( aFootprint && t.function == uint8_t( TEXT_FUNCTION::VALUE ) )
1437 {
1438 aFootprint->Value().SetText( ToWx( t.text ) );
1439 configureText( &aFootprint->Value(), aElem, aLayer );
1440 return;
1441 }
1442
1443 PCB_TEXT* text = new PCB_TEXT( aParent );
1444 text->SetText( m_project.FunctionText( t, false ).value_or( ToWx( t.text ) ) );
1445 configureText( text, aElem, aLayer );
1446 aParent->Add( text, ADD_MODE::APPEND );
1447}
int index
const char * name
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr int ARC_HIGH_DEF
Definition base_units.h:144
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
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.
Abstract interface for BOARD_ITEMs capable of storing other items inside.
virtual void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false)=0
Adds an item to the container.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:85
virtual void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the item shape to a closed polygon.
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:383
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
constexpr coord_type GetY() const
Definition box2.h:205
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr coord_type GetX() const
Definition box2.h:204
constexpr size_type GetHeight() const
Definition box2.h:212
virtual void SetFilled(bool aFlag)
Definition eda_shape.h:144
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
void SetRadius(int aX)
Definition eda_shape.h:358
virtual void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:556
void SetMirrored(bool isMirrored)
Definition eda_text.cpp:354
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:378
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:236
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:370
void SetPosition(const VECTOR2I &aPos) override
void SetOrientation(const EDA_ANGLE &aNewAngle)
void SetAttributes(int aAttributes)
Definition footprint.h:510
PCB_FIELD & Value()
read/write accessors:
Definition footprint.h:831
void SetReference(const wxString &aReference)
Definition footprint.h:799
void SetValue(const wxString &aValue)
Definition footprint.h:822
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.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetFPIDAsString(const wxString &aFPID)
Definition footprint.h:420
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
Handle the data for a net.
Definition netinfo.h:50
const wxString & GetNetname() const
Definition netinfo.h:110
int GetNetCode() const
Definition netinfo.h:104
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
static LSET PTHMask()
layer set for a through hole pad
Definition pad.cpp:615
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition pad.cpp:636
void SetMid(const VECTOR2I &aMid)
Definition pcb_track.h:292
void SetCenter(const VECTOR2I &aCenter) override
void SetBezierC1(const VECTOR2I &aPt) override
void SetEnd(const VECTOR2I &aEnd) override
void SetPolyShape(const SHAPE_POLY_SET &aShape) override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetStart(const VECTOR2I &aStart) override
void SetBezierC2(const VECTOR2I &aPt) override
void SetStroke(const STROKE_PARAMS &aStroke) override
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd) override
Set the three controlling points for an arc.
VECTOR2I GetTextPos() const override
Definition pcb_text.cpp:461
void TransformTextToPolySet(SHAPE_POLY_SET &aBuffer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc) const
Convert the text to a polygonSet describing the actual character strokes (one per segment).
Definition pcb_text.cpp:787
void SetTextAngle(const EDA_ANGLE &aAngle) override
Definition pcb_text.cpp:572
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
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
SHAPE_LINE_CHAIN ConvertToPolyline(int aMaxError) const
Build a polyline approximation of the ellipse or arc.
Represent a set of closed polygons.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int NewOutline()
Creates a new empty polygon in the set and returns its index.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Simple container to manage line stroke parameters.
static VECTOR2I BaselineShift(const EDA_TEXT &aText)
Offset in text units that moves aText so the baseline-left point of its first glyph lies on its posit...
TARGET3001::LAYER_FUNCTION LayerFunction(int aLayer) const
Function of a TARGET 3001! board layer in this project.
const TARGET3001::PROJECT & m_project
std::map< uint16_t, NETINFO_ITEM * > m_nets
by signal ID
std::vector< POUR_AREA > m_pourAreas
void addShape(const TARGET3001::ELEMENT &aElem, BOARD_ITEM_CONTAINER *aParent, FOOTPRINT *aFootprint, bool aIsTrack, PCB_LAYER_ID aLayer)
Add the graphic or track form of aElem on one KiCad layer.
void addVia(const TARGET3001::ELEMENT &aElem)
std::map< int, SHAPE_POLY_SET > m_pourKeepouts
KiCad layer -> pour keepout.
void addFootprint(const TARGET3001::ELEMENT &aComponent)
std::vector< int > m_copperStack
TARGET layers, top to bottom.
void addItem(const TARGET3001::ELEMENT &aElem, BOARD_ITEM_CONTAINER *aParent, FOOTPRINT *aFootprint, bool aIsTrack)
Add one board element to aParent; footprint items are passed with their footprint.
std::vector< std::unique_ptr< PCB_SHAPE > > fiducialShapes(const TARGET3001::ELEMENT &aElem, BOARD_ITEM_CONTAINER *aParent) const
Strokes and discs that draw a fiducial mark, not yet assigned a layer.
std::optional< PCB_LAYER_ID > pourCopperLayer(int aLayer) const
Copper layer that an area or deletion layer applies to.
void buildAreaClearanceRules()
Clearance rules between copper areas and each signal's tracks and vias.
bool isCopper(int aLayer) const
NETINFO_ITEM * net(uint16_t aSignal) const
void addPad(const TARGET3001::ELEMENT &aElem, FOOTPRINT *aFootprint)
void connectAuraPads()
Connect pads to areas of their own signal the way their aura and NO_AURA layers do.
bool elementOutline(const TARGET3001::ELEMENT &aElem, SHAPE_POLY_SET &aOut) const
Outline of a filled element (rectangle, triangle, disc, stroked line or arc) in KiCad coordinates.
std::set< std::pair< uint16_t, PCB_LAYER_ID > > m_pourLayers
(signal, KiCad layer) holding an area
std::map< std::pair< int, int >, SHAPE_POLY_SET > m_copperFills
(net, KiCad layer) -> filled copper
std::map< uint16_t, std::map< int, int > > m_signalClearances
Track and via clearances counted per signal, then per clearance value.
TARGET3001_PCB_BUILDER(BOARD *aBoard, const TARGET3001::PROJECT &aProject, REPORTER *aReporter)
void addPadstackCuts(const BOARD_ITEM &aItem, int aPadstack)
Cut aItem, grown per layer, from the pours of the deletion layers padstack aPadstack lists.
std::optional< LSET > padstackLayers(int aLayer) const
Copper, mask and paste layers of the padstack a pad layer >= 101 selects.
void addText(const TARGET3001::ELEMENT &aElem, BOARD_ITEM_CONTAINER *aParent, FOOTPRINT *aFootprint, PCB_LAYER_ID aLayer)
void mergeContour(const TARGET3001::ELEMENT &aElem)
Grow the board contour box by the defining points of an element, ignoring line widths.
void configureText(PCB_TEXT *aText, const TARGET3001::ELEMENT &aElem, PCB_LAYER_ID aLayer)
VECTOR2I toKi(int32_t aX, int32_t aY) const
std::optional< int > m_contourLayer
first TARGET layer holding the contour
std::optional< PCB_LAYER_ID > mapLayer(int aLayer) const
Copper or graphic layer for a board element, if KiCad has a place for it.
std::map< int, PCB_LAYER_ID > m_layerMap
bool m_contourCarvesNothing
a contour stroke has no clearance
std::vector< AURA_PAD > m_auraPads
A triangle object.
Definition triangle_3d.h:39
Handle a list of polygons defining a copper zone.
Definition zone.h:70
void SetDoNotAllowPads(bool aEnable)
Definition zone.h:858
void SetLocalClearance(std::optional< int > aClearance)
Definition zone.h:183
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 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 SetDoNotAllowFootprints(bool aEnable)
Definition zone.h:859
void SetDoNotAllowZoneFills(bool aEnable)
Definition zone.h:855
void SetIslandRemovalMode(ISLAND_REMOVAL_MODE aRemove)
Definition zone.h:862
void TransformRingToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aCentre, int aRadius, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arcs to multiple straight segments.
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 TransformArcToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arc to multiple straight segments.
void TransformOvalToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a oblong shape to a polygon, using multiple segments.
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:450
@ DEGREES_T
Definition eda_angle.h:30
@ SEGMENT
Definition eda_shape.h:56
@ FP_EXCLUDE_FROM_POS_FILES
Definition footprint.h:88
@ FP_BOARD_ONLY
Definition footprint.h:90
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:89
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
PCB_LAYER_ID BoardLayerFromLegacyId(int aLegacyId)
Retrieve a layer ID from an integer converted from a legacy (pre-V9) enum value.
Definition layer_id.cpp:229
bool IsSolderMaskLayer(int aLayer)
Definition layer_ids.h:812
#define MAX_CU_LAYERS
Definition layer_ids.h:172
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
@ B_Adhes
Definition layer_ids.h:99
@ Edge_Cuts
Definition layer_ids.h:108
@ Dwgs_User
Definition layer_ids.h:103
@ F_Paste
Definition layer_ids.h:100
@ Cmts_User
Definition layer_ids.h:104
@ F_Adhes
Definition layer_ids.h:98
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ B_Paste
Definition layer_ids.h:101
@ User_9
Definition layer_ids.h:128
@ F_Fab
Definition layer_ids.h:115
@ F_SilkS
Definition layer_ids.h:96
@ User_1
Definition layer_ids.h:120
@ B_SilkS
Definition layer_ids.h:97
@ F_Cu
Definition layer_ids.h:60
@ B_Fab
Definition layer_ids.h:114
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:79
KICOMMON_API std::string FormatInternalUnits(const EDA_IU_SCALE &aIuScale, int aValue, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
Converts aValue from internal units to a string appropriate for writing to file.
LAYER_FUNCTION
Board layer function codes stored per layer in the project header.
constexpr int FIRST_PADSTACK_LAYER
A pad on board layer FIRST_PADSTACK_LAYER + n takes its layers from project padstack n.
wxString PadNumber(const ELEMENT &aPad)
Number of a pad element in KiCad, its PADNAME when present, else its stored number.
constexpr int ALL_COPPER_LAYER
Board elements on this layer exist on every copper layer.
@ POLYGON
vertices in the POLY_PAD property
@ CROSSHAIR_RINGS
cross and rings at 1, 2/3 and 1/3 of the outer centreline radius
@ FILLED_CIRCLE
solid disc of the outer radius
wxString ToWx(const std::string &aRaw)
Convert a stored ANSI string to a wxString.
ELEMENT_TYPE
Element class tags as stored in the file.
constexpr int BOARD_LAYER_COUNT
Board layers 0..99 carry a per-project function; 100 and above have fixed meanings.
constexpr uint8_t NO_ASSOCIATION
Layer association value for a layer that belongs to no copper layer.
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:98
@ 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
LINE_STYLE
Dashed line types.
Stroked arc or circle. The base position is the start point.
double Sweep() const
Counter-clockwise sweep in (0, 360]; equal angles give a full circle.
float startAngle
degrees, counter-clockwise from +X
Fields shared by every drawable element.
uint16_t signal
ID of the owning signal (a signal stores its own), 0 for none.
std::optional< std::pair< int32_t, int32_t > > TextRightEnd() const
Right end of right-aligned text from its ARXY property, in file coordinates.
const std::string * FindProp(const std::string &aKey) const
Return the value of the first property named aKey, if any.
One element of any class.
std::shared_ptr< COMPONENT > component
std::shared_ptr< SIGNAL > signal
std::shared_ptr< PACKAGE > package
uint8_t kind
FIDUCIAL_KIND.
uint8_t dashStyle
0 solid, 1 long dash, 2 dash-dot, 3 other, 4 short dash
Footprint placement. Pads and graphics of the footprint are stored in board coordinates.
Project padstack; the board layers a pad exists on and how far it grows on each.
std::array< bool, BOARD_LAYER_COUNT > onLayer
std::array< int32_t, BOARD_LAYER_COUNT > oversize
growth per side, nm
int32_t width
along the pad's own X axis before rotation
int32_t drill
0 for no hole
Filled rectangle anchored at a corner (the base position); sizes are signed.
Quadratic Bezier from the base position to the end point.
Filled triangle given by three absolute corners (the first is the base position).
constexpr int ALL_COPPER_LAYER
Board elements on this layer exist on every copper layer.
constexpr int BOARD_LAYER_COUNT
Board layers 0..99 carry a per-project function; 100 and above have fixed meanings.
constexpr uint8_t NO_ASSOCIATION
Layer association value for a layer that belongs to no copper layer.
static wxString ruleNetLiteral(const wxString &aNetName)
An expression string literal, whose only escape is \', inside a quoted rules file string.
wxString ToWx(const std::string &aRaw)
Convert a stored ANSI string to a wxString.
KIBIS top(path, &reporter)
KIBIS_COMPONENT * comp
VECTOR2I center
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
int clearance
static double sweep(float aStart, float aEnd)
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
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
double DEG2RAD(double deg)
Definition trigo.h:172
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707
@ NONE
Pads are not covered.
Definition zones.h:45
@ FULL
pads are covered by copper
Definition zones.h:47