KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_io_ipc2581.cpp
Go to the documentation of this file.
1
19
20#include "pcb_io_ipc2581.h"
21
22#include <base_units.h>
23#include <bezier_curves.h>
24#include <board.h>
27#include <build_version.h>
28#include <callback_gal.h>
32#include <font/font.h>
33#include <hash_eda.h>
34#include <pcb_dimension.h>
35#include <pcb_textbox.h>
36#include <pgm_base.h>
37#include <progress_reporter.h>
39#include <string_utils.h>
40#include <wx_fstream_progress.h>
41
45
46#include <wx/log.h>
47#include <wx/numformatter.h>
48#include <wx/xml/xml.h>
49
50
56static const wxChar traceIpc2581[] = wxT( "KICAD_IPC_2581" );
57
58
63
64
66{
68 delete fp;
69
70 m_loaded_footprints.clear();
71}
72
73
75{
76 std::vector<FOOTPRINT*> retval;
77
79 retval.push_back( static_cast<FOOTPRINT*>( fp->Clone() ) );
80
81 return retval;
82}
83
84
85void PCB_IO_IPC2581::insertNode( wxXmlNode* aParent, wxXmlNode* aNode )
86{
87 // insertNode places the node at the start of the list of children
88
89 if( aParent->GetChildren() )
90 aNode->SetNext( aParent->GetChildren() );
91 else
92 aNode->SetNext( nullptr );
93
94 aParent->SetChildren( aNode );
95 aNode->SetParent( aParent );
96 m_total_bytes += 2 * aNode->GetName().size() + 5;
97}
98
99
100void PCB_IO_IPC2581::insertNodeAfter( wxXmlNode* aPrev, wxXmlNode* aNode )
101{
102 // insertNode places the node directly after aPrev
103
104 aNode->SetNext( aPrev->GetNext() );
105 aPrev->SetNext( aNode );
106 aNode->SetParent( aPrev->GetParent() );
107 m_total_bytes += 2 * aNode->GetName().size() + 5;
108}
109
110
111wxXmlNode* PCB_IO_IPC2581::insertNode( wxXmlNode* aParent, const wxString& aName )
112{
113 // Opening tag, closing tag, brackets and the closing slash
114 m_total_bytes += 2 * aName.size() + 5;
115 wxXmlNode* node = new wxXmlNode( wxXML_ELEMENT_NODE, aName );
116 insertNode( aParent, node );
117 return node;
118}
119
120
121void PCB_IO_IPC2581::appendNode( wxXmlNode* aParent, wxXmlNode* aNode )
122{
123 // AddChild iterates through the entire list of children, so we want to avoid
124 // that if possible. When we share a parent and our next sibling is null,
125 // then we are the last child and can just append to the end of the list.
126
127 static wxXmlNode* lastNode = nullptr;
128
129 if( lastNode && lastNode->GetParent() == aParent && lastNode->GetNext() == nullptr )
130 {
131 aNode->SetParent( aParent );
132 lastNode->SetNext( aNode );
133 }
134 else
135 {
136 aParent->AddChild( aNode );
137 }
138
139 lastNode = aNode;
140
141 // Opening tag, closing tag, brackets and the closing slash
142 m_total_bytes += 2 * aNode->GetName().size() + 5;
143}
144
145
146wxXmlNode* PCB_IO_IPC2581::appendNode( wxXmlNode* aParent, const wxString& aName )
147{
148 wxXmlNode* node = new wxXmlNode( wxXML_ELEMENT_NODE, aName );
149
150 appendNode( aParent, node );
151 return node;
152}
153
154
155wxString PCB_IO_IPC2581::sanitizeId( const wxString& aStr ) const
156{
157 wxString str;
158
159 if( m_version == 'C' )
160 {
161 str = aStr;
162 str.Replace( wxT( ":" ), wxT( "_" ) );
163 }
164 else
165 {
166 for( wxString::const_iterator iter = aStr.begin(); iter != aStr.end(); ++iter )
167 {
168 if( !m_acceptable_chars.count( *iter ) )
169 str.Append( '_' );
170 else
171 str.Append( *iter );
172 }
173 }
174
175 return str;
176}
177
178
179wxString PCB_IO_IPC2581::genString( const wxString& aStr, const char* aPrefix ) const
180{
181 // Build a key using the prefix and original string so that repeated calls for the same
182 // element return the same generated name.
183 wxString key = aPrefix ? wxString( aPrefix ) + wxT( ":" ) + aStr : aStr;
184
185 auto it = m_generated_names.find( key );
186
187 if( it != m_generated_names.end() )
188 return it->second;
189
190 wxString str = sanitizeId( aStr );
191
192 wxString base = str;
193 wxString name = base;
194 int suffix = 1;
195
196 while( m_element_names.count( name ) )
197 name = wxString::Format( "%s_%d", base, suffix++ );
198
199 m_element_names.insert( name );
200 m_generated_names[key] = name;
201
202 return name;
203}
204
205
206wxString PCB_IO_IPC2581::genLayerString( PCB_LAYER_ID aLayer, const char* aPrefix ) const
207{
208 return genString( m_board->GetLayerName( aLayer ), aPrefix );
209}
210
211
213 const char* aPrefix ) const
214{
215 return genString( wxString::Format( wxS( "%s_%s" ),
216 m_board->GetLayerName( aTop ),
217 m_board->GetLayerName( aBottom ) ), aPrefix );
218}
219
220
221wxString PCB_IO_IPC2581::pinName( const PAD* aPad ) const
222{
223 wxString name = aPad->GetNumber();
224
225 FOOTPRINT* fp = aPad->GetParentFootprint();
226 size_t ii = 0;
227
228 if( name.empty() && fp )
229 {
230 for( ii = 0; ii < fp->GetPadCount(); ++ii )
231 {
232 if( fp->Pads()[ii] == aPad )
233 break;
234 }
235 }
236
237 // Pins are required to have names, so if our pad doesn't have a name, we need to
238 // generate one that is unique
239 if( aPad->GetAttribute() == PAD_ATTRIB::NPTH )
240 name = wxString::Format( "NPTH%zu", ii );
241 else if( name.empty() )
242 name = wxString::Format( "PAD%zu", ii );
243
244 // Pins are scoped per-package, so we only sanitize; uniqueness is handled by
245 // the per-package pin_nodes map in addPackage().
246 return sanitizeId( name );
247}
248
249
251{
252 auto tryInsert =
253 [&]( const wxString& aName )
254 {
255 if( m_footprint_refdes_dict.count( aName ) )
256 {
257 if( m_footprint_refdes_dict.at( aName ) != aFootprint )
258 return false;
259 }
260 else
261 {
262 m_footprint_refdes_dict.insert( { aName, aFootprint } );
263 }
264
265 return true;
266 };
267
268 if( m_footprint_refdes_reverse_dict.count( aFootprint ) )
269 return m_footprint_refdes_reverse_dict.at( aFootprint );
270
271 wxString baseName = genString( aFootprint->GetReference(), "CMP" );
272 wxString name = baseName;
273 int suffix = 1;
274
275 while( !tryInsert( name ) )
276 name = wxString::Format( "%s_%d", baseName, suffix++ );
277
279
280 return name;
281}
282
283
284wxString PCB_IO_IPC2581::floatVal( double aVal, int aSigFig ) const
285{
286 wxString str = wxString::FromCDouble( aVal, aSigFig == -1 ? m_sigfig : aSigFig );
287
288 // Remove all but the last trailing zeros from str
289 while( str.EndsWith( wxT( "00" ) ) )
290 str.RemoveLast();
291
292 // We don't want to output -0.0 as this value is just 0 for fabs
293 if( str == wxT( "-0.0" ) )
294 return wxT( "0.0" );
295
296 return str;
297}
298
299
300void PCB_IO_IPC2581::addXY( wxXmlNode* aNode, const VECTOR2I& aVec, const char* aXName,
301 const char* aYName )
302{
303 if( aXName )
304 addAttribute( aNode, aXName, floatVal( m_scale * aVec.x ) );
305 else
306 addAttribute( aNode, "x", floatVal( m_scale * aVec.x ) );
307
308 if( aYName )
309 addAttribute( aNode, aYName, floatVal( -m_scale * aVec.y ) );
310 else
311 addAttribute( aNode, "y", floatVal( -m_scale * aVec.y ) );
312}
313
314
315void PCB_IO_IPC2581::addAttribute( wxXmlNode* aNode, const wxString& aName, const wxString& aValue )
316{
317 m_total_bytes += aName.size() + aValue.size() + 4;
318 aNode->AddAttribute( aName, aValue );
319}
320
321
323{
324 wxXmlNode* xmlHeaderNode = new wxXmlNode(wxXML_ELEMENT_NODE, "IPC-2581");
325 addAttribute( xmlHeaderNode, "revision", m_version);
326 addAttribute( xmlHeaderNode, "xmlns", "http://webstds.ipc.org/2581");
327 addAttribute( xmlHeaderNode, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
328 addAttribute( xmlHeaderNode, "xmlns:xsd", "http://www.w3.org/2001/XMLSchema");
329
330 if( m_version == 'B' )
331 {
332 addAttribute( xmlHeaderNode, "xsi:schemaLocation",
333 "http://webstds.ipc.org/2581 http://webstds.ipc.org/2581/IPC-2581B1.xsd" );
334 }
335 else
336 {
337 addAttribute( xmlHeaderNode, "xsi:schemaLocation",
338 "http://webstds.ipc.org/2581 http://webstds.ipc.org/2581/IPC-2581C.xsd" );
339 }
340
341 m_xml_doc->SetRoot( xmlHeaderNode );
342
343 return xmlHeaderNode;
344}
345
346
348{
350 m_progressReporter->AdvancePhase( _( "Generating content section" ) );
351
352 wxXmlNode* contentNode = appendNode( m_xml_root, "Content" );
353 addAttribute( contentNode, "roleRef", "Owner" );
354
355 wxXmlNode* node = appendNode( contentNode, "FunctionMode" );
356 addAttribute( node, "mode", "ASSEMBLY" );
357
358 // This element is deprecated in revision 'C' and later
359 if( m_version == 'B' )
360 addAttribute( node, "level", "3" );
361
362 node = appendNode( contentNode, "StepRef" );
363 wxFileName fn( m_board->GetFileName() );
364 addAttribute( node, "name", genString( fn.GetName(), "BOARD" ) );
365
366 wxXmlNode* color_node = generateContentStackup( contentNode );
367
368 if( m_version == 'C' )
369 {
370 contentNode->AddChild( color_node );
371 m_line_node = appendNode( contentNode, "DictionaryLineDesc" );
373
374 wxXmlNode* fillNode = appendNode( contentNode, "DictionaryFillDesc" );
375 addAttribute( fillNode, "units", m_units_str );
376
377 m_shape_std_node = appendNode( contentNode, "DictionaryStandard" );
379
380 m_shape_user_node = appendNode( contentNode, "DictionaryUser" );
382 }
383 else
384 {
385 m_shape_std_node = appendNode( contentNode, "DictionaryStandard" );
387
388 m_shape_user_node = appendNode( contentNode, "DictionaryUser" );
390
391 m_line_node = appendNode( contentNode, "DictionaryLineDesc" );
393
394 contentNode->AddChild( color_node );
395 }
396
397 return contentNode;
398}
399
400
401void PCB_IO_IPC2581::addLocationNode( wxXmlNode* aNode, double aX, double aY )
402{
403 wxXmlNode* location_node = appendNode( aNode, "Location" );
404 addXY( location_node, VECTOR2I( aX, aY ) );
405}
406
407
408void PCB_IO_IPC2581::addLocationNode( wxXmlNode* aNode, const PAD& aPad, bool aRelative )
409{
410 VECTOR2D pos{};
411
412 if( aRelative )
413 pos = aPad.GetFPRelativePosition();
414 else
415 pos = aPad.GetPosition();
416
417 if( aPad.GetOffset( PADSTACK::ALL_LAYERS ).x != 0 || aPad.GetOffset( PADSTACK::ALL_LAYERS ).y != 0 )
418 pos += aPad.GetOffset( PADSTACK::ALL_LAYERS );
419
420 addLocationNode( aNode, pos.x, pos.y );
421}
422
423
424void PCB_IO_IPC2581::addLocationNode( wxXmlNode* aNode, const PCB_SHAPE& aShape )
425{
426 VECTOR2D pos{};
427
428 switch( aShape.GetShape() )
429 {
430 // Rectangles in KiCad are mapped by their corner while IPC2581 uses the center
432 pos = aShape.GetPosition()
433 + VECTOR2I( aShape.GetRectangleWidth() / 2.0, aShape.GetRectangleHeight() / 2.0 );
434 break;
435 // Both KiCad and IPC2581 use the center of the circle
436 case SHAPE_T::CIRCLE:
437 pos = aShape.GetPosition();
438 break;
439
440 // KiCad uses the exact points on the board, so we want the reference location to be 0,0
441 case SHAPE_T::POLY:
442 case SHAPE_T::BEZIER:
443 case SHAPE_T::SEGMENT:
444 case SHAPE_T::ARC:
445 pos = VECTOR2D( 0, 0 );
446 break;
447
449 wxFAIL;
450 }
451
452 addLocationNode( aNode, pos.x, pos.y );
453}
454
455
456size_t PCB_IO_IPC2581::lineHash( int aWidth, LINE_STYLE aDashType )
457{
458 size_t hash = hash_val( aWidth );
459 hash_combine( hash, aDashType );
460
461 return hash;
462}
463
464
466{
467 return hash_fp_item( &aShape, HASH_POS | REL_COORD );
468}
469
470
471wxXmlNode* PCB_IO_IPC2581::generateContentStackup( wxXmlNode* aContentNode )
472{
473
474 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
475 BOARD_STACKUP& stackup = bds.GetStackupDescriptor();
476 stackup.SynchronizeWithBoard( &bds );
477
478 wxXmlNode* color_node = new wxXmlNode( wxXML_ELEMENT_NODE, "DictionaryColor" );
479
480 for( BOARD_STACKUP_ITEM* item: stackup.GetList() )
481 {
482 wxString layer_name = item->GetLayerName();
483 int sub_layer_count = 1;
484
485 if( layer_name.empty() )
486 layer_name = m_board->GetLayerName( item->GetBrdLayerId() );
487
488 layer_name = genString( layer_name, "LAYER" );
489
490 if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
491 {
492 layer_name = genString( wxString::Format( "DIELECTRIC_%d", item->GetDielectricLayerId() ),
493 "LAYER" );
494 sub_layer_count = item->GetSublayersCount();
495 }
496 else
497 {
498 m_layer_name_map.emplace( item->GetBrdLayerId(), layer_name );
499 }
500
501 for( int sub_idx = 0; sub_idx < sub_layer_count; sub_idx++ )
502 {
503 wxString sub_layer_name = layer_name;
504
505 if( sub_idx > 0 )
506 sub_layer_name += wxString::Format( "_%d", sub_idx );
507
508 wxXmlNode* node = appendNode( aContentNode, "LayerRef" );
509 addAttribute( node, "name", sub_layer_name );
510
511 if( !IsPrmSpecified( item->GetColor( sub_idx ) ) )
512 continue;
513
514 wxXmlNode* entry_color = appendNode( color_node, "EntryColor" );
515 addAttribute( entry_color, "id", genString( sub_layer_name, "COLOR" ) );
516 wxXmlNode* color = appendNode( entry_color, "Color" );
517
518 wxString colorName = item->GetColor( sub_idx );
519
520 if( colorName.StartsWith( wxT( "#" ) ) ) // This is a user defined color,
521 // not in standard color list.
522 {
523 COLOR4D layer_color( colorName );
524 addAttribute( color, "r", wxString::Format( "%d",
525 KiROUND( layer_color.r * 255 ) ) );
526 addAttribute( color, "g", wxString::Format( "%d",
527 KiROUND( layer_color.g * 255 ) ) );
528 addAttribute( color, "b", wxString::Format( "%d",
529 KiROUND( layer_color.b * 255 ) ) );
530 }
531 else
532 {
533 for( const FAB_LAYER_COLOR& fab_color : GetStandardColors( item->GetType() ) )
534 {
535 if( fab_color.GetName() == colorName )
536 {
537 addAttribute( color, "r", wxString::Format( "%d", KiROUND( fab_color.GetColor( item->GetType() ).r * 255 ) ) );
538 addAttribute( color, "g", wxString::Format( "%d", KiROUND( fab_color.GetColor( item->GetType() ).g * 255 ) ) );
539 addAttribute( color, "b", wxString::Format( "%d", KiROUND( fab_color.GetColor( item->GetType() ).b * 255 ) ) );
540 break;
541 }
542 }
543 }
544 }
545 }
546
547 return color_node;
548}
549
550
551void PCB_IO_IPC2581::addFillDesc( wxXmlNode* aNode, FILL_T aFill, bool aForce )
552{
553 if( aFill == FILL_T::FILLED_SHAPE )
554 {
555 // By default, we do not fill shapes because FILL is the default value for most.
556 // But for some outlines, we may need to force a fill.
557 if( aForce )
558 {
559 wxXmlNode* fillDesc_node = appendNode( aNode, "FillDesc" );
560 addAttribute( fillDesc_node, "fillProperty", "FILL" );
561 }
562 }
563 else
564 {
565 wxXmlNode* fillDesc_node = appendNode( aNode, "FillDesc" );
566 addAttribute( fillDesc_node, "fillProperty", "HOLLOW" );
567 }
568}
569
570
571void PCB_IO_IPC2581::addLineDesc( wxXmlNode* aNode, int aWidth, LINE_STYLE aDashType, bool aForce )
572{
573 wxCHECK_RET( aNode, "aNode is null" );
574
575 if( aWidth < 0 )
576 return;
577
578 wxXmlNode* entry_node = nullptr;
579
580 if( !aForce )
581 {
582 size_t hash = lineHash( aWidth, aDashType );
583 wxString name = wxString::Format( "LINE_%zu", m_line_dict.size() + 1 );
584 auto[ iter, inserted ] = m_line_dict.emplace( hash, name );
585
586 // Either add a new entry or reference an existing one
587 wxXmlNode* lineDesc_node = appendNode( aNode, "LineDescRef" );
588 addAttribute( lineDesc_node, "id", iter->second );
589
590 if( !inserted )
591 return;
592
593 entry_node = appendNode( m_line_node, "EntryLineDesc" );
594 addAttribute( entry_node, "id", name );
595 }
596 else
597 {
598 // Force the LineDesc to be added directly to the parent node
599 entry_node = aNode;
600 }
601
602 wxXmlNode* line_node = appendNode( entry_node, "LineDesc" );
603 addAttribute( line_node, "lineWidth", floatVal( m_scale * aWidth ) );
604 addAttribute( line_node, "lineEnd", "ROUND" );
605
606 switch( aDashType )
607 {
608 case LINE_STYLE::DOT:
609 addAttribute( line_node, "lineProperty", "DOTTED" );
610 break;
611 case LINE_STYLE::DASH:
612 addAttribute( line_node, "lineProperty", "DASHED" );
613 break;
615 addAttribute( line_node, "lineProperty", "CENTER" );
616 break;
618 addAttribute( line_node, "lineProperty", "PHANTOM" );
619 break;
620 default:
621 break;
622 }
623}
624
625
626void PCB_IO_IPC2581::addKnockoutText( wxXmlNode* aContentNode, PCB_TEXT* aText )
627{
628 SHAPE_POLY_SET finalPoly;
629
630 aText->TransformTextToPolySet( finalPoly, 0, ARC_HIGH_DEF, ERROR_INSIDE );
631 finalPoly.Fracture();
632
633 for( int ii = 0; ii < finalPoly.OutlineCount(); ++ii )
634 addContourNode( aContentNode, finalPoly, ii );
635}
636
637
638void PCB_IO_IPC2581::addText( wxXmlNode* aContentNode, EDA_TEXT* aText,
639 const KIFONT::METRICS& aFontMetrics )
640{
642 KIFONT::FONT* font = aText->GetDrawFont( nullptr );
643 TEXT_ATTRIBUTES attrs = aText->GetAttributes();
644
646 attrs.m_Angle = aText->GetDrawRotation();
647 attrs.m_Multiline = false;
648
649 wxXmlNode* text_node = appendNode( aContentNode, "UserSpecial" );
650
651 std::list<VECTOR2I> pts;
652
653 auto push_pts =
654 [&]()
655 {
656 if( pts.size() < 2 )
657 return;
658
659 wxXmlNode* line_node = nullptr;
660
661 // Polylines are only allowed for more than 3 points (in version B).
662 // Otherwise, we have to use a line
663 if( pts.size() < 3 )
664 {
665 line_node = appendNode( text_node, "Line" );
666 addXY( line_node, pts.front(), "startX", "startY" );
667 addXY( line_node, pts.back(), "endX", "endY" );
668 }
669 else
670 {
671 line_node = appendNode( text_node, "Polyline" );
672 wxXmlNode* point_node = appendNode( line_node, "PolyBegin" );
673 addXY( point_node, pts.front() );
674
675 auto iter = pts.begin();
676
677 for( ++iter; iter != pts.end(); ++iter )
678 {
679 wxXmlNode* point_node = appendNode( line_node, "PolyStepSegment" );
680 addXY( point_node, *iter );
681 }
682
683 }
684
685 addLineDesc( line_node, attrs.m_StrokeWidth, LINE_STYLE::SOLID );
686 pts.clear();
687 };
688
689 CALLBACK_GAL callback_gal( empty_opts,
690 // Stroke callback
691 [&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2 )
692 {
693 if( !pts.empty() )
694 {
695 if( aPt1 == pts.back() )
696 pts.push_back( aPt2 );
697 else if( aPt2 == pts.front() )
698 pts.push_front( aPt1 );
699 else if( aPt1 == pts.front() )
700 pts.push_front( aPt2 );
701 else if( aPt2 == pts.back() )
702 pts.push_back( aPt1 );
703 else
704 {
705 push_pts();
706 pts.push_back( aPt1 );
707 pts.push_back( aPt2 );
708 }
709 }
710 else
711 {
712 pts.push_back( aPt1 );
713 pts.push_back( aPt2 );
714 }
715 },
716 // Polygon callback
717 [&]( const SHAPE_LINE_CHAIN& aPoly )
718 {
719 if( aPoly.PointCount() < 3 )
720 return;
721
722 wxXmlNode* outline_node = appendNode( text_node, "Outline" );
723 wxXmlNode* poly_node = appendNode( outline_node, "Polygon" );
724 addLineDesc( outline_node, 0, LINE_STYLE::SOLID );
725
726 const std::vector<VECTOR2I>& pts = aPoly.CPoints();
727 wxXmlNode* point_node = appendNode( poly_node, "PolyBegin" );
728 addXY( point_node, pts.front() );
729
730 for( size_t ii = 1; ii < pts.size(); ++ii )
731 {
732 wxXmlNode* point_node =
733 appendNode( poly_node, "PolyStepSegment" );
734 addXY( point_node, pts[ii] );
735 }
736
737 point_node = appendNode( poly_node, "PolyStepSegment" );
738 addXY( point_node, pts.front() );
739 } );
740
741 //TODO: handle multiline text
742
743 font->Draw( &callback_gal, aText->GetShownText( true ), aText->GetTextPos(), attrs,
744 aFontMetrics );
745
746 if( !pts.empty() )
747 push_pts();
748
749 if( text_node->GetChildren() == nullptr )
750 {
751 aContentNode->RemoveChild( text_node );
752 delete text_node;
753 }
754}
755
756
757void PCB_IO_IPC2581::addShape( wxXmlNode* aContentNode, const PAD& aPad, PCB_LAYER_ID aLayer )
758{
759 size_t hash = hash_fp_item( &aPad, 0 );
760 auto iter = m_std_shape_dict.find( hash );
761
762 if( iter != m_std_shape_dict.end() )
763 {
764 wxXmlNode* shape_node = appendNode( aContentNode, "StandardPrimitiveRef" );
765 addAttribute( shape_node, "id", iter->second );
766 return;
767 }
768
769 int maxError = m_board->GetDesignSettings().m_MaxError;
770 wxString name;
771 VECTOR2I expansion{ 0, 0 };
772
773 if( LSET( { F_Mask, B_Mask } ).Contains( aLayer ) )
774 expansion.x = expansion.y = 2 * aPad.GetSolderMaskExpansion( PADSTACK::ALL_LAYERS );
775
776 if( LSET( { F_Paste, B_Paste } ).Contains( aLayer ) )
777 expansion = 2 * aPad.GetSolderPasteMargin( PADSTACK::ALL_LAYERS );
778
779 switch( aPad.GetShape( PADSTACK::ALL_LAYERS ) )
780 {
782 {
783 name = wxString::Format( "CIRCLE_%zu", m_std_shape_dict.size() + 1 );
784 m_std_shape_dict.emplace( hash, name );
785
786 wxXmlNode* entry_node = appendNode( m_shape_std_node, "EntryStandard" );
787 addAttribute( entry_node, "id", name );
788
789 wxXmlNode* circle_node = appendNode( entry_node, "Circle" );
790 circle_node->AddAttribute( "diameter",
791 floatVal( m_scale * ( expansion.x + aPad.GetSizeX() ) ) );
792 break;
793 }
794
796 {
797 name = wxString::Format( "RECT_%zu", m_std_shape_dict.size() + 1 );
798 m_std_shape_dict.emplace( hash, name );
799
800 wxXmlNode* entry_node = appendNode( m_shape_std_node, "EntryStandard" );
801 addAttribute( entry_node, "id", name );
802
803 wxXmlNode* rect_node = appendNode( entry_node, "RectCenter" );
804 VECTOR2D pad_size = aPad.GetSize( PADSTACK::ALL_LAYERS ) + expansion;
805 addAttribute( rect_node, "width", floatVal( m_scale * std::abs( pad_size.x ) ) );
806 addAttribute( rect_node, "height", floatVal( m_scale * std::abs( pad_size.y ) ) );
807
808 break;
809
810 }
811 case PAD_SHAPE::OVAL:
812 {
813 name = wxString::Format( "OVAL_%zu", m_std_shape_dict.size() + 1 );
814 m_std_shape_dict.emplace( hash, name );
815
816 wxXmlNode* entry_node = appendNode( m_shape_std_node, "EntryStandard" );
817 addAttribute( entry_node, "id", name );
818
819 wxXmlNode* oval_node = appendNode( entry_node, "Oval" );
820 VECTOR2D pad_size = aPad.GetSize( PADSTACK::ALL_LAYERS ) + expansion;
821 addAttribute( oval_node, "width", floatVal( m_scale * pad_size.x ) );
822 addAttribute( oval_node, "height", floatVal( m_scale * pad_size.y ) );
823
824 break;
825 }
826
828 {
829 name = wxString::Format( "ROUNDRECT_%zu", m_std_shape_dict.size() + 1 );
830 m_std_shape_dict.emplace( hash, name );
831
832 wxXmlNode* entry_node = appendNode( m_shape_std_node, "EntryStandard" );
833 addAttribute( entry_node, "id", name );
834
835 wxXmlNode* roundrect_node = appendNode( entry_node, "RectRound" );
836 VECTOR2D pad_size = aPad.GetSize( PADSTACK::ALL_LAYERS ) + expansion;
837 addAttribute( roundrect_node, "width", floatVal( m_scale * pad_size.x ) );
838 addAttribute( roundrect_node, "height", floatVal( m_scale * pad_size.y ) );
839 roundrect_node->AddAttribute( "radius",
841 addAttribute( roundrect_node, "upperRight", "true" );
842 addAttribute( roundrect_node, "upperLeft", "true" );
843 addAttribute( roundrect_node, "lowerRight", "true" );
844 addAttribute( roundrect_node, "lowerLeft", "true" );
845
846 break;
847 }
848
850 {
851 name = wxString::Format( "RECTCHAMFERED_%zu", m_std_shape_dict.size() + 1 );
852 m_std_shape_dict.emplace( hash, name );
853
854 wxXmlNode* entry_node = appendNode( m_shape_std_node, "EntryStandard" );
855 addAttribute( entry_node, "id", name );
856
857 wxXmlNode* chamfered_node = appendNode( entry_node, "RectCham" );
858 VECTOR2D pad_size = aPad.GetSize( PADSTACK::ALL_LAYERS ) + expansion;
859 addAttribute( chamfered_node, "width", floatVal( m_scale * pad_size.x ) );
860 addAttribute( chamfered_node, "height", floatVal( m_scale * pad_size.y ) );
861
862 int shorterSide = std::min( pad_size.x, pad_size.y );
863 int chamfer = std::max( 0, KiROUND( aPad.GetChamferRectRatio( PADSTACK::ALL_LAYERS ) * shorterSide ) );
864
865 addAttribute( chamfered_node, "chamfer", floatVal( m_scale * chamfer ) );
866
867 int positions = aPad.GetChamferPositions( PADSTACK::ALL_LAYERS );
868
869 if( positions & RECT_CHAMFER_TOP_LEFT )
870 addAttribute( chamfered_node, "upperLeft", "true" );
871 if( positions & RECT_CHAMFER_TOP_RIGHT )
872 addAttribute( chamfered_node, "upperRight", "true" );
873 if( positions & RECT_CHAMFER_BOTTOM_LEFT )
874 addAttribute( chamfered_node, "lowerLeft", "true" );
875 if( positions & RECT_CHAMFER_BOTTOM_RIGHT )
876 addAttribute( chamfered_node, "lowerRight", "true" );
877
878 break;
879 }
880
882 {
883 name = wxString::Format( "TRAPEZOID_%zu", m_std_shape_dict.size() + 1 );
884 m_std_shape_dict.emplace( hash, name );
885
886 wxXmlNode* entry_node = appendNode( m_shape_std_node, "EntryStandard" );
887 addAttribute( entry_node, "id", name );
888
889 VECTOR2I pad_size = aPad.GetSize( PADSTACK::ALL_LAYERS );
890 VECTOR2I trap_delta = aPad.GetDelta( PADSTACK::ALL_LAYERS );
891 SHAPE_POLY_SET outline;
892 outline.NewOutline();
893 int dx = pad_size.x / 2;
894 int dy = pad_size.y / 2;
895 int ddx = trap_delta.x / 2;
896 int ddy = trap_delta.y / 2;
897
898 outline.Append( -dx - ddy, dy + ddx );
899 outline.Append( dx + ddy, dy - ddx );
900 outline.Append( dx - ddy, -dy + ddx );
901 outline.Append( -dx + ddy, -dy - ddx );
902
903 // Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate()
904 // which can create bad shapes if margin.x is < 0
905 if( expansion.x )
906 {
908 maxError );
909 }
910
911 addContourNode( entry_node, outline );
912
913 break;
914 }
916 {
917 name = wxString::Format( "CUSTOM_%zu", m_std_shape_dict.size() + 1 );
918 m_std_shape_dict.emplace( hash, name );
919
920 wxXmlNode* entry_node = appendNode( m_shape_std_node, "EntryStandard" );
921 addAttribute( entry_node, "id", name );
922
923 SHAPE_POLY_SET shape;
925
926 if( expansion != VECTOR2I( 0, 0 ) )
927 {
928 shape.InflateWithLinkedHoles( std::max( expansion.x, expansion.y ),
930 }
931
932 addContourNode( entry_node, shape );
933 break;
934 }
935 default:
936 Report( _( "Pad has unsupported type; it was skipped." ), RPT_SEVERITY_WARNING );
937 break;
938 }
939
940 if( !name.empty() )
941 {
942 m_std_shape_dict.emplace( hash, name );
943 wxXmlNode* shape_node = appendNode( aContentNode, "StandardPrimitiveRef" );
944 addAttribute( shape_node, "id", name );
945 }
946}
947
948
949void PCB_IO_IPC2581::addShape( wxXmlNode* aContentNode, const PCB_SHAPE& aShape )
950{
951 size_t hash = shapeHash( aShape );
952 auto iter = m_user_shape_dict.find( hash );
953 wxString name;
954
955 if( iter != m_user_shape_dict.end() )
956 {
957 wxXmlNode* shape_node = appendNode( aContentNode, "UserPrimitiveRef" );
958 addAttribute( shape_node, "id", iter->second );
959 return;
960 }
961
962 switch( aShape.GetShape() )
963 {
964 case SHAPE_T::CIRCLE:
965 {
966 name = wxString::Format( "UCIRCLE_%zu", m_user_shape_dict.size() + 1 );
967 m_user_shape_dict.emplace( hash, name );
968 int diameter = aShape.GetRadius() * 2.0;
969 int width = aShape.GetStroke().GetWidth();
970 LINE_STYLE dash = aShape.GetStroke().GetLineStyle();
971
972
973 wxXmlNode* entry_node = appendNode( m_shape_user_node, "EntryUser" );
974 addAttribute( entry_node, "id", name );
975 wxXmlNode* special_node = appendNode( entry_node, "UserSpecial" );
976
977 wxXmlNode* circle_node = appendNode( special_node, "Circle" );
978
979 if( aShape.GetFillMode() == FILL_T::NO_FILL )
980 {
981 addAttribute( circle_node, "diameter", floatVal( m_scale * diameter ) );
982 addLineDesc( circle_node, width, dash, true );
983 }
984 else
985 {
986 // IPC2581 does not allow strokes on filled elements
987 addAttribute( circle_node, "diameter", floatVal( m_scale * ( diameter + width ) ) );
988 }
989
990 addFillDesc( circle_node, aShape.GetFillMode() );
991
992 break;
993 }
994
996 {
997 name = wxString::Format( "URECT_%zu", m_user_shape_dict.size() + 1 );
998 m_user_shape_dict.emplace( hash, name );
999
1000 wxXmlNode* entry_node = appendNode( m_shape_user_node, "EntryUser" );
1001 addAttribute( entry_node, "id", name );
1002 wxXmlNode* special_node = appendNode( entry_node, "UserSpecial" );
1003
1004 int width = std::abs( aShape.GetRectangleWidth() );
1005 int height = std::abs( aShape.GetRectangleHeight() );
1006 int stroke_width = aShape.GetStroke().GetWidth();
1007 LINE_STYLE dash = aShape.GetStroke().GetLineStyle();
1008
1009 wxXmlNode* rect_node = appendNode( special_node, "RectRound" );
1010 addLineDesc( rect_node, aShape.GetStroke().GetWidth(), aShape.GetStroke().GetLineStyle(),
1011 true );
1012
1013 if( aShape.GetFillMode() == FILL_T::NO_FILL )
1014 {
1015 addAttribute( rect_node, "upperRight", "false" );
1016 addAttribute( rect_node, "upperLeft", "false" );
1017 addAttribute( rect_node, "lowerRight", "false" );
1018 addAttribute( rect_node, "lowerLeft", "false" );
1019 }
1020 else
1021 {
1022 addAttribute( rect_node, "upperRight", "true" );
1023 addAttribute( rect_node, "upperLeft", "true" );
1024 addAttribute( rect_node, "lowerRight", "true" );
1025 addAttribute( rect_node, "lowerLeft", "true" );
1026 width += stroke_width;
1027 height += stroke_width;
1028 }
1029
1030 addFillDesc( rect_node, aShape.GetFillMode() );
1031
1032 addAttribute( rect_node, "width", floatVal( m_scale * width ) );
1033 addAttribute( rect_node, "height", floatVal( m_scale * height ) );
1034 addAttribute( rect_node, "radius", floatVal( m_scale * ( stroke_width / 2.0 ) ) );
1035
1036 break;
1037 }
1038
1039 case SHAPE_T::POLY:
1040 {
1041 name = wxString::Format( "UPOLY_%zu", m_user_shape_dict.size() + 1 );
1042 m_user_shape_dict.emplace( hash, name );
1043
1044 wxXmlNode* entry_node = appendNode( m_shape_user_node, "EntryUser" );
1045 addAttribute( entry_node, "id", name );
1046
1047 // If we are stroking a polygon, we need two contours. This is only allowed
1048 // inside a "UserSpecial" shape
1049 wxXmlNode* special_node = appendNode( entry_node, "UserSpecial" );
1050
1051 const SHAPE_POLY_SET& poly_set = aShape.GetPolyShape();
1052
1053 for( int ii = 0; ii < poly_set.OutlineCount(); ++ii )
1054 {
1055 if( aShape.GetFillMode() != FILL_T::NO_FILL )
1056 {
1057 // IPC2581 does not allow strokes on filled elements
1058 addContourNode( special_node, poly_set, ii, FILL_T::FILLED_SHAPE, 0,
1060 }
1061
1062 addContourNode( special_node, poly_set, ii, FILL_T::NO_FILL,
1063 aShape.GetStroke().GetWidth(), aShape.GetStroke().GetLineStyle() );
1064 }
1065
1066 break;
1067 }
1068
1069 case SHAPE_T::ARC:
1070 {
1071 wxXmlNode* arc_node = appendNode( aContentNode, "Arc" );
1072 addXY( arc_node, aShape.GetStart(), "startX", "startY" );
1073 addXY( arc_node, aShape.GetEnd(), "endX", "endY" );
1074 addXY( arc_node, aShape.GetCenter(), "centerX", "centerY" );
1075
1076 //N.B. because our coordinate system is flipped, we need to flip the arc direction
1077 addAttribute( arc_node, "clockwise", !aShape.IsClockwiseArc() ? "true" : "false" );
1078
1079 if( aShape.GetStroke().GetWidth() > 0 )
1080 {
1081 addLineDesc( arc_node, aShape.GetStroke().GetWidth(),
1082 aShape.GetStroke().GetLineStyle(), true );
1083 }
1084
1085 break;
1086 }
1087
1088 case SHAPE_T::BEZIER:
1089 {
1090 wxXmlNode* polyline_node = appendNode( aContentNode, "Polyline" );
1091 std::vector<VECTOR2I> ctrlPoints = { aShape.GetStart(), aShape.GetBezierC1(),
1092 aShape.GetBezierC2(), aShape.GetEnd() };
1093 BEZIER_POLY converter( ctrlPoints );
1094 std::vector<VECTOR2I> points;
1095 converter.GetPoly( points, ARC_HIGH_DEF );
1096
1097 wxXmlNode* point_node = appendNode( polyline_node, "PolyBegin" );
1098 addXY( point_node, points[0] );
1099
1100 for( size_t i = 1; i < points.size(); i++ )
1101 {
1102 wxXmlNode* point_node = appendNode( polyline_node, "PolyStepSegment" );
1103 addXY( point_node, points[i] );
1104 }
1105
1106 if( aShape.GetStroke().GetWidth() > 0 )
1107 {
1108 addLineDesc( polyline_node, aShape.GetStroke().GetWidth(),
1109 aShape.GetStroke().GetLineStyle(), true );
1110 }
1111
1112 break;
1113 }
1114
1115 case SHAPE_T::SEGMENT:
1116 {
1117 wxXmlNode* line_node = appendNode( aContentNode, "Line" );
1118 addXY( line_node, aShape.GetStart(), "startX", "startY" );
1119 addXY( line_node, aShape.GetEnd(), "endX", "endY" );
1120
1121 if( aShape.GetStroke().GetWidth() > 0 )
1122 {
1123 addLineDesc( line_node, aShape.GetStroke().GetWidth(),
1124 aShape.GetStroke().GetLineStyle(), true );
1125 }
1126
1127 break;
1128 }
1129
1130 case SHAPE_T::UNDEFINED:
1131 wxFAIL;
1132 }
1133
1134 if( !name.empty() )
1135 {
1136 wxXmlNode* shape_node = appendNode( aContentNode, "UserPrimitiveRef" );
1137 addAttribute( shape_node, "id", name );
1138 }
1139
1140}
1141
1142
1143void PCB_IO_IPC2581::addSlotCavity( wxXmlNode* aNode, const PAD& aPad, const wxString& aName )
1144{
1145 wxXmlNode* slotNode = appendNode( aNode, "SlotCavity" );
1146 addAttribute( slotNode, "name", aName );
1147 addAttribute( slotNode, "platingStatus", aPad.GetAttribute() == PAD_ATTRIB::PTH ? "PLATED"
1148 : "NONPLATED" );
1149 addAttribute( slotNode, "plusTol", "0.0" );
1150 addAttribute( slotNode, "minusTol", "0.0" );
1151
1152 if( m_version > 'B' )
1153 addLocationNode( slotNode, aPad, false );
1154
1155 // Normally only oblong drill shapes should reach this code path since m_slot_holes
1156 // is filtered to pads where DrillSizeX != DrillSizeY. However, use a fallback to
1157 // ensure valid XML is always generated.
1159 {
1160 VECTOR2I drill_size = aPad.GetDrillSize();
1161 EDA_ANGLE rotation = aPad.GetOrientation();
1162
1163 // IPC-2581C requires width >= height for Oval primitive
1164 // Swap dimensions if needed and adjust rotation accordingly
1165 if( drill_size.y > drill_size.x )
1166 {
1167 std::swap( drill_size.x, drill_size.y );
1168 rotation += ANGLE_90;
1169 }
1170
1171 // Add Xform if rotation is needed (must come before Feature per IPC-2581C schema)
1172 if( rotation != ANGLE_0 )
1173 {
1174 wxXmlNode* xformNode = appendNode( slotNode, "Xform" );
1175 addAttribute( xformNode, "rotation", floatVal( rotation.AsDegrees() ) );
1176 }
1177
1178 // Use IPC-2581 Oval primitive for oblong slots
1179 wxXmlNode* ovalNode = appendNode( slotNode, "Oval" );
1180 addAttribute( ovalNode, "width", floatVal( m_scale * drill_size.x ) );
1181 addAttribute( ovalNode, "height", floatVal( m_scale * drill_size.y ) );
1182 }
1183 else
1184 {
1185 // Fallback to polygon outline for non-oblong shapes
1186 SHAPE_POLY_SET poly_set;
1187 int maxError = m_board->GetDesignSettings().m_MaxError;
1188 aPad.TransformHoleToPolygon( poly_set, 0, maxError, ERROR_INSIDE );
1189
1190 addOutlineNode( slotNode, poly_set );
1191 }
1192}
1193
1194
1196{
1197 wxXmlNode* logisticNode = appendNode( m_xml_root, "LogisticHeader" );
1198
1199 wxXmlNode* roleNode = appendNode( logisticNode, "Role" );
1200 addAttribute( roleNode, "id", "Owner" );
1201 addAttribute( roleNode, "roleFunction", "SENDER" );
1202
1203 m_enterpriseNode = appendNode( logisticNode, "Enterprise" );
1204 addAttribute( m_enterpriseNode, "id", "UNKNOWN" );
1205 addAttribute( m_enterpriseNode, "code", "NONE" );
1206
1207 wxXmlNode* personNode = appendNode( logisticNode, "Person" );
1208 addAttribute( personNode, "name", "UNKNOWN" );
1209 addAttribute( personNode, "enterpriseRef", "UNKNOWN" );
1210 addAttribute( personNode, "roleRef", "Owner" );
1211
1212 return logisticNode;
1213}
1214
1215
1217{
1218 if( m_progressReporter )
1219 m_progressReporter->AdvancePhase( _( "Generating history section" ) );
1220
1221 wxXmlNode* historyNode = appendNode( m_xml_root, "HistoryRecord" );
1222 addAttribute( historyNode, "number", "1" );
1223 addAttribute( historyNode, "origination", wxDateTime::Now().FormatISOCombined() );
1224 addAttribute( historyNode, "software", "KiCad EDA" );
1225 addAttribute( historyNode, "lastChange", wxDateTime::Now().FormatISOCombined() );
1226
1227 wxXmlNode* fileRevisionNode = appendNode( historyNode, "FileRevision" );
1228 addAttribute( fileRevisionNode, "fileRevisionId", "1" );
1229 addAttribute( fileRevisionNode, "comment", "NO COMMENT" );
1230 addAttribute( fileRevisionNode, "label", "NO LABEL" );
1231
1232 wxXmlNode* softwarePackageNode = appendNode( fileRevisionNode, "SoftwarePackage" );
1233 addAttribute( softwarePackageNode, "name", "KiCad" );
1234 addAttribute( softwarePackageNode, "revision", GetMajorMinorPatchVersion() );
1235 addAttribute( softwarePackageNode, "vendor", "KiCad EDA" );
1236
1237 wxXmlNode* certificationNode = appendNode( softwarePackageNode, "Certification" );
1238 addAttribute( certificationNode, "certificationStatus", "SELFTEST" );
1239
1240 return historyNode;
1241}
1242
1243
1244wxXmlNode* PCB_IO_IPC2581::generateBOMSection( wxXmlNode* aEcadNode )
1245{
1246 if( m_progressReporter )
1247 m_progressReporter->AdvancePhase( _( "Generating BOM section" ) );
1248
1249 struct REFDES
1250 {
1251 wxString m_name;
1252 wxString m_pkg;
1253 bool m_populate;
1254 wxString m_layer;
1255 };
1256
1257 struct BOM_ENTRY
1258 {
1259 BOM_ENTRY()
1260 {
1261 m_refdes = new std::vector<REFDES>();
1262 m_props = new std::map<wxString, wxString>();
1263 m_count = 0;
1264 m_pads = 0;
1265 }
1266
1267 ~BOM_ENTRY()
1268 {
1269 delete m_refdes;
1270 delete m_props;
1271 }
1272
1273 wxString m_OEMDesignRef; // String combining LIB+FP+VALUE
1274 int m_count;
1275 int m_pads;
1276 wxString m_type;
1277 wxString m_description;
1278
1279 std::vector<REFDES>* m_refdes;
1280 std::map<wxString, wxString>* m_props;
1281 };
1282
1283 std::set<std::unique_ptr<struct BOM_ENTRY>,
1284 std::function<bool( const std::unique_ptr<struct BOM_ENTRY>&,
1285 const std::unique_ptr<struct BOM_ENTRY>& )>> bom_entries(
1286 []( const std::unique_ptr<struct BOM_ENTRY>& a,
1287 const std::unique_ptr<struct BOM_ENTRY>& b )
1288 {
1289 return a->m_OEMDesignRef < b->m_OEMDesignRef;
1290 } );
1291
1292 for( FOOTPRINT* fp_it : m_board->Footprints() )
1293 {
1294 std::unique_ptr<FOOTPRINT> fp( static_cast<FOOTPRINT*>( fp_it->Clone() ) );
1295 fp->SetParentGroup( nullptr );
1296 fp->SetPosition( {0, 0} );
1297 fp->SetOrientation( ANGLE_0 );
1298
1299 size_t hash = hash_fp_item( fp.get(), HASH_POS | REL_COORD );
1300 auto iter = m_footprint_dict.find( hash );
1301
1302 if( iter == m_footprint_dict.end() )
1303 {
1304 Report( wxString::Format( _( "Footprint %s not found in dictionary; BOM data may be incomplete." ),
1305 fp->GetFPID().GetLibItemName().wx_str() ),
1307 continue;
1308 }
1309
1310 auto entry = std::make_unique<struct BOM_ENTRY>();
1311
1314 if( auto it = m_OEMRef_dict.find( fp_it ); it != m_OEMRef_dict.end() )
1315 {
1316 entry->m_OEMDesignRef = it->second;
1317 }
1318 else
1319 {
1320 Report( wxString::Format( _( "Component \"%s\" missing OEM reference; BOM entry will be skipped." ),
1321 fp->GetFPID().GetLibItemName().wx_str() ),
1323 }
1324
1325 entry->m_OEMDesignRef = genString( entry->m_OEMDesignRef, "REF" );
1326 entry->m_count = 1;
1327 entry->m_pads = fp->GetPadCount();
1328
1329 // TODO: The options are "ELECTRICAL", "MECHANICAL", "PROGRAMMABLE", "DOCUMENT", "MATERIAL"
1330 // We need to figure out how to determine this.
1331 const wxString variantName = m_board ? m_board->GetCurrentVariant() : wxString();
1332
1333 if( entry->m_pads == 0 || fp_it->GetExcludedFromBOMForVariant( variantName ) )
1334 entry->m_type = "DOCUMENT";
1335 else
1336 entry->m_type = "ELECTRICAL";
1337
1338 // Use the footprint's Description field if it exists
1339 const PCB_FIELD* descField = fp_it->GetField( FIELD_T::DESCRIPTION );
1340
1341 if( descField && !descField->GetShownText( false ).IsEmpty() )
1342 entry->m_description = descField->GetShownText( false );
1343
1344 auto[ bom_iter, inserted ] = bom_entries.insert( std::move( entry ) );
1345
1346 if( !inserted )
1347 ( *bom_iter )->m_count++;
1348
1349 REFDES refdes;
1350 refdes.m_name = componentName( fp_it );
1351 refdes.m_pkg = fp->GetFPID().GetLibItemName().wx_str();
1352 refdes.m_populate = !fp->GetDNPForVariant( variantName )
1353 && !fp->GetExcludedFromBOMForVariant( variantName );
1354 refdes.m_layer = m_layer_name_map[fp_it->GetLayer()];
1355
1356 ( *bom_iter )->m_refdes->push_back( refdes );
1357
1358 // TODO: This amalgamates all the properties from all the footprints. We need to decide
1359 // if we want to group footprints by their properties
1360 for( PCB_FIELD* prop : fp->GetFields() )
1361 {
1362 // We don't include Reference, Datasheet, or Description in BOM characteristics.
1363 // Value and any user-defined fields are included. Reference is captured above,
1364 // and Description is used for the BomItem description attribute.
1365 if( prop->IsMandatory() && !prop->IsValue() )
1366 continue;
1367
1368 ( *bom_iter )->m_props->emplace( prop->GetName(), prop->GetShownText( false ) );
1369 }
1370 }
1371
1372 if( bom_entries.empty() )
1373 return nullptr;
1374
1375 wxFileName fn( m_board->GetFileName() );
1376
1377 wxXmlNode* bomNode = new wxXmlNode( wxXML_ELEMENT_NODE, "Bom" );
1378 m_xml_root->InsertChild( bomNode, aEcadNode );
1379 addAttribute( bomNode, "name", genString( fn.GetName(), "BOM" ) );
1380
1381 wxXmlNode* bomHeaderNode = appendNode( bomNode, "BomHeader" );
1382 addAttribute( bomHeaderNode, "revision", "1.0" );
1383 addAttribute( bomHeaderNode, "assembly", genString( fn.GetName() ) );
1384
1385 wxXmlNode* stepRefNode = appendNode( bomHeaderNode, "StepRef" );
1386 addAttribute( stepRefNode, "name", genString( fn.GetName(), "BOARD" ) );
1387
1388 for( const auto& entry : bom_entries )
1389 {
1390 wxXmlNode* bomEntryNode = appendNode( bomNode, "BomItem" );
1391 addAttribute( bomEntryNode, "OEMDesignNumberRef", entry->m_OEMDesignRef );
1392 addAttribute( bomEntryNode, "quantity", wxString::Format( "%d", entry->m_count ) );
1393 addAttribute( bomEntryNode, "pinCount", wxString::Format( "%d", entry->m_pads ) );
1394 addAttribute( bomEntryNode, "category", entry->m_type );
1395
1396 if( !entry->m_description.IsEmpty() )
1397 addAttribute( bomEntryNode, "description", entry->m_description );
1398
1399 for( const REFDES& refdes : *( entry->m_refdes ) )
1400 {
1401 wxXmlNode* refdesNode = appendNode( bomEntryNode, "RefDes" );
1402 addAttribute( refdesNode, "name", refdes.m_name );
1403 addAttribute( refdesNode, "packageRef", genString( refdes.m_pkg, "PKG" ) );
1404 addAttribute( refdesNode, "populate", refdes.m_populate ? "true" : "false" );
1405 addAttribute( refdesNode, "layerRef", refdes.m_layer );
1406 }
1407
1408 wxXmlNode* characteristicsNode = appendNode( bomEntryNode, "Characteristics" );
1409 addAttribute( characteristicsNode, "category", entry->m_type );
1410
1411 for( const auto& prop : *( entry->m_props ) )
1412 {
1413 wxXmlNode* textualDefNode = appendNode( characteristicsNode, "Textual" );
1414 addAttribute( textualDefNode, "definitionSource", "KICAD" );
1415 addAttribute( textualDefNode, "textualCharacteristicName", prop.first );
1416 addAttribute( textualDefNode, "textualCharacteristicValue", prop.second );
1417 }
1418 }
1419
1420 return bomNode;
1421}
1422
1423
1425{
1426 if( m_progressReporter )
1427 m_progressReporter->AdvancePhase( _( "Generating CAD data" ) );
1428
1429 wxXmlNode* ecadNode = appendNode( m_xml_root, "Ecad" );
1430 addAttribute( ecadNode, "name", "Design" );
1431
1432 addCadHeader( ecadNode );
1433
1434 wxXmlNode* cadDataNode = appendNode( ecadNode, "CadData" );
1435 generateCadLayers( cadDataNode );
1436 generateDrillLayers( cadDataNode );
1437 generateAuxilliaryLayers( cadDataNode );
1438 generateStackup( cadDataNode );
1439 generateStepSection( cadDataNode );
1440
1442
1443 return ecadNode;
1444}
1445
1446
1447void PCB_IO_IPC2581::generateCadSpecs( wxXmlNode* aCadLayerNode )
1448{
1449 BOARD_DESIGN_SETTINGS& dsnSettings = m_board->GetDesignSettings();
1450 BOARD_STACKUP& stackup = dsnSettings.GetStackupDescriptor();
1451 stackup.SynchronizeWithBoard( &dsnSettings );
1452
1453 std::vector<BOARD_STACKUP_ITEM*> layers = stackup.GetList();
1454 std::set<PCB_LAYER_ID> added_layers;
1455
1456 for( int i = 0; i < stackup.GetCount(); i++ )
1457 {
1458 BOARD_STACKUP_ITEM* stackup_item = layers.at( i );
1459
1460 for( int sublayer_id = 0; sublayer_id < stackup_item->GetSublayersCount(); sublayer_id++ )
1461 {
1462 wxString ly_name = stackup_item->GetLayerName();
1463
1464 if( ly_name.IsEmpty() )
1465 {
1466 if( IsValidLayer( stackup_item->GetBrdLayerId() ) )
1467 ly_name = m_board->GetLayerName( stackup_item->GetBrdLayerId() );
1468
1469 if( ly_name.IsEmpty() && stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1470 {
1471 ly_name = wxString::Format( "DIELECTRIC_%d", stackup_item->GetDielectricLayerId() );
1472
1473 if( sublayer_id > 0 )
1474 ly_name += wxString::Format( "_%d", sublayer_id );
1475 }
1476 }
1477
1478 ly_name = genString( ly_name, "SPEC_LAYER" );
1479
1480 wxXmlNode* specNode = appendNode( aCadLayerNode, "Spec" );
1481 addAttribute( specNode, "name", ly_name );
1482 wxXmlNode* generalNode = appendNode( specNode, "General" );
1483 addAttribute( generalNode, "type", "MATERIAL" );
1484 wxXmlNode* propertyNode = appendNode( generalNode, "Property" );
1485
1486 switch ( stackup_item->GetType() )
1487 {
1489 {
1490 addAttribute( propertyNode, "text", "COPPER" );
1491 wxXmlNode* conductorNode = appendNode( specNode, "Conductor" );
1492 addAttribute( conductorNode, "type", "CONDUCTIVITY" );
1493 propertyNode = appendNode( conductorNode, "Property" );
1494 addAttribute( propertyNode, "unit", wxT( "SIEMENS/M" ) );
1495 addAttribute( propertyNode, "value", wxT( "5.959E7" ) );
1496 break;
1497 }
1499 {
1500 addAttribute( propertyNode, "text", stackup_item->GetMaterial() );
1501 propertyNode = appendNode( generalNode, "Property" );
1502 addAttribute( propertyNode, "text", wxString::Format( "Type : %s",
1503 stackup_item->GetTypeName() ) );
1504 wxXmlNode* dielectricNode = appendNode( specNode, "Dielectric" );
1505 addAttribute( dielectricNode, "type", "DIELECTRIC_CONSTANT" );
1506 propertyNode = appendNode( dielectricNode, "Property" );
1507 addAttribute( propertyNode, "value",
1508 floatVal( stackup_item->GetEpsilonR( sublayer_id ) ) );
1509 dielectricNode = appendNode( specNode, "Dielectric" );
1510 addAttribute( dielectricNode, "type", "LOSS_TANGENT" );
1511 propertyNode = appendNode( dielectricNode, "Property" );
1512 addAttribute( propertyNode, "value",
1513 floatVal( stackup_item->GetLossTangent( sublayer_id ) ) );
1514 break;
1515 }
1517 addAttribute( propertyNode, "text", stackup_item->GetTypeName() );
1518 propertyNode = appendNode( generalNode, "Property" );
1519 addAttribute( propertyNode, "text", wxString::Format( "Color : %s",
1520 stackup_item->GetColor() ) );
1521 propertyNode = appendNode( generalNode, "Property" );
1522 addAttribute( propertyNode, "text", wxString::Format( "Type : %s",
1523 stackup_item->GetTypeName() ) );
1524 break;
1526 {
1527 addAttribute( propertyNode, "text", "SOLDERMASK" );
1528 propertyNode = appendNode( generalNode, "Property" );
1529 addAttribute( propertyNode, "text", wxString::Format( "Color : %s",
1530 stackup_item->GetColor() ) );
1531 propertyNode = appendNode( generalNode, "Property" );
1532 addAttribute( propertyNode, "text", wxString::Format( "Type : %s",
1533 stackup_item->GetTypeName() ) );
1534
1535 // Generate Epsilon R if > 1.0 (value <= 1.0 means not specified)
1536 if( stackup_item->GetEpsilonR( sublayer_id ) > 1.0 )
1537 {
1538 wxXmlNode* dielectricNode = appendNode( specNode, "Dielectric" );
1539 addAttribute( dielectricNode, "type", "DIELECTRIC_CONSTANT" );
1540 propertyNode = appendNode( dielectricNode, "Property" );
1541 addAttribute( propertyNode, "value", floatVal( stackup_item->GetEpsilonR( sublayer_id ) ) );
1542 }
1543
1544 // Generate LossTangent if > 0.0 (value <= 0.0 means not specified)
1545 if( stackup_item->GetLossTangent( sublayer_id ) > 0.0 )
1546 {
1547 wxXmlNode* dielectricNode = appendNode( specNode, "Dielectric" );
1548 addAttribute( dielectricNode, "type", "LOSS_TANGENT" );
1549 propertyNode = appendNode( dielectricNode, "Property" );
1550 addAttribute( propertyNode, "value", floatVal( stackup_item->GetLossTangent( sublayer_id ) ) );
1551 }
1552 break;
1553 }
1554 default:
1555 break;
1556 }
1557 }
1558 }
1559}
1560
1561
1562void PCB_IO_IPC2581::addCadHeader( wxXmlNode* aEcadNode )
1563{
1564 wxXmlNode* cadHeaderNode = appendNode( aEcadNode, "CadHeader" );
1565 addAttribute( cadHeaderNode, "units", m_units_str );
1566
1567 m_cad_header_node = cadHeaderNode;
1568
1569 generateCadSpecs( cadHeaderNode );
1570}
1571
1572
1574{
1575 return ( aLayer >= F_Cu && aLayer <= User_9 ) || aLayer == UNDEFINED_LAYER;
1576}
1577
1578
1579void PCB_IO_IPC2581::addLayerAttributes( wxXmlNode* aNode, PCB_LAYER_ID aLayer )
1580{
1581 switch( aLayer )
1582 {
1583 case F_Adhes:
1584 case B_Adhes:
1585 addAttribute( aNode, "layerFunction", "GLUE" );
1586 addAttribute( aNode, "polarity", "POSITIVE" );
1587 addAttribute( aNode, "side", aLayer == F_Adhes ? "TOP" : "BOTTOM" );
1588 break;
1589 case F_Paste:
1590 case B_Paste:
1591 addAttribute( aNode, "layerFunction", "SOLDERPASTE" );
1592 addAttribute( aNode, "polarity", "POSITIVE" );
1593 addAttribute( aNode, "side", aLayer == F_Paste ? "TOP" : "BOTTOM" );
1594 break;
1595 case F_SilkS:
1596 case B_SilkS:
1597 addAttribute( aNode, "layerFunction", "SILKSCREEN" );
1598 addAttribute( aNode, "polarity", "POSITIVE" );
1599 addAttribute( aNode, "side", aLayer == F_SilkS ? "TOP" : "BOTTOM" );
1600 break;
1601 case F_Mask:
1602 case B_Mask:
1603 addAttribute( aNode, "layerFunction", "SOLDERMASK" );
1604 addAttribute( aNode, "polarity", "POSITIVE" );
1605 addAttribute( aNode, "side", aLayer == F_Mask ? "TOP" : "BOTTOM" );
1606 break;
1607 case Edge_Cuts:
1608 addAttribute( aNode, "layerFunction", "BOARD_OUTLINE" );
1609 addAttribute( aNode, "polarity", "POSITIVE" );
1610 addAttribute( aNode, "side", "ALL" );
1611 break;
1612 case B_CrtYd:
1613 case F_CrtYd:
1614 addAttribute( aNode, "layerFunction", "COURTYARD" );
1615 addAttribute( aNode, "polarity", "POSITIVE" );
1616 addAttribute( aNode, "side", aLayer == F_CrtYd ? "TOP" : "BOTTOM" );
1617 break;
1618 case B_Fab:
1619 case F_Fab:
1620 addAttribute( aNode, "layerFunction", "ASSEMBLY" );
1621 addAttribute( aNode, "polarity", "POSITIVE" );
1622 addAttribute( aNode, "side", aLayer == F_Fab ? "TOP" : "BOTTOM" );
1623 break;
1624 case Dwgs_User:
1625 case Cmts_User:
1626 case Eco1_User:
1627 case Eco2_User:
1628 case Margin:
1629 case User_1:
1630 case User_2:
1631 case User_3:
1632 case User_4:
1633 case User_5:
1634 case User_6:
1635 case User_7:
1636 case User_8:
1637 case User_9:
1638 addAttribute( aNode, "layerFunction", "DOCUMENT" );
1639 addAttribute( aNode, "polarity", "POSITIVE" );
1640 addAttribute( aNode, "side", "NONE" );
1641 break;
1642
1643 default:
1644 if( IsCopperLayer( aLayer ) )
1645 {
1646 addAttribute( aNode, "layerFunction", "CONDUCTOR" );
1647 addAttribute( aNode, "polarity", "POSITIVE" );
1648 addAttribute( aNode, "side",
1649 aLayer == F_Cu ? "TOP"
1650 : aLayer == B_Cu ? "BOTTOM"
1651 : "INTERNAL" );
1652 }
1653
1654 break; // Do not handle other layers
1655 }
1656}
1657
1658
1659void PCB_IO_IPC2581::generateStackup( wxXmlNode* aCadLayerNode )
1660{
1661 BOARD_DESIGN_SETTINGS& dsnSettings = m_board->GetDesignSettings();
1662 BOARD_STACKUP& stackup = dsnSettings.GetStackupDescriptor();
1663 stackup.SynchronizeWithBoard( &dsnSettings );
1664
1665 wxXmlNode* stackupNode = appendNode( aCadLayerNode, "Stackup" );
1666 addAttribute( stackupNode, "name", "Primary_Stackup" );
1667 addAttribute( stackupNode, "overallThickness", floatVal( m_scale * stackup.BuildBoardThicknessFromStackup() ) );
1668 addAttribute( stackupNode, "tolPlus", "0.0" );
1669 addAttribute( stackupNode, "tolMinus", "0.0" );
1670 addAttribute( stackupNode, "whereMeasured", "MASK" );
1671
1672 if( m_version > 'B' )
1673 addAttribute( stackupNode, "stackupStatus", "PROPOSED" );
1674
1675 wxXmlNode* stackupGroup = appendNode( stackupNode, "StackupGroup" );
1676 addAttribute( stackupGroup, "name", "Primary_Stackup_Group" );
1677 addAttribute( stackupGroup, "thickness", floatVal( m_scale * stackup.BuildBoardThicknessFromStackup() ) );
1678 addAttribute( stackupGroup, "tolPlus", "0.0" );
1679 addAttribute( stackupGroup, "tolMinus", "0.0" );
1680
1681 std::vector<BOARD_STACKUP_ITEM*> layers = stackup.GetList();
1682 std::set<PCB_LAYER_ID> added_layers;
1683
1684 for( int i = 0; i < stackup.GetCount(); i++ )
1685 {
1686 BOARD_STACKUP_ITEM* stackup_item = layers.at( i );
1687
1688 for( int sublayer_id = 0; sublayer_id < stackup_item->GetSublayersCount(); sublayer_id++ )
1689 {
1690
1691 wxXmlNode* stackupLayer = appendNode( stackupGroup, "StackupLayer" );
1692 wxString ly_name = stackup_item->GetLayerName();
1693
1694 if( ly_name.IsEmpty() )
1695 {
1696 if( IsValidLayer( stackup_item->GetBrdLayerId() ) )
1697 ly_name = m_board->GetLayerName( stackup_item->GetBrdLayerId() );
1698
1699 if( ly_name.IsEmpty() && stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1700 {
1701 ly_name = wxString::Format( "DIELECTRIC_%d", stackup_item->GetDielectricLayerId() );
1702
1703 if( sublayer_id > 0 )
1704 ly_name += wxString::Format( "_%d", sublayer_id );
1705 }
1706 }
1707
1708 wxString spec_name = genString( ly_name, "SPEC_LAYER" );
1709 ly_name = genString( ly_name, "LAYER" );
1710
1711 addAttribute( stackupLayer, "layerOrGroupRef", ly_name );
1712 addAttribute( stackupLayer, "thickness", floatVal( m_scale * stackup_item->GetThickness() ) );
1713 addAttribute( stackupLayer, "tolPlus", "0.0" );
1714 addAttribute( stackupLayer, "tolMinus", "0.0" );
1715 addAttribute( stackupLayer, "sequence", wxString::Format( "%d", i ) );
1716
1717 wxXmlNode* specLayerNode = appendNode( stackupLayer, "SpecRef" );
1718 addAttribute( specLayerNode, "id", spec_name );
1719 }
1720 }
1721}
1722
1723
1724void PCB_IO_IPC2581::generateCadLayers( wxXmlNode* aCadLayerNode )
1725{
1726
1727 BOARD_DESIGN_SETTINGS& dsnSettings = m_board->GetDesignSettings();
1728 BOARD_STACKUP& stackup = dsnSettings.GetStackupDescriptor();
1729 stackup.SynchronizeWithBoard( &dsnSettings );
1730
1731 std::vector<BOARD_STACKUP_ITEM*> layers = stackup.GetList();
1732 std::set<PCB_LAYER_ID> added_layers;
1733
1734 for( int i = 0; i < stackup.GetCount(); i++ )
1735 {
1736 BOARD_STACKUP_ITEM* stackup_item = layers.at( i );
1737
1738 if( !isValidLayerFor2581( stackup_item->GetBrdLayerId() ) )
1739 continue;
1740
1741 for( int sublayer_id = 0; sublayer_id < stackup_item->GetSublayersCount(); sublayer_id++ )
1742 {
1743 wxXmlNode* cadLayerNode = appendNode( aCadLayerNode, "Layer" );
1744 wxString ly_name = stackup_item->GetLayerName();
1745
1746 if( ly_name.IsEmpty() )
1747 {
1748
1749 if( IsValidLayer( stackup_item->GetBrdLayerId() ) )
1750 ly_name = m_board->GetLayerName( stackup_item->GetBrdLayerId() );
1751
1752 if( ly_name.IsEmpty() && stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1753 {
1754 ly_name = wxString::Format( "DIELECTRIC_%d", stackup_item->GetDielectricLayerId() );
1755
1756 if( sublayer_id > 0 )
1757 ly_name += wxString::Format( "_%d", sublayer_id );
1758 }
1759 }
1760
1761 ly_name = genString( ly_name, "LAYER" );
1762
1763 addAttribute( cadLayerNode, "name", ly_name );
1764
1765 if( stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1766 {
1767 if( stackup_item->GetTypeName() == KEY_CORE )
1768 addAttribute( cadLayerNode, "layerFunction", "DIELCORE" );
1769 else
1770 addAttribute( cadLayerNode, "layerFunction", "DIELPREG" );
1771
1772 addAttribute( cadLayerNode, "polarity", "POSITIVE" );
1773 addAttribute( cadLayerNode, "side", "INTERNAL" );
1774 continue;
1775 }
1776 else
1777 {
1778 added_layers.insert( stackup_item->GetBrdLayerId() );
1779 addLayerAttributes( cadLayerNode, stackup_item->GetBrdLayerId() );
1780 m_layer_name_map.emplace( stackup_item->GetBrdLayerId(), ly_name );
1781 }
1782 }
1783 }
1784
1785 LSEQ layer_seq = m_board->GetEnabledLayers().Seq();
1786
1787 for( PCB_LAYER_ID layer : layer_seq )
1788 {
1789 if( added_layers.find( layer ) != added_layers.end() || !isValidLayerFor2581( layer ) )
1790 continue;
1791
1792 wxString ly_name = genLayerString( layer, "LAYER" );
1793 m_layer_name_map.emplace( layer, ly_name );
1794 added_layers.insert( layer );
1795 wxXmlNode* cadLayerNode = appendNode( aCadLayerNode, "Layer" );
1796 addAttribute( cadLayerNode, "name", ly_name );
1797
1798 addLayerAttributes( cadLayerNode, layer );
1799 }
1800}
1801
1802
1803void PCB_IO_IPC2581::generateDrillLayers( wxXmlNode* aCadLayerNode )
1804{
1805 for( BOARD_ITEM* item : m_board->Tracks() )
1806 {
1807 if( item->Type() == PCB_VIA_T )
1808 {
1809 PCB_VIA* via = static_cast<PCB_VIA*>( item );
1810 m_drill_layers[std::make_pair( via->TopLayer(), via->BottomLayer() )].push_back( via );
1811 }
1812 }
1813
1814 for( FOOTPRINT* fp : m_board->Footprints() )
1815 {
1816 for( PAD* pad : fp->Pads() )
1817 {
1818 if( pad->HasDrilledHole() )
1819 m_drill_layers[std::make_pair( F_Cu, B_Cu )].push_back( pad );
1820 else if( pad->HasHole() )
1821 m_slot_holes[std::make_pair( F_Cu, B_Cu )].push_back( pad );
1822 }
1823 }
1824
1825 for( const auto& [layers, vec] : m_drill_layers )
1826 {
1827 wxXmlNode* drillNode = appendNode( aCadLayerNode, "Layer" );
1828 drillNode->AddAttribute( "name", genLayersString( layers.first, layers.second, "DRILL" ) );
1829 addAttribute( drillNode, "layerFunction", "DRILL" );
1830 addAttribute( drillNode, "polarity", "POSITIVE" );
1831 addAttribute( drillNode, "side", "ALL" );
1832
1833 wxXmlNode* spanNode = appendNode( drillNode, "Span" );
1834 addAttribute( spanNode, "fromLayer", genLayerString( layers.first, "LAYER" ) );
1835 addAttribute( spanNode, "toLayer", genLayerString( layers.second, "LAYER" ) );
1836 }
1837
1838 for( const auto& [layers, vec] : m_slot_holes )
1839 {
1840 wxXmlNode* drillNode = appendNode( aCadLayerNode, "Layer" );
1841 drillNode->AddAttribute( "name", genLayersString( layers.first, layers.second, "SLOT" ) );
1842
1843 addAttribute( drillNode, "layerFunction", "ROUT" );
1844 addAttribute( drillNode, "polarity", "POSITIVE" );
1845 addAttribute( drillNode, "side", "ALL" );
1846
1847 wxXmlNode* spanNode = appendNode( drillNode, "Span" );
1848 addAttribute( spanNode, "fromLayer", genLayerString( layers.first, "LAYER" ) );
1849 addAttribute( spanNode, "toLayer", genLayerString( layers.second, "LAYER" ) );
1850 }
1851}
1852
1853
1854void PCB_IO_IPC2581::generateAuxilliaryLayers( wxXmlNode* aCadLayerNode )
1855{
1856 for( BOARD_ITEM* item : m_board->Tracks() )
1857 {
1858 if( item->Type() != PCB_VIA_T )
1859 continue;
1860
1861 PCB_VIA* via = static_cast<PCB_VIA*>( item );
1862
1863 std::vector<std::tuple<auxLayerType, PCB_LAYER_ID, PCB_LAYER_ID>> new_layers;
1864
1865 if( via->Padstack().IsFilled().value_or( false ) )
1866 new_layers.emplace_back( auxLayerType::FILLING, via->TopLayer(), via->BottomLayer() );
1867
1868 if( via->Padstack().IsCapped().value_or( false ) )
1869 new_layers.emplace_back( auxLayerType::CAPPING, via->TopLayer(), via->BottomLayer() );
1870
1871 for( PCB_LAYER_ID layer : { via->TopLayer(), via->BottomLayer() } )
1872 {
1873 if( via->Padstack().IsPlugged( layer ).value_or( false ) )
1874 new_layers.emplace_back( auxLayerType::PLUGGING, layer, UNDEFINED_LAYER );
1875
1876 if( via->Padstack().IsCovered( layer ).value_or( false ) )
1877 new_layers.emplace_back( auxLayerType::COVERING, layer, UNDEFINED_LAYER );
1878
1879 if( via->Padstack().IsTented( layer ).value_or( false ) )
1880 new_layers.emplace_back( auxLayerType::TENTING, layer, UNDEFINED_LAYER );
1881 }
1882
1883 for( auto& tuple : new_layers )
1884 m_auxilliary_Layers[tuple].push_back( via );
1885 }
1886
1887 for( const auto& [layers, vec] : m_auxilliary_Layers )
1888 {
1889 bool add_node = true;
1890
1891 wxString name;
1892 wxString layerFunction;
1893
1894 // clang-format off: suggestion is inconsitent
1895 switch( std::get<0>(layers) )
1896 {
1898 name = "COVERING";
1899 layerFunction = "COATINGNONCOND";
1900 break;
1902 name = "PLUGGING";
1903 layerFunction = "HOLEFILL";
1904 break;
1906 name = "TENTING";
1907 layerFunction = "COATINGNONCOND";
1908 break;
1910 name = "FILLING";
1911 layerFunction = "HOLEFILL";
1912 break;
1914 name = "CAPPING";
1915 layerFunction = "COATINGCOND";
1916 break;
1917 default:
1918 add_node = false;
1919 break;
1920 }
1921 // clang-format on: suggestion is inconsitent
1922
1923 if( add_node && !vec.empty() )
1924 {
1925 wxXmlNode* node = appendNode( aCadLayerNode, "LAYER" );
1926 addAttribute( node, "layerFunction", layerFunction );
1927 addAttribute( node, "polarity", "POSITIVE" );
1928
1929 if( std::get<2>( layers ) == UNDEFINED_LAYER )
1930 {
1931 addAttribute( node, "name", genLayerString( std::get<1>( layers ), TO_UTF8( name ) ) );
1932 addAttribute( node, "side", IsFrontLayer( std::get<1>( layers ) ) ? "TOP" : "BOTTOM" );
1933 }
1934 else
1935 {
1936 addAttribute( node, "name",
1937 genLayersString( std::get<1>( layers ), std::get<2>( layers ), TO_UTF8( name ) ) );
1938
1939 const bool first_external = std::get<1>( layers ) == F_Cu || std::get<1>( layers ) == B_Cu;
1940 const bool second_external = std::get<2>( layers ) == F_Cu || std::get<2>( layers ) == B_Cu;
1941
1942 if( first_external )
1943 {
1944 if( second_external )
1945 addAttribute( node, "side", "ALL" );
1946 else
1947 addAttribute( node, "side", "FRONT" );
1948 }
1949 else
1950 {
1951 if( second_external )
1952 addAttribute( node, "side", "BACK" );
1953 else
1954 addAttribute( node, "side", "INTERNAL" );
1955 }
1956
1957 wxXmlNode* spanNode = appendNode( node, "SPAN" );
1958 addAttribute( spanNode, "fromLayer", genLayerString( std::get<1>( layers ), "LAYER" ) );
1959 addAttribute( spanNode, "toLayer", genLayerString( std::get<2>( layers ), "LAYER" ) );
1960 }
1961 }
1962 }
1963}
1964
1965
1966void PCB_IO_IPC2581::generateStepSection( wxXmlNode* aCadNode )
1967{
1968 wxXmlNode* stepNode = appendNode( aCadNode, "Step" );
1969 wxFileName fn( m_board->GetFileName() );
1970 addAttribute( stepNode, "name", genString( fn.GetName(), "BOARD" ) );
1971
1972 if( m_version > 'B' )
1973 addAttribute( stepNode, "type", "BOARD" );
1974
1975 wxXmlNode* datumNode = appendNode( stepNode, "Datum" );
1976 addAttribute( datumNode, "x", "0.0" );
1977 addAttribute( datumNode, "y", "0.0" );
1978
1979 generateProfile( stepNode );
1980 generateComponents( stepNode );
1981
1982 m_last_padstack = insertNode( stepNode, "NonstandardAttribute" );
1983 addAttribute( m_last_padstack, "name", "FOOTPRINT_COUNT" );
1984 addAttribute( m_last_padstack, "type", "INTEGER" );
1985 addAttribute( m_last_padstack, "value", wxString::Format( "%zu", m_board->Footprints().size() ) );
1986
1987 generateLayerFeatures( stepNode );
1988 generateLayerSetDrill( stepNode );
1989 generateLayerSetAuxilliary( stepNode );
1990}
1991
1992
1993void PCB_IO_IPC2581::addPad( wxXmlNode* aContentNode, const PAD* aPad, PCB_LAYER_ID aLayer )
1994{
1995 wxXmlNode* padNode = appendNode( aContentNode, "Pad" );
1996 FOOTPRINT* fp = aPad->GetParentFootprint();
1997
1998 addPadStack( padNode, aPad );
1999
2000 if( aPad->GetOrientation() != ANGLE_0 )
2001 {
2002 wxXmlNode* xformNode = appendNode( padNode, "Xform" );
2003 EDA_ANGLE angle = aPad->GetOrientation().Normalize();
2004
2005 xformNode->AddAttribute( "rotation", floatVal( angle.AsDegrees() ) );
2006 }
2007
2008 addLocationNode( padNode, *aPad, false );
2009 addShape( padNode, *aPad, aLayer );
2010
2011 if( fp )
2012 {
2013 wxXmlNode* pinRefNode = appendNode( padNode, "PinRef" );
2014
2015 addAttribute( pinRefNode, "componentRef", componentName( fp ) );
2016 addAttribute( pinRefNode, "pin", pinName( aPad ) );
2017 }
2018}
2019
2020
2021void PCB_IO_IPC2581::addVia( wxXmlNode* aContentNode, const PCB_VIA* aVia, PCB_LAYER_ID aLayer )
2022{
2023 if( !aVia->FlashLayer( aLayer ) )
2024 return;
2025
2026 wxXmlNode* padNode = appendNode( aContentNode, "Pad" );
2027
2028 addPadStack( padNode, aVia );
2029 addLocationNode( padNode, aVia->GetPosition().x, aVia->GetPosition().y );
2030
2031 PAD dummy( nullptr );
2032 int hole = aVia->GetDrillValue();
2033 dummy.SetDrillSize( VECTOR2I( hole, hole ) );
2034 dummy.SetPosition( aVia->GetStart() );
2035 dummy.SetSize( aLayer, VECTOR2I( aVia->GetWidth( aLayer ), aVia->GetWidth( aLayer ) ) );
2036
2037 addShape( padNode, dummy, aLayer );
2038}
2039
2040
2041void PCB_IO_IPC2581::addPadStack( wxXmlNode* aPadNode, const PAD* aPad )
2042{
2043 size_t hash = hash_fp_item( aPad, 0 );
2044 wxString name = wxString::Format( "PADSTACK_%zu", m_padstack_dict.size() + 1 );
2045 auto [ th_pair, success ] = m_padstack_dict.emplace( hash, name );
2046
2047 addAttribute( aPadNode, "padstackDefRef", th_pair->second );
2048
2049 // If we did not insert a new padstack, then we have already added it to the XML
2050 // and we don't need to add it again.
2051 if( !success )
2052 return;
2053
2054 wxXmlNode* padStackDefNode = new wxXmlNode( wxXML_ELEMENT_NODE, "PadStackDef" );
2055 addAttribute( padStackDefNode, "name", name );
2057 m_padstacks.push_back( padStackDefNode );
2058
2059 if( m_last_padstack )
2060 {
2061 insertNodeAfter( m_last_padstack, padStackDefNode );
2062 m_last_padstack = padStackDefNode;
2063 }
2064
2065 // Only handle round holes here because IPC2581 does not support non-round holes
2066 // These will be handled in a slot layer
2067 if( aPad->HasDrilledHole() )
2068 {
2069 wxXmlNode* padStackHoleNode = appendNode( padStackDefNode, "PadstackHoleDef" );
2070 padStackHoleNode->AddAttribute( "name",
2071 wxString::Format( "%s%d_%d",
2072 aPad->GetAttribute() == PAD_ATTRIB::PTH ? "PTH" : "NPTH",
2073 aPad->GetDrillSizeX(), aPad->GetDrillSizeY() ) );
2074
2075 addAttribute( padStackHoleNode, "diameter", floatVal( m_scale * aPad->GetDrillSizeX() ) );
2076 addAttribute( padStackHoleNode, "platingStatus",
2077 aPad->GetAttribute() == PAD_ATTRIB::PTH ? "PLATED" : "NONPLATED" );
2078 addAttribute( padStackHoleNode, "plusTol", "0.0" );
2079 addAttribute( padStackHoleNode, "minusTol", "0.0" );
2080 addXY( padStackHoleNode, aPad->GetOffset( PADSTACK::ALL_LAYERS ) );
2081 }
2082
2083 LSEQ layer_seq = aPad->GetLayerSet().Seq();
2084
2085 for( PCB_LAYER_ID layer : layer_seq )
2086 {
2087 FOOTPRINT* fp = aPad->GetParentFootprint();
2088
2089 if( !m_board->IsLayerEnabled( layer ) )
2090 continue;
2091
2092 wxXmlNode* padStackPadDefNode = appendNode( padStackDefNode, "PadstackPadDef" );
2093 addAttribute( padStackPadDefNode, "layerRef", m_layer_name_map[layer] );
2094 addAttribute( padStackPadDefNode, "padUse", "REGULAR" );
2095 addLocationNode( padStackPadDefNode, aPad->GetOffset( PADSTACK::ALL_LAYERS ).x, aPad->GetOffset( PADSTACK::ALL_LAYERS ).y );
2096
2097 if( aPad->HasHole() || !aPad->FlashLayer( layer ) )
2098 {
2099 PCB_SHAPE shape( nullptr, SHAPE_T::CIRCLE );
2100 shape.SetStart( aPad->GetOffset( PADSTACK::ALL_LAYERS ) );
2101 shape.SetEnd( shape.GetStart() + aPad->GetDrillSize() / 2 );
2102 addShape( padStackPadDefNode, shape );
2103 }
2104 else
2105 {
2106 addShape( padStackPadDefNode, *aPad, layer );
2107 }
2108 }
2109}
2110
2111
2112void PCB_IO_IPC2581::addPadStack( wxXmlNode* aContentNode, const PCB_VIA* aVia )
2113{
2114 size_t hash = hash_fp_item( aVia, 0 );
2115 wxString name = wxString::Format( "PADSTACK_%zu", m_padstack_dict.size() + 1 );
2116 auto [ via_pair, success ] = m_padstack_dict.emplace( hash, name );
2117
2118 addAttribute( aContentNode, "padstackDefRef", via_pair->second );
2119
2120 // If we did not insert a new padstack, then we have already added it to the XML
2121 // and we don't need to add it again.
2122 if( !success )
2123 return;
2124
2125 wxXmlNode* padStackDefNode = new wxXmlNode( wxXML_ELEMENT_NODE, "PadStackDef" );
2126 insertNodeAfter( m_last_padstack, padStackDefNode );
2127 m_last_padstack = padStackDefNode;
2128 addAttribute( padStackDefNode, "name", name );
2130
2131 wxXmlNode* padStackHoleNode = appendNode( padStackDefNode, "PadstackHoleDef" );
2132 addAttribute( padStackHoleNode, "name", wxString::Format( "PH%d", aVia->GetDrillValue() ) );
2133 padStackHoleNode->AddAttribute( "diameter", floatVal( m_scale * aVia->GetDrillValue() ) );
2134 addAttribute( padStackHoleNode, "platingStatus", "VIA" );
2135 addAttribute( padStackHoleNode, "plusTol", "0.0" );
2136 addAttribute( padStackHoleNode, "minusTol", "0.0" );
2137 addAttribute( padStackHoleNode, "x", "0.0" );
2138 addAttribute( padStackHoleNode, "y", "0.0" );
2139
2140 LSEQ layer_seq = aVia->GetLayerSet().Seq();
2141
2142 auto addPadShape{ [&]( PCB_LAYER_ID layer, const PCB_VIA* aVia, const wxString& name,
2143 bool drill ) -> void
2144 {
2145 PCB_SHAPE shape( nullptr, SHAPE_T::CIRCLE );
2146
2147 if( drill )
2148 shape.SetEnd( { KiROUND( aVia->GetDrillValue() / 2.0 ), 0 } );
2149 else
2150 shape.SetEnd( { KiROUND( aVia->GetWidth( layer ) / 2.0 ), 0 } );
2151
2152 wxXmlNode* padStackPadDefNode =
2153 appendNode( padStackDefNode, "PadstackPadDef" );
2154 addAttribute( padStackPadDefNode, "layerRef", name );
2155 addAttribute( padStackPadDefNode, "padUse", "REGULAR" );
2156
2157 addLocationNode( padStackPadDefNode, 0.0, 0.0 );
2158 addShape( padStackPadDefNode, shape );
2159 } };
2160
2161 for( PCB_LAYER_ID layer : layer_seq )
2162 {
2163 if( !aVia->FlashLayer( layer ) || !m_board->IsLayerEnabled( layer ) )
2164 continue;
2165
2166 addPadShape( layer, aVia, m_layer_name_map[layer], false );
2167 }
2168
2169 if( aVia->Padstack().IsFilled().value_or( false ) )
2170 addPadShape( UNDEFINED_LAYER, aVia, genLayersString( aVia->TopLayer(), aVia->BottomLayer(), "FILLING" ), true );
2171
2172 if( aVia->Padstack().IsCapped().value_or( false ) )
2173 addPadShape( UNDEFINED_LAYER, aVia, genLayersString( aVia->TopLayer(), aVia->BottomLayer(), "CAPPING" ), true );
2174
2175 for( PCB_LAYER_ID layer : { aVia->TopLayer(), aVia->BottomLayer() } )
2176 {
2177 if( aVia->Padstack().IsPlugged( layer ).value_or( false ) )
2178 addPadShape( layer, aVia, genLayerString( layer, "PLUGGING" ), true );
2179
2180 if( aVia->Padstack().IsCovered( layer ).value_or( false ) )
2181 addPadShape( layer, aVia, genLayerString( layer, "COVERING" ), false );
2182
2183 if( aVia->Padstack().IsTented( layer ).value_or( false ) )
2184 addPadShape( layer, aVia, genLayerString( layer, "TENTING" ), false );
2185 }
2186}
2187
2188
2189void PCB_IO_IPC2581::ensureBackdrillSpecs( const wxString& aPadstackName, const PADSTACK& aPadstack )
2190{
2191 if( m_padstack_backdrill_specs.find( aPadstackName ) != m_padstack_backdrill_specs.end() )
2192 return;
2193
2194 const PADSTACK::DRILL_PROPS& secondary = aPadstack.SecondaryDrill();
2195
2196 if( secondary.start == UNDEFINED_LAYER || secondary.end == UNDEFINED_LAYER )
2197 return;
2198
2199 if( secondary.size.x <= 0 && secondary.size.y <= 0 )
2200 return;
2201
2202 if( !m_cad_header_node )
2203 return;
2204
2205 auto layerHasRef = [&]( PCB_LAYER_ID aLayer ) -> bool
2206 {
2207 return m_layer_name_map.find( aLayer ) != m_layer_name_map.end();
2208 };
2209
2210 if( !layerHasRef( secondary.start ) || !layerHasRef( secondary.end ) )
2211 return;
2212
2213 BOARD_DESIGN_SETTINGS& dsnSettings = m_board->GetDesignSettings();
2214 BOARD_STACKUP& stackup = dsnSettings.GetStackupDescriptor();
2215 stackup.SynchronizeWithBoard( &dsnSettings );
2216
2217 auto createSpec = [&]( const PADSTACK::DRILL_PROPS& aDrill, const wxString& aSpecName ) -> wxString
2218 {
2219 if( aDrill.start == UNDEFINED_LAYER || aDrill.end == UNDEFINED_LAYER )
2220 return wxString();
2221
2222 auto startLayer = m_layer_name_map.find( aDrill.start );
2223 auto endLayer = m_layer_name_map.find( aDrill.end );
2224
2225 if( startLayer == m_layer_name_map.end() || endLayer == m_layer_name_map.end() )
2226 return wxString();
2227
2228 wxXmlNode* specNode = appendNode( m_cad_header_node, "Spec" );
2229 addAttribute( specNode, "name", aSpecName );
2230
2231 wxXmlNode* backdrillNode = appendNode( specNode, "Backdrill" );
2232 addAttribute( backdrillNode, "startLayerRef", startLayer->second );
2233 addAttribute( backdrillNode, "mustNotCutLayerRef", endLayer->second );
2234
2235 int stubLength = stackup.GetLayerDistance( aDrill.start, aDrill.end );
2236
2237 if( stubLength < 0 )
2238 stubLength = 0;
2239
2240 addAttribute( backdrillNode, "maxStubLength", floatVal( m_scale * stubLength ) );
2241
2243
2244 if( aDrill.start == F_Cu )
2245 pm_mode = aPadstack.FrontPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
2246 else if( aDrill.start == B_Cu )
2247 pm_mode = aPadstack.BackPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
2248
2249 bool isPostMachined = ( pm_mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ||
2251
2252 addAttribute( backdrillNode, "postMachining", isPostMachined ? wxT( "true" )
2253 : wxT( "false" ) );
2254
2255 m_backdrill_spec_nodes[aSpecName] = specNode;
2256
2257 return aSpecName;
2258 };
2259
2260 int specIndex = m_backdrill_spec_index + 1;
2261
2262 const PADSTACK::DRILL_PROPS& primary = aPadstack.Drill();
2263 wxString primarySpec = createSpec( primary, wxString::Format( wxT( "BD_%dA" ), specIndex ) );
2264
2265 wxString secondarySpec = createSpec( secondary, wxString::Format( wxT( "BD_%dB" ), specIndex ) );
2266
2267 if( primarySpec.IsEmpty() && secondarySpec.IsEmpty() )
2268 return;
2269
2270 m_backdrill_spec_index = specIndex;
2271 m_padstack_backdrill_specs.emplace( aPadstackName, std::make_pair( primarySpec, secondarySpec ) );
2272}
2273
2274
2275void PCB_IO_IPC2581::addBackdrillSpecRefs( wxXmlNode* aHoleNode, const wxString& aPadstackName )
2276{
2277 auto it = m_padstack_backdrill_specs.find( aPadstackName );
2278
2279 if( it == m_padstack_backdrill_specs.end() )
2280 return;
2281
2282 auto addRef = [&]( const wxString& aSpecName )
2283 {
2284 if( aSpecName.IsEmpty() )
2285 return;
2286
2287 wxXmlNode* specRefNode = appendNode( aHoleNode, "SpecRef" );
2288 addAttribute( specRefNode, "id", aSpecName );
2289 m_backdrill_spec_used.insert( aSpecName );
2290 };
2291
2292 addRef( it->second.first );
2293 addRef( it->second.second );
2294}
2295
2296
2298{
2299 if( !m_cad_header_node )
2300 return;
2301
2302 auto it = m_backdrill_spec_nodes.begin();
2303
2304 while( it != m_backdrill_spec_nodes.end() )
2305 {
2306 if( m_backdrill_spec_used.find( it->first ) == m_backdrill_spec_used.end() )
2307 {
2308 wxXmlNode* specNode = it->second;
2309
2310 if( specNode )
2311 {
2312 m_cad_header_node->RemoveChild( specNode );
2313 delete specNode;
2314 }
2315
2316 it = m_backdrill_spec_nodes.erase( it );
2317 }
2318 else
2319 {
2320 ++it;
2321 }
2322 }
2323}
2324
2325
2326bool PCB_IO_IPC2581::addPolygonNode( wxXmlNode* aParentNode,
2327 const SHAPE_LINE_CHAIN& aPolygon, FILL_T aFillType,
2328 int aWidth, LINE_STYLE aDashType )
2329{
2330 wxXmlNode* polygonNode = nullptr;
2331
2332 if( aPolygon.PointCount() < 3 )
2333 return false;
2334
2335 auto make_node =
2336 [&]()
2337 {
2338 polygonNode = appendNode( aParentNode, "Polygon" );
2339 wxXmlNode* polybeginNode = appendNode( polygonNode, "PolyBegin" );
2340
2341 const std::vector<VECTOR2I>& pts = aPolygon.CPoints();
2342 addXY( polybeginNode, pts[0] );
2343
2344 for( size_t ii = 1; ii < pts.size(); ++ii )
2345 {
2346 wxXmlNode* polyNode = appendNode( polygonNode, "PolyStepSegment" );
2347 addXY( polyNode, pts[ii] );
2348 }
2349
2350 wxXmlNode* polyendNode = appendNode( polygonNode, "PolyStepSegment" );
2351 addXY( polyendNode, pts[0] );
2352 };
2353
2354 // Allow the case where we don't want line/fill information in the polygon
2355 if( aFillType == FILL_T::NO_FILL )
2356 {
2357 make_node();
2358 // If we specify a line width, we need to add a LineDescRef node and
2359 // since this is only valid for a non-filled polygon, we need to create
2360 // the fillNode as well
2361 if( aWidth > 0 )
2362 addLineDesc( polygonNode, aWidth, aDashType, true );
2363 }
2364 else
2365 {
2366 wxCHECK( aWidth == 0, false );
2367 make_node();
2368 }
2369
2370 addFillDesc( polygonNode, aFillType );
2371
2372 return true;
2373}
2374
2375
2376bool PCB_IO_IPC2581::addPolygonCutouts( wxXmlNode* aParentNode,
2377 const SHAPE_POLY_SET::POLYGON& aPolygon )
2378{
2379 for( size_t ii = 1; ii < aPolygon.size(); ++ii )
2380 {
2381 wxCHECK2( aPolygon[ii].PointCount() >= 3, continue );
2382
2383 wxXmlNode* cutoutNode = appendNode( aParentNode, "Cutout" );
2384 wxXmlNode* polybeginNode = appendNode( cutoutNode, "PolyBegin" );
2385
2386 const std::vector<VECTOR2I>& hole = aPolygon[ii].CPoints();
2387 addXY( polybeginNode, hole[0] );
2388
2389 for( size_t jj = 1; jj < hole.size(); ++jj )
2390 {
2391 wxXmlNode* polyNode = appendNode( cutoutNode, "PolyStepSegment" );
2392 addXY( polyNode, hole[jj] );
2393 }
2394
2395 wxXmlNode* polyendNode = appendNode( cutoutNode, "PolyStepSegment" );
2396 addXY( polyendNode, hole[0] );
2397 }
2398
2399 return true;
2400}
2401
2402
2403bool PCB_IO_IPC2581::addOutlineNode( wxXmlNode* aParentNode, const SHAPE_POLY_SET& aPolySet,
2404 int aWidth, LINE_STYLE aDashType )
2405{
2406 if( aPolySet.OutlineCount() == 0 )
2407 return false;
2408
2409 wxXmlNode* outlineNode = appendNode( aParentNode, "Outline" );
2410
2411 // Outlines can only have one polygon according to the IPC-2581 spec, so
2412 // if there are more than one, we need to combine them into a single polygon
2413 const SHAPE_LINE_CHAIN* outline = &aPolySet.Outline( 0 );
2414 SHAPE_LINE_CHAIN bbox_outline;
2415 BOX2I bbox = outline->BBox();
2416
2417 if( aPolySet.OutlineCount() > 1 )
2418 {
2419 for( int ii = 1; ii < aPolySet.OutlineCount(); ++ii )
2420 {
2421 wxCHECK2( aPolySet.Outline( ii ).PointCount() >= 3, continue );
2422 bbox.Merge( aPolySet.Outline( ii ).BBox() );
2423 }
2424
2425 bbox_outline.Append( bbox.GetLeft(), bbox.GetTop() );
2426 bbox_outline.Append( bbox.GetRight(), bbox.GetTop() );
2427 bbox_outline.Append( bbox.GetRight(), bbox.GetBottom() );
2428 bbox_outline.Append( bbox.GetLeft(), bbox.GetBottom() );
2429 outline = &bbox_outline;
2430 }
2431
2432
2433 if( !addPolygonNode( outlineNode, *outline ) )
2434 wxLogTrace( traceIpc2581, wxS( "Failed to add polygon to outline" ) );
2435
2436 if( !outlineNode->GetChildren() )
2437 {
2438 aParentNode->RemoveChild( outlineNode );
2439 delete outlineNode;
2440 return false;
2441 }
2442
2443 addLineDesc( outlineNode, aWidth, aDashType );
2444
2445 return true;
2446}
2447
2448
2449bool PCB_IO_IPC2581::addContourNode( wxXmlNode* aParentNode, const SHAPE_POLY_SET& aPolySet,
2450 int aOutline, FILL_T aFillType, int aWidth, LINE_STYLE aDashType )
2451{
2452 if( aPolySet.OutlineCount() < ( aOutline + 1 ) )
2453 return false;
2454
2455 wxXmlNode* contourNode = appendNode( aParentNode, "Contour" );
2456
2457 if( addPolygonNode( contourNode, aPolySet.Outline( aOutline ), aFillType, aWidth, aDashType ) )
2458 {
2459 // Do not attempt to add cutouts to shapes that are already hollow
2460 if( aFillType != FILL_T::NO_FILL )
2461 addPolygonCutouts( contourNode, aPolySet.Polygon( aOutline ) );
2462 }
2463 else
2464 {
2465 aParentNode->RemoveChild( contourNode );
2466 delete contourNode;
2467 return false;
2468 }
2469
2470 return true;
2471}
2472
2473
2474void PCB_IO_IPC2581::generateProfile( wxXmlNode* aStepNode )
2475{
2476 SHAPE_POLY_SET board_outline;
2477
2478 if( ! m_board->GetBoardPolygonOutlines( board_outline, false ) )
2479 {
2480 Report( _( "Board outline is invalid or missing. Please run DRC." ), RPT_SEVERITY_ERROR );
2481 return;
2482 }
2483
2484 wxXmlNode* profileNode = appendNode( aStepNode, "Profile" );
2485
2486 if( !addPolygonNode( profileNode, board_outline.Outline( 0 ) ) )
2487 {
2488 wxLogTrace( traceIpc2581, wxS( "Failed to add polygon to profile" ) );
2489 aStepNode->RemoveChild( profileNode );
2490 delete profileNode;
2491 }
2492}
2493
2494
2495static bool isOppositeSideSilk( const FOOTPRINT* aFootprint, PCB_LAYER_ID aLayer )
2496{
2497 if( !aFootprint )
2498 return false;
2499
2500 if( aLayer != F_SilkS && aLayer != B_SilkS )
2501 return false;
2502
2503 if( aFootprint->IsFlipped() )
2504 return aLayer == F_SilkS;
2505
2506 return aLayer == B_SilkS;
2507}
2508
2509
2510wxXmlNode* PCB_IO_IPC2581::addPackage( wxXmlNode* aContentNode, FOOTPRINT* aFp )
2511{
2512 std::unique_ptr<FOOTPRINT> fp( static_cast<FOOTPRINT*>( aFp->Clone() ) );
2513 fp->SetParentGroup( nullptr );
2514 fp->SetPosition( { 0, 0 } );
2515 fp->SetOrientation( ANGLE_0 );
2516 // Normalize package geometry to the unflipped footprint coordinate system.
2517 if( fp->IsFlipped() )
2518 fp->Flip( fp->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
2519
2520 size_t hash = hash_fp_item( fp.get(), HASH_POS | REL_COORD );
2521 wxString name = genString( wxString::Format( "%s_%zu",
2522 fp->GetFPID().GetLibItemName().wx_str(),
2523 m_footprint_dict.size() + 1 ) );
2524
2525 auto [ iter, success ] = m_footprint_dict.emplace( hash, name );
2526 addAttribute( aContentNode, "packageRef", iter->second );
2527
2528 if( !success)
2529 return nullptr;
2530
2531 // Package and Component nodes are at the same level, so we need to find the parent
2532 // which should be the Step node
2533 wxXmlNode* packageNode = new wxXmlNode( wxXML_ELEMENT_NODE, "Package" );
2534 wxXmlNode* otherSideViewNode = nullptr; // Only set this if we have elements on the back side
2535
2536 addAttribute( packageNode, "name", name );
2537 addAttribute( packageNode, "type", "OTHER" ); // TODO: Replace with actual package type once we encode this
2538
2539 // We don't specially identify pin 1 in our footprints, so we need to guess
2540 if( fp->FindPadByNumber( "1" ) )
2541 addAttribute( packageNode, "pinOne", "1" );
2542 else if ( fp->FindPadByNumber( "A1" ) )
2543 addAttribute( packageNode, "pinOne", "A1" );
2544 else if ( fp->FindPadByNumber( "A" ) )
2545 addAttribute( packageNode, "pinOne", "A" );
2546 else if ( fp->FindPadByNumber( "a" ) )
2547 addAttribute( packageNode, "pinOne", "a" );
2548 else if ( fp->FindPadByNumber( "a1" ) )
2549 addAttribute( packageNode, "pinOne", "a1" );
2550 else if ( fp->FindPadByNumber( "Anode" ) )
2551 addAttribute( packageNode, "pinOne", "Anode" );
2552 else if ( fp->FindPadByNumber( "ANODE" ) )
2553 addAttribute( packageNode, "pinOne", "ANODE" );
2554 else
2555 addAttribute( packageNode, "pinOne", "UNKNOWN" );
2556
2557 addAttribute( packageNode, "pinOneOrientation", "OTHER" );
2558
2559 const SHAPE_POLY_SET& courtyard = fp->GetCourtyard( F_CrtYd );
2560 const SHAPE_POLY_SET& courtyard_back = fp->GetCourtyard( B_CrtYd );
2561
2562 if( courtyard.OutlineCount() > 0 )
2563 addOutlineNode( packageNode, courtyard, courtyard.Outline( 0 ).Width(), LINE_STYLE::SOLID );
2564
2565 if( courtyard_back.OutlineCount() > 0 )
2566 {
2567 if( m_version > 'B' )
2568 {
2569 otherSideViewNode = appendNode( packageNode, "OtherSideView" );
2570 addOutlineNode( otherSideViewNode, courtyard_back, courtyard_back.Outline( 0 ).Width(),
2572 }
2573 }
2574
2575 if( !courtyard.OutlineCount() && !courtyard_back.OutlineCount() )
2576 {
2577 SHAPE_POLY_SET bbox = fp->GetBoundingHull();
2578 addOutlineNode( packageNode, bbox );
2579 }
2580
2581 wxXmlNode* pickupPointNode = appendNode( packageNode, "PickupPoint" );
2582 addAttribute( pickupPointNode, "x", "0.0" );
2583 addAttribute( pickupPointNode, "y", "0.0" );
2584
2585 std::map<PCB_LAYER_ID, std::map<bool, std::vector<BOARD_ITEM*>>> elements;
2586
2587 for( BOARD_ITEM* item : fp->GraphicalItems() )
2588 {
2589 PCB_LAYER_ID layer = item->GetLayer();
2590
2594 if( layer != F_SilkS && layer != B_SilkS && layer != F_Fab && layer != B_Fab )
2595 continue;
2596
2597 if( m_version == 'B' && isOppositeSideSilk( fp.get(), layer ) )
2598 continue;
2599
2600 bool is_abs = true;
2601
2602 if( item->Type() == PCB_SHAPE_T )
2603 {
2604 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2605
2606 // Circles and Rectanges only have size information so we need to place them in
2607 // a separate node that has a location
2608 if( shape->GetShape() == SHAPE_T::CIRCLE || shape->GetShape() == SHAPE_T::RECTANGLE )
2609 is_abs = false;
2610 }
2611
2612 elements[item->GetLayer()][is_abs].push_back( item );
2613 }
2614
2615 auto add_base_node =
2616 [&]( PCB_LAYER_ID aLayer ) -> wxXmlNode*
2617 {
2618 wxXmlNode* parent = packageNode;
2619 bool is_back = aLayer == B_SilkS || aLayer == B_Fab;
2620
2621 if( is_back && m_version > 'B' )
2622 {
2623 if( !otherSideViewNode )
2624 otherSideViewNode = new wxXmlNode( wxXML_ELEMENT_NODE, "OtherSideView" );
2625
2626 parent = otherSideViewNode;
2627 }
2628
2629 wxString name;
2630
2631 if( aLayer == F_SilkS || aLayer == B_SilkS )
2632 name = "SilkScreen";
2633 else if( aLayer == F_Fab || aLayer == B_Fab )
2634 name = "AssemblyDrawing";
2635 else
2636 wxASSERT( false );
2637
2638 wxXmlNode* new_node = appendNode( parent, name );
2639 return new_node;
2640 };
2641
2642 auto add_marking_node =
2643 [&]( wxXmlNode* aNode ) -> wxXmlNode*
2644 {
2645 wxXmlNode* marking_node = appendNode( aNode, "Marking" );
2646 addAttribute( marking_node, "markingUsage", "NONE" );
2647 return marking_node;
2648 };
2649
2650 std::map<PCB_LAYER_ID, wxXmlNode*> layer_nodes;
2651 std::map<PCB_LAYER_ID, BOX2I> layer_bbox;
2652
2653 for( auto layer : { F_Fab, B_Fab } )
2654 {
2655 if( elements.find( layer ) != elements.end() )
2656 {
2657 if( elements[layer][true].size() > 0 )
2658 layer_bbox[layer] = elements[layer][true][0]->GetBoundingBox();
2659 else if( elements[layer][false].size() > 0 )
2660 layer_bbox[layer] = elements[layer][false][0]->GetBoundingBox();
2661 }
2662 }
2663
2664 for( auto& [layer, map] : elements )
2665 {
2666 wxXmlNode* layer_node = add_base_node( layer );
2667 wxXmlNode* marking_node = add_marking_node( layer_node );
2668 wxXmlNode* group_node = appendNode( marking_node, "UserSpecial" );
2669 bool update_bbox = false;
2670
2671 if( layer == F_Fab || layer == B_Fab )
2672 {
2673 layer_nodes[layer] = layer_node;
2674 update_bbox = true;
2675 }
2676
2677 for( auto& [is_abs, vec] : map )
2678 {
2679 for( BOARD_ITEM* item : vec )
2680 {
2681 wxXmlNode* output_node = nullptr;
2682
2683 if( update_bbox )
2684 layer_bbox[layer].Merge( item->GetBoundingBox() );
2685
2686 if( !is_abs )
2687 output_node = add_marking_node( layer_node );
2688 else
2689 output_node = group_node;
2690
2691 switch( item->Type() )
2692 {
2693 case PCB_TEXT_T:
2694 {
2695 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
2696
2697 if( text->IsKnockout() )
2698 addKnockoutText( output_node, text );
2699 else
2700 addText( output_node, text, text->GetFontMetrics() );
2701
2702 break;
2703 }
2704
2705 case PCB_TEXTBOX_T:
2706 {
2707 PCB_TEXTBOX* text = static_cast<PCB_TEXTBOX*>( item );
2708 addText( output_node, text, text->GetFontMetrics() );
2709
2710 // We want to force this to be a polygon to get absolute coordinates
2711 if( text->IsBorderEnabled() )
2712 {
2713 SHAPE_POLY_SET poly_set;
2714 text->GetEffectiveShape()->TransformToPolygon( poly_set, 0, ERROR_INSIDE );
2715 addContourNode( output_node, poly_set, 0, FILL_T::NO_FILL,
2716 text->GetBorderWidth() );
2717 }
2718
2719 break;
2720 }
2721
2722 case PCB_SHAPE_T:
2723 {
2724 if( !is_abs )
2725 addLocationNode( output_node, *static_cast<PCB_SHAPE*>( item ) );
2726
2727 addShape( output_node, *static_cast<PCB_SHAPE*>( item ) );
2728
2729 break;
2730 }
2731
2732 default: break;
2733 }
2734 }
2735 }
2736
2737 if( group_node->GetChildren() == nullptr )
2738 {
2739 marking_node->RemoveChild( group_node );
2740 layer_node->RemoveChild( marking_node );
2741 delete group_node;
2742 delete marking_node;
2743 }
2744 }
2745
2746 for( auto&[layer, bbox] : layer_bbox )
2747 {
2748 if( bbox.GetWidth() > 0 )
2749 {
2750 wxXmlNode* outlineNode = insertNode( layer_nodes[layer], "Outline" );
2751
2752 SHAPE_LINE_CHAIN outline;
2753 std::vector<VECTOR2I> points( 4 );
2754 points[0] = bbox.GetPosition();
2755 points[2] = bbox.GetEnd();
2756 points[1].x = points[0].x;
2757 points[1].y = points[2].y;
2758 points[3].x = points[2].x;
2759 points[3].y = points[0].y;
2760
2761 outline.Append( points );
2762 addPolygonNode( outlineNode, outline, FILL_T::NO_FILL, 0 );
2763 addLineDesc( outlineNode, 0, LINE_STYLE::SOLID );
2764 }
2765 }
2766
2767 std::map<wxString, wxXmlNode*> pin_nodes;
2768
2769 for( size_t ii = 0; ii < fp->Pads().size(); ++ii )
2770 {
2771 PAD* pad = fp->Pads()[ii];
2772 wxString name = pinName( pad );
2773 wxXmlNode* pinNode = nullptr;
2774
2775 auto [ it, inserted ] = pin_nodes.emplace( name, nullptr );
2776
2777 if( inserted )
2778 {
2779 pinNode = appendNode( packageNode, "Pin" );
2780 it->second = pinNode;
2781
2782 addAttribute( pinNode, "number", name );
2783
2784 m_net_pin_dict[pad->GetNetCode()].emplace_back(
2785 genString( fp->GetReference(), "CMP" ), name );
2786
2787 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
2788 addAttribute( pinNode, "electricalType", "MECHANICAL" );
2789 else if( pad->IsOnCopperLayer() )
2790 addAttribute( pinNode, "electricalType", "ELECTRICAL" );
2791 else
2792 addAttribute( pinNode, "electricalType", "UNDEFINED" );
2793
2794 if( pad->HasHole() )
2795 addAttribute( pinNode, "type", "THRU" );
2796 else
2797 addAttribute( pinNode, "type", "SURFACE" );
2798
2799 if( pad->GetFPRelativeOrientation() != ANGLE_0 )//|| fp->IsFlipped() )
2800 {
2801 wxXmlNode* xformNode = appendNode( pinNode, "Xform" );
2802 EDA_ANGLE pad_angle = pad->GetFPRelativeOrientation().Normalize();
2803
2804 if( fp->IsFlipped() )
2805 pad_angle = pad_angle.Invert().Normalize();
2806
2807 if( pad_angle != ANGLE_0 )
2808 xformNode->AddAttribute( "rotation", floatVal( pad_angle.AsDegrees() ) );
2809 }
2810 }
2811 else
2812 {
2813 pinNode = it->second;
2814 }
2815
2816 addLocationNode( pinNode, *pad, true );
2817 addShape( pinNode, *pad, pad->GetLayer() );
2818
2819 // We just need the padstack, we don't need the reference here. The reference will be
2820 // created in the LayerFeature set
2821 wxXmlNode dummy;
2822 addPadStack( &dummy, pad );
2823 }
2824
2825 return packageNode;
2826}
2827
2828
2829void PCB_IO_IPC2581::generateComponents( wxXmlNode* aStepNode )
2830{
2831 std::vector<wxXmlNode*> componentNodes;
2832 std::vector<wxXmlNode*> packageNodes;
2833 std::set<wxString> packageNames;
2834
2835 bool generate_unique = m_OEMRef.empty();
2836
2837 for( FOOTPRINT* fp : m_board->Footprints() )
2838 {
2839 wxXmlNode* componentNode = new wxXmlNode( wxXML_ELEMENT_NODE, "Component" );
2840 addAttribute( componentNode, "refDes", componentName( fp ) );
2841 wxXmlNode* pkg = addPackage( componentNode, fp );
2842
2843 if( pkg )
2844 packageNodes.push_back( pkg );
2845
2846 wxString name;
2847
2848 PCB_FIELD* field = nullptr;
2849
2850 if( !generate_unique )
2851 field = fp->GetField( m_OEMRef );
2852
2853 if( field && !field->GetText().empty() )
2854 {
2855 name = field->GetShownText( false );
2856 }
2857 else
2858 {
2859 name = wxString::Format( "%s_%s_%s", fp->GetFPID().GetFullLibraryName(),
2860 fp->GetFPID().GetLibItemName().wx_str(),
2861 fp->GetValue() );
2862 }
2863
2864 if( !m_OEMRef_dict.emplace( fp, name ).second )
2865 Report( _( "Duplicate footprint pointers encountered; IPC-2581 output may be incorrect." ),
2867
2868 addAttribute( componentNode, "part", genString( name, "REF" ) );
2869 addAttribute( componentNode, "layerRef", m_layer_name_map[fp->GetLayer()] );
2870
2871 if( fp->GetAttributes() & FP_THROUGH_HOLE )
2872 addAttribute( componentNode, "mountType", "THMT" );
2873 else if( fp->GetAttributes() & FP_SMD )
2874 addAttribute( componentNode, "mountType", "SMT" );
2875 else
2876 addAttribute( componentNode, "mountType", "OTHER" );
2877
2878 if( fp->GetOrientation() != ANGLE_0 || fp->IsFlipped() )
2879 {
2880 wxXmlNode* xformNode = appendNode( componentNode, "Xform" );
2881
2882 EDA_ANGLE fp_angle = fp->GetOrientation().Normalize();
2883
2884 if( fp->IsFlipped() )
2885 fp_angle = fp_angle.Invert().Normalize();
2886
2887 if( fp_angle != ANGLE_0 )
2888 addAttribute( xformNode, "rotation", floatVal( fp_angle.AsDegrees(), 2 ) );
2889
2890 if( fp->IsFlipped() )
2891 addAttribute( xformNode, "mirror", "true" );
2892 }
2893
2894 addLocationNode( componentNode, fp->GetPosition().x, fp->GetPosition().y );
2895
2896 componentNodes.push_back( componentNode );
2897 }
2898
2899 for( wxXmlNode* padstack : m_padstacks )
2900 {
2901 insertNode( aStepNode, padstack );
2902 m_last_padstack = padstack;
2903 }
2904
2905 for( wxXmlNode* pkg : packageNodes )
2906 aStepNode->AddChild( pkg );
2907
2908 for( wxXmlNode* cmp : componentNodes )
2909 aStepNode->AddChild( cmp );
2910}
2911
2912
2913void PCB_IO_IPC2581::generateLogicalNets( wxXmlNode* aStepNode )
2914{
2915 for( auto& [ net, pin_pair] : m_net_pin_dict )
2916 {
2917 wxXmlNode* netNode = appendNode( aStepNode, "LogicalNet" );
2918 addAttribute( netNode, "name",
2919 genString( m_board->GetNetInfo().GetNetItem( net )->GetNetname(), "NET" ) ) ;
2920
2921 for( auto& [cmp, pin] : pin_pair )
2922 {
2923 wxXmlNode* netPinNode = appendNode( netNode, "PinRef" );
2924 addAttribute( netPinNode, "componentRef", cmp );
2925 addAttribute( netPinNode, "pin", pin );
2926 }
2927 //TODO: Finish
2928 }
2929}
2930
2931//TODO: Add PhyNetGroup section
2932
2933void PCB_IO_IPC2581::generateLayerFeatures( wxXmlNode* aStepNode )
2934{
2935 LSEQ layers = m_board->GetEnabledLayers().Seq();
2936 const NETINFO_LIST& nets = m_board->GetNetInfo();
2937 std::vector<std::unique_ptr<FOOTPRINT>> footprints;
2938
2939 // To avoid the overhead of repeatedly cycling through the layers and nets,
2940 // we pre-sort the board items into a map of layer -> net -> items
2941 std::map<PCB_LAYER_ID, std::map<int, std::vector<BOARD_ITEM*>>> elements;
2942
2943 std::for_each( m_board->Tracks().begin(), m_board->Tracks().end(),
2944 [&layers, &elements]( PCB_TRACK* aTrack )
2945 {
2946 if( aTrack->Type() == PCB_VIA_T )
2947 {
2948 PCB_VIA* via = static_cast<PCB_VIA*>( aTrack );
2949
2950 for( PCB_LAYER_ID layer : layers )
2951 {
2952 if( via->FlashLayer( layer ) )
2953 elements[layer][via->GetNetCode()].push_back( via );
2954 }
2955 }
2956 else
2957 {
2958 elements[aTrack->GetLayer()][aTrack->GetNetCode()].push_back( aTrack );
2959 }
2960 } );
2961
2962 std::for_each( m_board->Zones().begin(), m_board->Zones().end(),
2963 [ &elements ]( ZONE* zone )
2964 {
2965 LSEQ zone_layers = zone->GetLayerSet().Seq();
2966
2967 for( PCB_LAYER_ID layer : zone_layers )
2968 elements[layer][zone->GetNetCode()].push_back( zone );
2969 } );
2970
2971 for( BOARD_ITEM* item : m_board->Drawings() )
2972 {
2973 if( BOARD_CONNECTED_ITEM* conn_it = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
2974 elements[conn_it->GetLayer()][conn_it->GetNetCode()].push_back( conn_it );
2975 else
2976 elements[item->GetLayer()][0].push_back( item );
2977 }
2978
2979 for( FOOTPRINT* fp : m_board->Footprints() )
2980 {
2981 for( PCB_FIELD* field : fp->GetFields() )
2982 elements[field->GetLayer()][0].push_back( field );
2983
2984 for( BOARD_ITEM* item : fp->GraphicalItems() )
2985 elements[item->GetLayer()][0].push_back( item );
2986
2987 for( PAD* pad : fp->Pads() )
2988 {
2989 LSEQ pad_layers = pad->GetLayerSet().Seq();
2990
2991 for( PCB_LAYER_ID layer : pad_layers )
2992 {
2993 if( pad->FlashLayer( layer ) )
2994 elements[layer][pad->GetNetCode()].push_back( pad );
2995 }
2996 }
2997 }
2998
2999 for( PCB_LAYER_ID layer : layers )
3000 {
3001 if( m_progressReporter )
3002 m_progressReporter->SetMaxProgress( nets.GetNetCount() * layers.size() );
3003
3004 wxXmlNode* layerNode = appendNode( aStepNode, "LayerFeature" );
3005 addAttribute( layerNode, "layerRef", m_layer_name_map[layer] );
3006
3007 auto process_net = [&] ( int net )
3008 {
3009 std::vector<BOARD_ITEM*>& vec = elements[layer][net];
3010
3011 if( vec.empty() )
3012 return;
3013
3014 std::stable_sort( vec.begin(), vec.end(),
3015 []( BOARD_ITEM* a, BOARD_ITEM* b )
3016 {
3017 if( a->GetParentFootprint() == b->GetParentFootprint() )
3018 return a->Type() < b->Type();
3019
3020 return a->GetParentFootprint() < b->GetParentFootprint();
3021 } );
3022
3023 generateLayerSetNet( layerNode, layer, vec );
3024 };
3025
3026 for( const NETINFO_ITEM* net : nets )
3027 {
3028 if( m_progressReporter )
3029 {
3030 m_progressReporter->Report( wxString::Format( _( "Exporting Layer %s, Net %s" ),
3031 m_board->GetLayerName( layer ),
3032 net->GetNetname() ) );
3033 m_progressReporter->AdvanceProgress();
3034 }
3035
3036 process_net( net->GetNetCode() );
3037 }
3038
3039 if( layerNode->GetChildren() == nullptr )
3040 {
3041 aStepNode->RemoveChild( layerNode );
3042 delete layerNode;
3043 }
3044 }
3045}
3046
3047
3048void PCB_IO_IPC2581::generateLayerSetDrill( wxXmlNode* aLayerNode )
3049{
3050 int hole_count = 1;
3051
3052 for( const auto& [layers, vec] : m_drill_layers )
3053 {
3054 wxXmlNode* layerNode = appendNode( aLayerNode, "LayerFeature" );
3055 layerNode->AddAttribute( "layerRef", genLayersString( layers.first, layers.second, "DRILL" ) );
3056
3057 for( BOARD_ITEM* item : vec )
3058 {
3059 if( item->Type() == PCB_VIA_T )
3060 {
3061 PCB_VIA* via = static_cast<PCB_VIA*>( item );
3062 auto it = m_padstack_dict.find( hash_fp_item( via, 0 ) );
3063
3064 if( it == m_padstack_dict.end() )
3065 {
3066 Report( _( "Via uses unsupported padstack; omitted from drill data." ),
3068 continue;
3069 }
3070
3071 wxXmlNode* padNode = appendNode( layerNode, "Set" );
3072 addAttribute( padNode, "geometry", it->second );
3073
3074 if( via->GetNetCode() > 0 )
3075 addAttribute( padNode, "net", genString( via->GetNetname(), "NET" ) );
3076
3077 wxXmlNode* holeNode = appendNode( padNode, "Hole" );
3078 addAttribute( holeNode, "name", wxString::Format( "H%d", hole_count++ ) );
3079 addAttribute( holeNode, "diameter", floatVal( m_scale * via->GetDrillValue() ) );
3080 addAttribute( holeNode, "platingStatus", "VIA" );
3081 addAttribute( holeNode, "plusTol", "0.0" );
3082 addAttribute( holeNode, "minusTol", "0.0" );
3083 addXY( holeNode, via->GetPosition() );
3084 addBackdrillSpecRefs( holeNode, it->second );
3085 }
3086 else if( item->Type() == PCB_PAD_T )
3087 {
3088 PAD* pad = static_cast<PAD*>( item );
3089 auto it = m_padstack_dict.find( hash_fp_item( pad, 0 ) );
3090
3091 if( it == m_padstack_dict.end() )
3092 {
3093 Report( _( "Pad uses unsupported padstack; hole was omitted from drill data." ),
3095 continue;
3096 }
3097
3098 wxXmlNode* padNode = appendNode( layerNode, "Set" );
3099 addAttribute( padNode, "geometry", it->second );
3100
3101 if( pad->GetNetCode() > 0 )
3102 addAttribute( padNode, "net", genString( pad->GetNetname(), "NET" ) );
3103
3104 wxXmlNode* holeNode = appendNode( padNode, "Hole" );
3105 addAttribute( holeNode, "name", wxString::Format( "H%d", hole_count++ ) );
3106 addAttribute( holeNode, "diameter", floatVal( m_scale * pad->GetDrillSizeX() ) );
3107 addAttribute( holeNode, "platingStatus",
3108 pad->GetAttribute() == PAD_ATTRIB::PTH ? "PLATED" : "NONPLATED" );
3109 addAttribute( holeNode, "plusTol", "0.0" );
3110 addAttribute( holeNode, "minusTol", "0.0" );
3111 addXY( holeNode, pad->GetPosition() );
3112 addBackdrillSpecRefs( holeNode, it->second );
3113 }
3114 }
3115 }
3116
3117 hole_count = 1;
3118
3119 for( const auto& [layers, vec] : m_slot_holes )
3120 {
3121 wxXmlNode* layerNode = appendNode( aLayerNode, "LayerFeature" );
3122 layerNode->AddAttribute( "layerRef", genLayersString( layers.first, layers.second, "SLOT" ) );
3123
3124 for( PAD* pad : vec )
3125 {
3126 wxXmlNode* padNode = appendNode( layerNode, "Set" );
3127
3128 if( pad->GetNetCode() > 0 )
3129 addAttribute( padNode, "net", genString( pad->GetNetname(), "NET" ) );
3130
3131 addSlotCavity( padNode, *pad, wxString::Format( "SLOT%d", hole_count++ ) );
3132 }
3133 }
3134}
3135
3136
3137void PCB_IO_IPC2581::generateLayerSetNet( wxXmlNode* aLayerNode, PCB_LAYER_ID aLayer,
3138 std::vector<BOARD_ITEM*>& aItems )
3139{
3140 auto it = aItems.begin();
3141 wxXmlNode* layerSetNode = appendNode( aLayerNode, "Set" );
3142 wxXmlNode* featureSetNode = appendNode( layerSetNode, "Features" );
3143 wxXmlNode* specialNode = appendNode( featureSetNode, "UserSpecial" );
3144
3145 bool has_via = false;
3146 bool has_pad = false;
3147
3148 wxXmlNode* padSetNode = nullptr;
3149
3150 wxXmlNode* viaSetNode = nullptr;
3151
3152 wxXmlNode* teardropLayerSetNode = nullptr;
3153 wxXmlNode* teardropFeatureSetNode = nullptr;
3154
3155 bool teardrop_warning = false;
3156
3157 if( BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( *it );
3158 IsCopperLayer( aLayer ) && item )
3159 {
3160 if( item->GetNetCode() > 0 )
3161 addAttribute( layerSetNode, "net", genString( item->GetNetname(), "NET" ) );
3162 }
3163
3164 auto add_track =
3165 [&]( PCB_TRACK* track )
3166 {
3167 if( track->Type() == PCB_TRACE_T )
3168 {
3169 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
3170 shape.SetStart( track->GetStart() );
3171 shape.SetEnd( track->GetEnd() );
3172 shape.SetWidth( track->GetWidth() );
3173 addShape( specialNode, shape );
3174 }
3175 else if( track->Type() == PCB_ARC_T )
3176 {
3177 PCB_ARC* arc = static_cast<PCB_ARC*>( track );
3178 PCB_SHAPE shape( nullptr, SHAPE_T::ARC );
3179 shape.SetArcGeometry( arc->GetStart(), arc->GetMid(), arc->GetEnd() );
3180 shape.SetWidth( arc->GetWidth() );
3181 addShape( specialNode, shape );
3182 }
3183 else
3184 {
3185 if( !viaSetNode )
3186 {
3187 if( !has_pad )
3188 {
3189 viaSetNode = layerSetNode;
3190 has_via = true;
3191 }
3192 else
3193 {
3194 viaSetNode = appendNode( layerSetNode, "Set" );
3195
3196 if( track->GetNetCode() > 0 )
3197 addAttribute( viaSetNode, "net", genString( track->GetNetname(), "NET" ) );
3198 }
3199
3200 addAttribute( viaSetNode, "padUsage", "VIA" );
3201 }
3202
3203 addVia( viaSetNode, static_cast<PCB_VIA*>( track ), aLayer );
3204 }
3205 };
3206
3207 auto add_zone =
3208 [&]( ZONE* zone )
3209 {
3210 wxXmlNode* zoneFeatureNode = specialNode;
3211
3212 if( zone->IsTeardropArea() )
3213 {
3214 if( m_version > 'B' )
3215 {
3216 if( !teardropFeatureSetNode )
3217 {
3218 teardropLayerSetNode = appendNode( aLayerNode, "Set" );
3219 addAttribute( teardropLayerSetNode, "geometryUsage", "TEARDROP" );
3220
3221 if( zone->GetNetCode() > 0 )
3222 {
3223 addAttribute( teardropLayerSetNode, "net",
3224 genString( zone->GetNetname(), "NET" ) );
3225 }
3226
3227 wxXmlNode* new_teardrops = appendNode( teardropLayerSetNode, "Features" );
3228 addLocationNode( new_teardrops, 0.0, 0.0 );
3229 teardropFeatureSetNode = appendNode( new_teardrops, "UserSpecial" );
3230 }
3231
3232 zoneFeatureNode = teardropFeatureSetNode;
3233 }
3234 else if( !teardrop_warning )
3235 {
3236 Report( _( "Teardrops are not supported in IPC-2581 revision B; they were exported as zones." ),
3238 teardrop_warning = true;
3239 }
3240 }
3241 else
3242 {
3243 if( FOOTPRINT* fp = zone->GetParentFootprint() )
3244 {
3245 wxXmlNode* tempSetNode = appendNode( aLayerNode, "Set" );
3246 wxString refDes = componentName( zone->GetParentFootprint() );
3247 addAttribute( tempSetNode, "componentRef", refDes );
3248 wxXmlNode* newFeatures = appendNode( tempSetNode, "Features" );
3249 addLocationNode( newFeatures, 0.0, 0.0 );
3250 zoneFeatureNode = appendNode( newFeatures, "UserSpecial" );
3251 }
3252 }
3253
3254 SHAPE_POLY_SET& zone_shape = *zone->GetFilledPolysList( aLayer );
3255
3256 for( int ii = 0; ii < zone_shape.OutlineCount(); ++ii )
3257 addContourNode( zoneFeatureNode, zone_shape, ii );
3258 };
3259
3260 auto add_shape =
3261 [&] ( PCB_SHAPE* shape )
3262 {
3263 FOOTPRINT* fp = shape->GetParentFootprint();
3264
3265 if( fp )
3266 {
3267 wxXmlNode* tempSetNode = appendNode( aLayerNode, "Set" );
3268
3269 if( m_version > 'B' )
3270 addAttribute( tempSetNode, "geometryUsage", "GRAPHIC" );
3271
3272 bool link_to_component = true;
3273
3274 if( m_version == 'B' && isOppositeSideSilk( fp, shape->GetLayer() ) )
3275 link_to_component = false;
3276
3277 if( link_to_component )
3278 addAttribute( tempSetNode, "componentRef", componentName( fp ) );
3279
3280 wxXmlNode* tempFeature = appendNode( tempSetNode, "Features" );
3281 addLocationNode( tempFeature, *shape );
3282
3283 EDA_ANGLE fp_angle = fp->GetOrientation().Normalize();
3284
3285 if( fp_angle != ANGLE_0 )
3286 {
3287 wxXmlNode* xformNode = appendNode( tempFeature, "Xform" );
3288 addAttribute( xformNode, "rotation", floatVal( fp_angle.AsDegrees(), 2 ) );
3289 }
3290
3291 addShape( tempFeature, *shape );
3292 }
3293 else if( shape->GetShape() == SHAPE_T::CIRCLE
3294 || shape->GetShape() == SHAPE_T::RECTANGLE
3295 || shape->GetShape() == SHAPE_T::POLY )
3296 {
3297 wxXmlNode* tempSetNode = appendNode( aLayerNode, "Set" );
3298
3299 if( shape->GetNetCode() > 0 )
3300 addAttribute( tempSetNode, "net", genString( shape->GetNetname(), "NET" ) );
3301
3302 wxXmlNode* tempFeature = appendNode( tempSetNode, "Features" );
3303 addLocationNode( tempFeature, *shape );
3304 addShape( tempFeature, *shape );
3305 }
3306 else
3307 {
3308 addShape( specialNode, *shape );
3309 }
3310 };
3311
3312 auto add_text =
3313 [&] ( BOARD_ITEM* text )
3314 {
3315 EDA_TEXT* text_item;
3316 FOOTPRINT* fp = text->GetParentFootprint();
3317
3318 if( PCB_TEXT* tmp_text = dynamic_cast<PCB_TEXT*>( text ) )
3319 text_item = static_cast<EDA_TEXT*>( tmp_text );
3320 else if( PCB_TEXTBOX* tmp_text = dynamic_cast<PCB_TEXTBOX*>( text ) )
3321 text_item = static_cast<EDA_TEXT*>( tmp_text );
3322
3323 if( !text_item->IsVisible() || text_item->GetShownText( false ).empty() )
3324 return;
3325
3326 wxXmlNode* tempSetNode = appendNode( aLayerNode, "Set" );
3327
3328 if( m_version > 'B' )
3329 addAttribute( tempSetNode, "geometryUsage", "TEXT" );
3330
3331 bool link_to_component = fp != nullptr;
3332
3333 if( m_version == 'B' && fp && isOppositeSideSilk( fp, text->GetLayer() ) )
3334 link_to_component = false;
3335
3336 if( link_to_component )
3337 addAttribute( tempSetNode, "componentRef", componentName( fp ) );
3338
3339 wxXmlNode* nonStandardAttributeNode = appendNode( tempSetNode, "NonstandardAttribute" );
3340 addAttribute( nonStandardAttributeNode, "name", "TEXT" );
3341 addAttribute( nonStandardAttributeNode, "value", text_item->GetShownText( false ) );
3342 addAttribute( nonStandardAttributeNode, "type", "STRING" );
3343
3344 wxXmlNode* tempFeature = appendNode( tempSetNode, "Features" );
3345 addLocationNode( tempFeature, 0.0, 0.0 );
3346
3347 if( text->Type() == PCB_TEXT_T && static_cast<PCB_TEXT*>( text )->IsKnockout() )
3348 addKnockoutText( tempFeature, static_cast<PCB_TEXT*>( text ) );
3349 else
3350 addText( tempFeature, text_item, text->GetFontMetrics() );
3351
3352 if( text->Type() == PCB_TEXTBOX_T )
3353 {
3354 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( text );
3355
3356 if( textbox->IsBorderEnabled() )
3357 addShape( tempFeature, *static_cast<PCB_SHAPE*>( textbox ) );
3358 }
3359 };
3360
3361 auto add_pad =
3362 [&]( PAD* pad )
3363 {
3364 if( !padSetNode )
3365 {
3366 if( !has_via )
3367 {
3368 padSetNode = layerSetNode;
3369 has_pad = true;
3370 }
3371 else
3372 {
3373 padSetNode = appendNode( aLayerNode, "Set" );
3374
3375 if( pad->GetNetCode() > 0 )
3376 addAttribute( padSetNode, "net", genString( pad->GetNetname(), "NET" ) );
3377 }
3378 }
3379
3380 FOOTPRINT* fp = pad->GetParentFootprint();
3381
3382 if( fp && fp->IsFlipped() )
3383 addPad( padSetNode, pad, FlipLayer( aLayer ) );
3384 else
3385 addPad( padSetNode, pad, aLayer );
3386 };
3387
3388 for( BOARD_ITEM* item : aItems )
3389 {
3390 switch( item->Type() )
3391 {
3392 case PCB_TRACE_T:
3393 case PCB_ARC_T:
3394 case PCB_VIA_T:
3395 add_track( static_cast<PCB_TRACK*>( item ) );
3396 break;
3397
3398 case PCB_ZONE_T:
3399 add_zone( static_cast<ZONE*>( item ) );
3400 break;
3401
3402 case PCB_PAD_T:
3403 add_pad( static_cast<PAD*>( item ) );
3404 break;
3405
3406 case PCB_SHAPE_T:
3407 add_shape( static_cast<PCB_SHAPE*>( item ) );
3408 break;
3409
3410 case PCB_TEXT_T:
3411 case PCB_TEXTBOX_T:
3412 case PCB_FIELD_T:
3413 add_text( item );
3414 break;
3415
3416 case PCB_DIMENSION_T:
3417 case PCB_TARGET_T:
3418 case PCB_DIM_ALIGNED_T:
3419 case PCB_DIM_LEADER_T:
3420 case PCB_DIM_CENTER_T:
3421 case PCB_DIM_RADIAL_T:
3423 //TODO: Add support for dimensions
3424 break;
3425
3426 default:
3427 wxLogTrace( traceIpc2581, wxS( "Unhandled type %s" ),
3428 ENUM_MAP<KICAD_T>::Instance().ToString( item->Type() ) );
3429 }
3430 }
3431
3432 if( specialNode->GetChildren() == nullptr )
3433 {
3434 featureSetNode->RemoveChild( specialNode );
3435 delete specialNode;
3436 }
3437
3438 if( featureSetNode->GetChildren() == nullptr )
3439 {
3440 layerSetNode->RemoveChild( featureSetNode );
3441 delete featureSetNode;
3442 }
3443
3444 if( layerSetNode->GetChildren() == nullptr )
3445 {
3446 aLayerNode->RemoveChild( layerSetNode );
3447 delete layerSetNode;
3448 }
3449}
3450
3452{
3453 int hole_count = 1;
3454
3455 for( const auto& [layers, vec] : m_auxilliary_Layers )
3456 {
3457 hole_count = 1;
3458 bool add_node = true;
3459
3460 wxString name;
3461 bool hole = false;
3462
3463 // clang-format off: suggestion is inconsitent
3464 switch( std::get<0>(layers) )
3465 {
3467 name = "COVERING";
3468 break;
3470 name = "PLUGGING";
3471 hole = true;
3472 break;
3474 name = "TENTING";
3475 break;
3477 name = "FILLING";
3478 hole = true;
3479 break;
3481 name = "CAPPING";
3482 hole = true;
3483 break;
3484 default:
3485 add_node = false;
3486 break;
3487 }
3488 // clang-format on: suggestion is inconsitent
3489
3490 if( !add_node )
3491 continue;
3492
3493 wxXmlNode* layerNode = appendNode( aStepNode, "LayerFeature" );
3494 if( std::get<2>( layers ) == UNDEFINED_LAYER )
3495 layerNode->AddAttribute( "layerRef", genLayerString( std::get<1>( layers ), TO_UTF8( name ) ) );
3496 else
3497 layerNode->AddAttribute( "layerRef", genLayersString( std::get<1>( layers ),
3498 std::get<2>( layers ), TO_UTF8( name ) ) );
3499
3500 for( BOARD_ITEM* item : vec )
3501 {
3502 if( item->Type() == PCB_VIA_T )
3503 {
3504 PCB_VIA* via = static_cast<PCB_VIA*>( item );
3505
3506 PCB_SHAPE shape( nullptr, SHAPE_T::CIRCLE );
3507
3508 if( hole )
3509 shape.SetEnd( { KiROUND( via->GetDrillValue() / 2.0 ), 0 } );
3510 else
3511 shape.SetEnd( { KiROUND( via->GetWidth( std::get<1>( layers ) ) / 2.0 ), 0 } );
3512
3513 wxXmlNode* padNode = appendNode( layerNode, "Pad" );
3514 addPadStack( padNode, via );
3515
3516 addLocationNode( padNode, 0.0, 0.0 );
3517 addShape( padNode, shape );
3518 }
3519 }
3520 }
3521}
3522
3523
3525{
3526 if( m_progressReporter )
3527 m_progressReporter->AdvancePhase( _( "Generating BOM section" ) );
3528
3529 wxXmlNode* avl = appendNode( m_xml_root, "Avl" );
3530 addAttribute( avl, "name", "Primary_Vendor_List" );
3531
3532 wxXmlNode* header = appendNode( avl, "AvlHeader" );
3533 addAttribute( header, "title", "BOM" );
3534 addAttribute( header, "source", "KiCad" );
3535 addAttribute( header, "author", "OWNER" );
3536 addAttribute( header, "datetime", wxDateTime::Now().FormatISOCombined() );
3537 addAttribute( header, "version", "1" );
3538
3539 std::set<wxString> unique_parts;
3540 std::map<wxString,wxString> unique_vendors;
3541
3542 for( auto& [fp, name] : m_OEMRef_dict )
3543 {
3544 auto [ it, success ] = unique_parts.insert( name );
3545
3546 if( !success )
3547 continue;
3548
3549 wxXmlNode* part = appendNode( avl, "AvlItem" );
3550 addAttribute( part, "OEMDesignNumber", genString( name, "REF" ) );
3551
3552 PCB_FIELD* nums[2] = { fp->GetField( m_mpn ), fp->GetField( m_distpn ) };
3553 PCB_FIELD* company[2] = { fp->GetField( m_mfg ), nullptr };
3554 wxString company_name[2] = { m_mfg, m_dist };
3555
3556 for ( int ii = 0; ii < 2; ++ii )
3557 {
3558 if( nums[ii] )
3559 {
3560 wxString mpn_name = nums[ii]->GetShownText( false );
3561
3562 if( mpn_name.empty() )
3563 continue;
3564
3565 wxXmlNode* vmpn = appendNode( part, "AvlVmpn" );
3566 addAttribute( vmpn, "qualified", "false" );
3567 addAttribute( vmpn, "chosen", "false" );
3568
3569 wxXmlNode* mpn = appendNode( vmpn, "AvlMpn" );
3570 addAttribute( mpn, "name", mpn_name );
3571
3572 wxXmlNode* vendor = appendNode( vmpn, "AvlVendor" );
3573
3574 wxString name = wxT( "UNKNOWN" );
3575
3576 // If the field resolves, then use that field content unless it is empty
3577 if( !ii && company[ii] )
3578 {
3579 wxString tmp = company[ii]->GetShownText( false );
3580
3581 if( !tmp.empty() )
3582 name = tmp;
3583 }
3584 // If it doesn't resolve but there is content from the dialog, use the static content
3585 else if( !ii && !company_name[ii].empty() )
3586 {
3587 name = company_name[ii];
3588 }
3589 else if( ii && !m_dist.empty() )
3590 {
3591 name = m_dist;
3592 }
3593
3594 auto [vendor_id, inserted] = unique_vendors.emplace(
3595 name,
3596 wxString::Format( "VENDOR_%zu", unique_vendors.size() ) );
3597
3598 addAttribute( vendor, "enterpriseRef", vendor_id->second );
3599
3600 if( inserted )
3601 {
3602 wxXmlNode* new_vendor = new wxXmlNode( wxXML_ELEMENT_NODE, "Enterprise" );
3603 addAttribute( new_vendor, "id", vendor_id->second );
3604 addAttribute( new_vendor, "name", name );
3605 addAttribute( new_vendor, "code", "NONE" );
3606 insertNodeAfter( m_enterpriseNode, new_vendor );
3607 m_enterpriseNode = new_vendor;
3608 }
3609 }
3610 }
3611 }
3612
3613 return avl;
3614}
3615
3616
3617void PCB_IO_IPC2581::SaveBoard( const wxString& aFileName, BOARD* aBoard,
3618 const std::map<std::string, UTF8>* aProperties )
3619{
3620 m_board = aBoard;
3622 m_backdrill_spec_nodes.clear();
3623 m_backdrill_spec_used.clear();
3625 m_cad_header_node = nullptr;
3626 m_layer_name_map.clear();
3627 m_units_str = "MILLIMETER";
3628 m_scale = 1.0 / PCB_IU_PER_MM;
3629 m_sigfig = 6;
3630
3631 if( auto it = aProperties->find( "units" ); it != aProperties->end() )
3632 {
3633 if( it->second == "inch" )
3634 {
3635 m_units_str = "INCH";
3636 m_scale = ( 1.0 / 25.4 ) / PCB_IU_PER_MM;
3637 }
3638 }
3639
3640 if( auto it = aProperties->find( "sigfig" ); it != aProperties->end() )
3641 m_sigfig = std::stoi( it->second );
3642
3643 if( auto it = aProperties->find( "version" ); it != aProperties->end() )
3644 m_version = it->second.c_str()[0];
3645
3646 if( auto it = aProperties->find( "OEMRef" ); it != aProperties->end() )
3647 m_OEMRef = it->second.wx_str();
3648
3649 if( auto it = aProperties->find( "mpn" ); it != aProperties->end() )
3650 m_mpn = it->second.wx_str();
3651
3652 if( auto it = aProperties->find( "mfg" ); it != aProperties->end() )
3653 m_mfg = it->second.wx_str();
3654
3655 if( auto it = aProperties->find( "dist" ); it != aProperties->end() )
3656 m_dist = it->second.wx_str();
3657
3658 if( auto it = aProperties->find( "distpn" ); it != aProperties->end() )
3659 m_distpn = it->second.wx_str();
3660
3661 if( m_version == 'B' )
3662 {
3663 for( char c = 'a'; c <= 'z'; ++c )
3664 m_acceptable_chars.insert( c );
3665
3666 for( char c = 'A'; c <= 'Z'; ++c )
3667 m_acceptable_chars.insert( c );
3668
3669 for( char c = '0'; c <= '9'; ++c )
3670 m_acceptable_chars.insert( c );
3671
3672 // Add special characters
3673 std::string specialChars = "_\\-.+><";
3674
3675 for( char c : specialChars )
3676 m_acceptable_chars.insert( c );
3677 }
3678
3679 m_xml_doc = new wxXmlDocument();
3681
3683
3684 if( m_progressReporter )
3685 {
3686 m_progressReporter->SetNumPhases( 7 );
3687 m_progressReporter->BeginPhase( 1 );
3688 m_progressReporter->Report( _( "Generating logistic section" ) );
3689 }
3690
3693
3694 wxXmlNode* ecad_node = generateEcadSection();
3695 generateBOMSection( ecad_node );
3697
3698 if( m_progressReporter )
3699 {
3700 m_progressReporter->AdvancePhase( _( "Saving file" ) );
3701 }
3702
3703 wxFileOutputStreamWithProgress out_stream( aFileName );
3704 double written_bytes = 0.0;
3705 double last_yield = 0.0;
3706
3707 // This is a rough estimation of the size of the spaces in the file
3708 // We just need to total to be slightly larger than the value of the
3709 // progress bar, so accurately counting spaces is not terribly important
3711
3712 auto update_progress = [&]( size_t aBytes )
3713 {
3714 written_bytes += aBytes;
3715 double percent = written_bytes / static_cast<double>( m_total_bytes );
3716
3717 if( m_progressReporter )
3718 {
3719 // Only update every percent
3720 if( last_yield + 0.01 < percent )
3721 {
3722 last_yield = percent;
3723 m_progressReporter->SetCurrentProgress( percent );
3724 }
3725 }
3726 };
3727
3728 out_stream.SetProgressCallback( update_progress );
3729
3730 if( !m_xml_doc->Save( out_stream ) )
3731 {
3732 Report( _( "Failed to save IPC-2581 data to buffer." ), RPT_SEVERITY_ERROR );
3733 return;
3734 }
3735
3736 size_t size = out_stream.GetSize();
3737}
const char * name
@ ERROR_INSIDE
constexpr int ARC_HIGH_DEF
Definition base_units.h:129
constexpr double PCB_IU_PER_MM
Pcbnew IU is 1 nanometer.
Definition base_units.h:70
bool IsPrmSpecified(const wxString &aPrmValue)
@ BS_ITEM_TYPE_COPPER
@ BS_ITEM_TYPE_SILKSCREEN
@ BS_ITEM_TYPE_DIELECTRIC
@ BS_ITEM_TYPE_SOLDERMASK
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
wxString GetMajorMinorPatchVersion()
Get the major, minor and patch version in a string major.minor.patch This is extracted by CMake from ...
Bezier curves to polygon converter.
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMaxError=10)
Convert a Bezier curve to a polygon.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
Container for design settings for a BOARD object.
BOARD_STACKUP & GetStackupDescriptor()
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
virtual bool IsKnockout() const
Definition board_item.h:323
FOOTPRINT * GetParentFootprint() const
VECTOR2I GetFPRelativePosition() const
Manage one layer needed to make a physical board.
wxString GetTypeName() const
int GetSublayersCount() const
double GetEpsilonR(int aDielectricSubLayer=0) const
wxString GetColor(int aDielectricSubLayer=0) const
wxString GetLayerName() const
PCB_LAYER_ID GetBrdLayerId() const
int GetThickness(int aDielectricSubLayer=0) const
BOARD_STACKUP_ITEM_TYPE GetType() const
wxString GetMaterial(int aDielectricSubLayer=0) const
int GetDielectricLayerId() const
double GetLossTangent(int aDielectricSubLayer=0) const
Manage layers needed to make a physical board.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
int GetCount() const
bool SynchronizeWithBoard(BOARD_DESIGN_SETTINGS *aSettings)
Synchronize the BOARD_STACKUP_ITEM* list with the board.
int BuildBoardThicknessFromStackup() const
int GetLayerDistance(PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer) const
Calculate the distance (height) between the two given copper layers.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:658
constexpr coord_type GetLeft() const
Definition box2.h:228
constexpr coord_type GetRight() const
Definition box2.h:217
constexpr coord_type GetTop() const
Definition box2.h:229
constexpr coord_type GetBottom() const
Definition box2.h:222
EDA_ANGLE Normalize()
Definition eda_angle.h:229
double AsDegrees() const
Definition eda_angle.h:116
EDA_ANGLE Invert() const
Definition eda_angle.h:173
const VECTOR2I & GetBezierC2() const
Definition eda_shape.h:258
FILL_T GetFillMode() const
Definition eda_shape.h:142
int GetRectangleWidth() const
SHAPE_POLY_SET & GetPolyShape()
Definition eda_shape.h:336
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:168
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:215
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:177
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:173
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:219
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
const VECTOR2I & GetBezierC1() const
Definition eda_shape.h:255
int GetRectangleHeight() const
bool IsClockwiseArc() const
void SetWidth(int aWidth)
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:80
const VECTOR2I & GetTextPos() const
Definition eda_text.h:273
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:98
virtual bool IsVisible() const
Definition eda_text.h:187
virtual EDA_ANGLE GetDrawRotation() const
Definition eda_text.h:377
virtual KIFONT::FONT * GetDrawFont(const RENDER_SETTINGS *aSettings) const
Definition eda_text.cpp:656
const TEXT_ATTRIBUTES & GetAttributes() const
Definition eda_text.h:231
int GetEffectiveTextPenWidth(int aDefaultPenWidth=0) const
The EffectiveTextPenWidth uses the text thickness if > 1 or aDefaultPenWidth.
Definition eda_text.cpp:474
virtual wxString GetShownText(bool aAllowExtraText, int aDepth=0) const
Return the string actually shown after processing of the base text.
Definition eda_text.h:109
static ENUM_MAP< T > & Instance()
Definition property.h:721
EDA_ANGLE GetOrientation() const
Definition footprint.h:328
unsigned GetPadCount(INCLUDE_NPTH_T aIncludeNPTH=INCLUDE_NPTH_T(INCLUDE_NPTH)) const
Return the number of pads.
EDA_ITEM * Clone() const override
Invoke a function on all children.
std::deque< PAD * > & Pads()
Definition footprint.h:304
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:337
bool IsFlipped() const
Definition footprint.h:514
const wxString & GetReference() const
Definition footprint.h:741
wxString m_name
Name of the IO loader.
Definition io_base.h:234
PROGRESS_REPORTER * m_progressReporter
Progress reporter to track the progress of the operation, may be nullptr.
Definition io_base.h:240
virtual void Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Definition io_base.cpp:124
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:131
void Draw(KIGFX::GAL *aGal, const wxString &aText, const VECTOR2I &aPosition, const VECTOR2I &aCursor, const TEXT_ATTRIBUTES &aAttributes, const METRICS &aFontMetrics, std::optional< VECTOR2I > aMousePos=std::nullopt, wxString *aActiveUrl=nullptr) const
Draw a string.
Definition font.cpp:250
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
double r
Red component.
Definition color4d.h:393
double g
Green component.
Definition color4d.h:394
double b
Blue component.
Definition color4d.h:395
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:296
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
Handle the data for a net.
Definition netinfo.h:54
Container for NETINFO_ITEM elements, which are the nets.
Definition netinfo.h:212
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
Definition padstack.h:157
std::optional< bool > IsFilled() const
std::optional< bool > IsTented(PCB_LAYER_ID aSide) const
Checks if this padstack is tented (covered in soldermask) on the given side.
POST_MACHINING_PROPS & FrontPostMachining()
Definition padstack.h:350
std::optional< bool > IsPlugged(PCB_LAYER_ID aSide) const
DRILL_PROPS & Drill()
Definition padstack.h:341
std::optional< bool > IsCapped() const
std::optional< bool > IsCovered(PCB_LAYER_ID aSide) const
DRILL_PROPS & SecondaryDrill()
Definition padstack.h:344
POST_MACHINING_PROPS & BackPostMachining()
Definition padstack.h:353
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
Definition pad.h:55
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition pad.h:560
int GetSizeX() const
Definition pad.h:285
void MergePrimitivesAsPolygon(PCB_LAYER_ID aLayer, SHAPE_POLY_SET *aMergedPolygon, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Merge all basic shapes to a SHAPE_POLY_SET.
Definition pad.cpp:3146
int GetRoundRectCornerRadius(PCB_LAYER_ID aLayer) const
Definition pad.cpp:859
bool FlashLayer(int aLayer, bool aOnlyCheckIfPermitted=false) const
Check to see whether the pad should be flashed on the specific layer.
Definition pad.cpp:408
int GetDrillSizeY() const
Definition pad.h:321
const VECTOR2I & GetDrillSize() const
Definition pad.h:317
PAD_ATTRIB GetAttribute() const
Definition pad.h:563
const wxString & GetNumber() const
Definition pad.h:137
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition pad.h:304
VECTOR2I GetPosition() const override
Definition pad.h:209
int GetDrillSizeX() const
Definition pad.h:319
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:196
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1610
const PADSTACK & Padstack() const
Definition pad.h:333
const VECTOR2I & GetOffset(PCB_LAYER_ID aLayer) const
Definition pad.h:329
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.h:420
PAD_DRILL_SHAPE GetDrillShape() const
Definition pad.h:437
int GetChamferPositions(PCB_LAYER_ID aLayer) const
Definition pad.h:834
double GetChamferRectRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:817
VECTOR2I GetSolderPasteMargin(PCB_LAYER_ID aLayer) const
Usually < 0 (mask shape smaller than pad)because the margin can be dependent on the pad size,...
Definition pad.cpp:1665
bool HasDrilledHole() const override
Definition pad.h:112
bool HasHole() const override
Definition pad.h:107
bool TransformHoleToPolygon(SHAPE_POLY_SET &aBuffer, int aClearance, int aError, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Build the corner list of the polygonal drill shape in the board coordinate system.
Definition pad.cpp:2462
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition pad.h:264
const VECTOR2I & GetMid() const
Definition pcb_track.h:347
wxString GetShownText(bool aAllowExtraText, int aDepth=0) const override
Return the string actually shown after processing of the base text.
void addText(wxXmlNode *aContentNode, EDA_TEXT *aShape, const KIFONT::METRICS &aFontMetrics)
wxString floatVal(double aVal, int aSigFig=-1) const
void generateLayerSetDrill(wxXmlNode *aStepNode)
wxString genLayerString(PCB_LAYER_ID aLayer, const char *aPrefix) const
wxXmlNode * generateContentSection()
Creates the Content section of the XML file.
wxXmlNode * appendNode(wxXmlNode *aParent, const wxString &aName)
void addBackdrillSpecRefs(wxXmlNode *aHoleNode, const wxString &aPadstackName)
void generateDrillLayers(wxXmlNode *aCadLayerNode)
wxString sanitizeId(const wxString &aStr) const
wxXmlNode * addPackage(wxXmlNode *aStepNode, FOOTPRINT *aFootprint)
void generateComponents(wxXmlNode *aStepNode)
bool addContourNode(wxXmlNode *aParentNode, const SHAPE_POLY_SET &aPolySet, int aOutline=0, FILL_T aFillType=FILL_T::FILLED_SHAPE, int aWidth=0, LINE_STYLE aDashType=LINE_STYLE::SOLID)
wxString componentName(FOOTPRINT *aFootprint)
bool addOutlineNode(wxXmlNode *aParentNode, const SHAPE_POLY_SET &aPolySet, int aWidth=0, LINE_STYLE aDashType=LINE_STYLE::SOLID)
void generateStackup(wxXmlNode *aCadLayerNode)
std::map< std::tuple< auxLayerType, PCB_LAYER_ID, PCB_LAYER_ID >, std::vector< BOARD_ITEM * > > m_auxilliary_Layers
bool addPolygonNode(wxXmlNode *aParentNode, const SHAPE_LINE_CHAIN &aPolygon, FILL_T aFillType=FILL_T::FILLED_SHAPE, int aWidth=0, LINE_STYLE aDashType=LINE_STYLE::SOLID)
void generateLayerSetNet(wxXmlNode *aLayerNode, PCB_LAYER_ID aLayer, std::vector< BOARD_ITEM * > &aItems)
bool isValidLayerFor2581(PCB_LAYER_ID aLayer)
void insertNodeAfter(wxXmlNode *aPrev, wxXmlNode *aNode)
void generateCadLayers(wxXmlNode *aCadLayerNode)
std::vector< wxXmlNode * > m_padstacks
wxString pinName(const PAD *aPad) const
void generateCadSpecs(wxXmlNode *aCadLayerNode)
void addVia(wxXmlNode *aContentNode, const PCB_VIA *aVia, PCB_LAYER_ID aLayer)
void addSlotCavity(wxXmlNode *aContentNode, const PAD &aPad, const wxString &aName)
wxXmlNode * m_last_padstack
size_t lineHash(int aWidth, LINE_STYLE aDashType)
std::map< size_t, wxString > m_std_shape_dict
void addAttribute(wxXmlNode *aNode, const wxString &aName, const wxString &aValue)
wxXmlNode * m_shape_user_node
wxXmlNode * generateAvlSection()
Creates the Approved Vendor List section.
wxXmlNode * generateHistorySection()
Creates the history section.
void addXY(wxXmlNode *aNode, const VECTOR2I &aVec, const char *aXName=nullptr, const char *aYName=nullptr)
wxXmlNode * m_shape_std_node
void addPadStack(wxXmlNode *aContentNode, const PAD *aPad)
std::map< FOOTPRINT *, wxString > m_OEMRef_dict
void clearLoadedFootprints()
Frees the memory allocated for the loaded footprints in m_loaded_footprints.
std::map< wxString, wxXmlNode * > m_backdrill_spec_nodes
std::map< std::pair< PCB_LAYER_ID, PCB_LAYER_ID >, std::vector< PAD * > > m_slot_holes
std::map< size_t, wxString > m_footprint_dict
wxXmlNode * generateLogisticSection()
Creates the logistical data header.
std::set< wxString > m_element_names
std::map< size_t, wxString > m_line_dict
void addLineDesc(wxXmlNode *aNode, int aWidth, LINE_STYLE aDashType, bool aForce=false)
void addKnockoutText(wxXmlNode *aContentNode, PCB_TEXT *aText)
std::map< size_t, wxString > m_padstack_dict
std::map< std::pair< PCB_LAYER_ID, PCB_LAYER_ID >, std::vector< BOARD_ITEM * > > m_drill_layers
std::map< wxString, FOOTPRINT * > m_footprint_refdes_dict
void generateProfile(wxXmlNode *aStepNode)
void generateLayerFeatures(wxXmlNode *aStepNode)
void SaveBoard(const wxString &aFileName, BOARD *aBoard, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aBoard to a storage file in a format that this PCB_IO implementation knows about or it can be u...
std::map< wxString, std::pair< wxString, wxString > > m_padstack_backdrill_specs
wxXmlNode * generateBOMSection(wxXmlNode *aEcadNode)
Creates the BOM section.
wxXmlNode * generateContentStackup(wxXmlNode *aContentNode)
void addShape(wxXmlNode *aContentNode, const PCB_SHAPE &aShape)
void generateAuxilliaryLayers(wxXmlNode *aCadLayerNode)
wxXmlNode * m_cad_header_node
void addCadHeader(wxXmlNode *aEcadNode)
void generateLayerSetAuxilliary(wxXmlNode *aStepNode)
std::vector< FOOTPRINT * > GetImportedCachedLibraryFootprints() override
Return a container with the cached library footprints generated in the last call to Load.
const std::map< std::string, UTF8 > * m_props
wxString genLayersString(PCB_LAYER_ID aTop, PCB_LAYER_ID aBottom, const char *aPrefix) const
void generateLogicalNets(wxXmlNode *aStepNode)
void addFillDesc(wxXmlNode *aNode, FILL_T aFillType, bool aForce=false)
std::map< size_t, wxString > m_user_shape_dict
size_t shapeHash(const PCB_SHAPE &aShape)
~PCB_IO_IPC2581() override
wxString genString(const wxString &aStr, const char *aPrefix=nullptr) const
wxXmlNode * m_xml_root
std::vector< FOOTPRINT * > m_loaded_footprints
bool addPolygonCutouts(wxXmlNode *aParentNode, const SHAPE_POLY_SET::POLYGON &aPolygon)
void pruneUnusedBackdrillSpecs()
std::set< wxUniChar > m_acceptable_chars
void addLayerAttributes(wxXmlNode *aNode, PCB_LAYER_ID aLayer)
wxXmlNode * generateXmlHeader()
Creates the XML header for IPC-2581.
void ensureBackdrillSpecs(const wxString &aPadstackName, const PADSTACK &aPadstack)
wxXmlNode * generateEcadSection()
Creates the ECAD section.
wxXmlDocument * m_xml_doc
wxXmlNode * insertNode(wxXmlNode *aParent, const wxString &aName)
void addPad(wxXmlNode *aContentNode, const PAD *aPad, PCB_LAYER_ID aLayer)
void generateStepSection(wxXmlNode *aCadNode)
std::map< PCB_LAYER_ID, wxString > m_layer_name_map
std::map< wxString, wxString > m_generated_names
std::set< wxString > m_backdrill_spec_used
void addLocationNode(wxXmlNode *aContentNode, double aX, double aY)
wxXmlNode * m_line_node
std::map< int, std::vector< std::pair< wxString, wxString > > > m_net_pin_dict
std::map< FOOTPRINT *, wxString > m_footprint_refdes_reverse_dict
wxXmlNode * m_enterpriseNode
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_shape.h:81
STROKE_PARAMS GetStroke() const override
Definition pcb_shape.h:91
VECTOR2I GetPosition() const override
Definition pcb_shape.h:79
bool IsBorderEnabled() const
Disables the border, this is done by changing the stroke internally.
void TransformTextToPolySet(SHAPE_POLY_SET &aBuffer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc) const
Function TransformTextToPolySet Convert the text to a polygonSet describing the actual character stro...
Definition pcb_text.cpp:567
const VECTOR2I & GetStart() const
Definition pcb_track.h:154
const VECTOR2I & GetEnd() const
Definition pcb_track.h:151
virtual int GetWidth() const
Definition pcb_track.h:148
PCB_LAYER_ID BottomLayer() const
VECTOR2I GetPosition() const override
Definition pcb_track.h:614
bool FlashLayer(int aLayer) const
Check to see whether the via should have a pad on the specific layer.
const PADSTACK & Padstack() const
Definition pcb_track.h:463
int GetWidth() const override
PCB_LAYER_ID TopLayer() const
int GetDrillValue() const
Calculate the drill value for vias (m_drill if > 0, or default drill value for the board).
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int Width() const
Get the current width of the segments in the chain.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const std::vector< VECTOR2I > & CPoints() const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Represent a set of closed polygons.
void Fracture()
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the set.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
void InflateWithLinkedHoles(int aFactor, CORNER_STRATEGY aCornerStrategy, int aMaxError)
Perform outline inflation/deflation, using round corners.
int GetWidth() const
LINE_STYLE GetLineStyle() const
Handle a list of polygons defining a copper zone.
Definition zone.h:74
void SetProgressCallback(std::function< void(size_t)> aCallback)
@ ROUND_ALL_CORNERS
All angles are rounded.
static bool empty(const wxTextEntryBase *aCtrl)
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
@ UNDEFINED
Definition eda_shape.h:44
@ SEGMENT
Definition eda_shape.h:45
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
FILL_T
Definition eda_shape.h:56
@ NO_FILL
Definition eda_shape.h:57
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:58
@ FP_SMD
Definition footprint.h:83
@ FP_THROUGH_HOLE
Definition footprint.h:82
static const wxChar traceIpc2581[]
This program source code file is part of KiCad, a free EDA CAD application.
static constexpr void hash_combine(std::size_t &seed)
This is a dummy function to take the final case of hash_combine below.
Definition hash.h:32
static constexpr std::size_t hash_val(const Types &... args)
Definition hash.h:51
size_t hash_fp_item(const EDA_ITEM *aItem, int aFlags)
Calculate hash of an EDA_ITEM.
Definition hash_eda.cpp:58
Hashing functions for EDA_ITEMs.
@ HASH_POS
Definition hash_eda.h:47
@ REL_COORD
Use coordinates relative to the parent object.
Definition hash_eda.h:50
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayerId, int aCopperLayersCount)
Definition layer_id.cpp:172
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:780
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:677
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ User_8
Definition layer_ids.h:131
@ F_CrtYd
Definition layer_ids.h:116
@ B_Adhes
Definition layer_ids.h:103
@ Edge_Cuts
Definition layer_ids.h:112
@ Dwgs_User
Definition layer_ids.h:107
@ F_Paste
Definition layer_ids.h:104
@ Cmts_User
Definition layer_ids.h:108
@ User_6
Definition layer_ids.h:129
@ User_7
Definition layer_ids.h:130
@ F_Adhes
Definition layer_ids.h:102
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ User_5
Definition layer_ids.h:128
@ Eco1_User
Definition layer_ids.h:109
@ F_Mask
Definition layer_ids.h:97
@ B_Paste
Definition layer_ids.h:105
@ User_9
Definition layer_ids.h:132
@ F_Fab
Definition layer_ids.h:119
@ Margin
Definition layer_ids.h:113
@ F_SilkS
Definition layer_ids.h:100
@ B_CrtYd
Definition layer_ids.h:115
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ Eco2_User
Definition layer_ids.h:110
@ User_3
Definition layer_ids.h:126
@ User_1
Definition layer_ids.h:124
@ B_SilkS
Definition layer_ids.h:101
@ User_4
Definition layer_ids.h:127
@ User_2
Definition layer_ids.h:125
@ F_Cu
Definition layer_ids.h:64
@ B_Fab
Definition layer_ids.h:118
bool IsValidLayer(int aLayerId)
Test whether a given integer is a valid layer index, i.e.
Definition layer_ids.h:655
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:29
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
PAD_DRILL_POST_MACHINING_MODE
Definition padstack.h:76
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ PTH
Plated through hole pad.
Definition padstack.h:98
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
@ TRAPEZOID
Definition padstack.h:56
@ RECTANGLE
Definition padstack.h:54
static bool isOppositeSideSilk(const FOOTPRINT *aFootprint, PCB_LAYER_ID aLayer)
see class PGM_BASE
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
std::vector< FAB_LAYER_COLOR > dummy
const std::vector< FAB_LAYER_COLOR > & GetStandardColors(BOARD_STACKUP_ITEM_TYPE aType)
#define KEY_CORE
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
LINE_STYLE
Dashed line types.
! The properties of a padstack drill. Drill position is always the pad position (origin).
Definition padstack.h:261
PCB_LAYER_ID start
Definition padstack.h:264
PCB_LAYER_ID end
Definition padstack.h:265
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
Definition padstack.h:262
std::optional< PAD_DRILL_POST_MACHINING_MODE > mode
Definition padstack.h:275
@ DESCRIPTION
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
KIBIS_PIN * pin
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:88
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:106
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:103
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:104
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:93
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:108
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:92
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:90
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:107
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:102
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:98
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:100
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:96
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:105
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694