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 if( entry->m_pads == 0 || fp_it->GetAttributes() & FP_EXCLUDE_FROM_BOM )
1332 entry->m_type = "DOCUMENT";
1333 else
1334 entry->m_type = "ELECTRICAL";
1335
1336 // Use the footprint's Description field if it exists
1337 const PCB_FIELD* descField = fp_it->GetField( FIELD_T::DESCRIPTION );
1338
1339 if( descField && !descField->GetText().IsEmpty() )
1340 entry->m_description = descField->GetText();
1341
1342 auto[ bom_iter, inserted ] = bom_entries.insert( std::move( entry ) );
1343
1344 if( !inserted )
1345 ( *bom_iter )->m_count++;
1346
1347 REFDES refdes;
1348 refdes.m_name = componentName( fp_it );
1349 refdes.m_pkg = fp->GetFPID().GetLibItemName().wx_str();
1350 refdes.m_populate = !fp->IsDNP() && !( fp->GetAttributes() & FP_EXCLUDE_FROM_BOM );
1351 refdes.m_layer = m_layer_name_map[fp_it->GetLayer()];
1352
1353 ( *bom_iter )->m_refdes->push_back( refdes );
1354
1355 // TODO: This amalgamates all the properties from all the footprints. We need to decide
1356 // if we want to group footprints by their properties
1357 for( PCB_FIELD* prop : fp->GetFields() )
1358 {
1359 // We don't include Reference, Datasheet, or Description in BOM characteristics.
1360 // Value and any user-defined fields are included. Reference is captured above,
1361 // and Description is used for the BomItem description attribute.
1362 if( prop->IsMandatory() && !prop->IsValue() )
1363 continue;
1364
1365 ( *bom_iter )->m_props->emplace( prop->GetName(), prop->GetText() );
1366 }
1367 }
1368
1369 if( bom_entries.empty() )
1370 return nullptr;
1371
1372 wxFileName fn( m_board->GetFileName() );
1373
1374 wxXmlNode* bomNode = new wxXmlNode( wxXML_ELEMENT_NODE, "Bom" );
1375 m_xml_root->InsertChild( bomNode, aEcadNode );
1376 addAttribute( bomNode, "name", genString( fn.GetName(), "BOM" ) );
1377
1378 wxXmlNode* bomHeaderNode = appendNode( bomNode, "BomHeader" );
1379 addAttribute( bomHeaderNode, "revision", "1.0" );
1380 addAttribute( bomHeaderNode, "assembly", genString( fn.GetName() ) );
1381
1382 wxXmlNode* stepRefNode = appendNode( bomHeaderNode, "StepRef" );
1383 addAttribute( stepRefNode, "name", genString( fn.GetName(), "BOARD" ) );
1384
1385 for( const auto& entry : bom_entries )
1386 {
1387 wxXmlNode* bomEntryNode = appendNode( bomNode, "BomItem" );
1388 addAttribute( bomEntryNode, "OEMDesignNumberRef", entry->m_OEMDesignRef );
1389 addAttribute( bomEntryNode, "quantity", wxString::Format( "%d", entry->m_count ) );
1390 addAttribute( bomEntryNode, "pinCount", wxString::Format( "%d", entry->m_pads ) );
1391 addAttribute( bomEntryNode, "category", entry->m_type );
1392
1393 if( !entry->m_description.IsEmpty() )
1394 addAttribute( bomEntryNode, "description", entry->m_description );
1395
1396 for( const REFDES& refdes : *( entry->m_refdes ) )
1397 {
1398 wxXmlNode* refdesNode = appendNode( bomEntryNode, "RefDes" );
1399 addAttribute( refdesNode, "name", refdes.m_name );
1400 addAttribute( refdesNode, "packageRef", genString( refdes.m_pkg, "PKG" ) );
1401 addAttribute( refdesNode, "populate", refdes.m_populate ? "true" : "false" );
1402 addAttribute( refdesNode, "layerRef", refdes.m_layer );
1403 }
1404
1405 wxXmlNode* characteristicsNode = appendNode( bomEntryNode, "Characteristics" );
1406 addAttribute( characteristicsNode, "category", entry->m_type );
1407
1408 for( const auto& prop : *( entry->m_props ) )
1409 {
1410 wxXmlNode* textualDefNode = appendNode( characteristicsNode, "Textual" );
1411 addAttribute( textualDefNode, "definitionSource", "KICAD" );
1412 addAttribute( textualDefNode, "textualCharacteristicName", prop.first );
1413 addAttribute( textualDefNode, "textualCharacteristicValue", prop.second );
1414 }
1415 }
1416
1417 return bomNode;
1418}
1419
1420
1422{
1423 if( m_progressReporter )
1424 m_progressReporter->AdvancePhase( _( "Generating CAD data" ) );
1425
1426 wxXmlNode* ecadNode = appendNode( m_xml_root, "Ecad" );
1427 addAttribute( ecadNode, "name", "Design" );
1428
1429 addCadHeader( ecadNode );
1430
1431 wxXmlNode* cadDataNode = appendNode( ecadNode, "CadData" );
1432 generateCadLayers( cadDataNode );
1433 generateDrillLayers( cadDataNode );
1434 generateAuxilliaryLayers( cadDataNode );
1435 generateStackup( cadDataNode );
1436 generateStepSection( cadDataNode );
1437
1439
1440 return ecadNode;
1441}
1442
1443
1444void PCB_IO_IPC2581::generateCadSpecs( wxXmlNode* aCadLayerNode )
1445{
1446 BOARD_DESIGN_SETTINGS& dsnSettings = m_board->GetDesignSettings();
1447 BOARD_STACKUP& stackup = dsnSettings.GetStackupDescriptor();
1448 stackup.SynchronizeWithBoard( &dsnSettings );
1449
1450 std::vector<BOARD_STACKUP_ITEM*> layers = stackup.GetList();
1451 std::set<PCB_LAYER_ID> added_layers;
1452
1453 for( int i = 0; i < stackup.GetCount(); i++ )
1454 {
1455 BOARD_STACKUP_ITEM* stackup_item = layers.at( i );
1456
1457 for( int sublayer_id = 0; sublayer_id < stackup_item->GetSublayersCount(); sublayer_id++ )
1458 {
1459 wxString ly_name = stackup_item->GetLayerName();
1460
1461 if( ly_name.IsEmpty() )
1462 {
1463 if( IsValidLayer( stackup_item->GetBrdLayerId() ) )
1464 ly_name = m_board->GetLayerName( stackup_item->GetBrdLayerId() );
1465
1466 if( ly_name.IsEmpty() && stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1467 {
1468 ly_name = wxString::Format( "DIELECTRIC_%d", stackup_item->GetDielectricLayerId() );
1469
1470 if( sublayer_id > 0 )
1471 ly_name += wxString::Format( "_%d", sublayer_id );
1472 }
1473 }
1474
1475 ly_name = genString( ly_name, "SPEC_LAYER" );
1476
1477 wxXmlNode* specNode = appendNode( aCadLayerNode, "Spec" );
1478 addAttribute( specNode, "name", ly_name );
1479 wxXmlNode* generalNode = appendNode( specNode, "General" );
1480 addAttribute( generalNode, "type", "MATERIAL" );
1481 wxXmlNode* propertyNode = appendNode( generalNode, "Property" );
1482
1483 switch ( stackup_item->GetType() )
1484 {
1486 {
1487 addAttribute( propertyNode, "text", "COPPER" );
1488 wxXmlNode* conductorNode = appendNode( specNode, "Conductor" );
1489 addAttribute( conductorNode, "type", "CONDUCTIVITY" );
1490 propertyNode = appendNode( conductorNode, "Property" );
1491 addAttribute( propertyNode, "unit", wxT( "SIEMENS/M" ) );
1492 addAttribute( propertyNode, "value", wxT( "5.959E7" ) );
1493 break;
1494 }
1496 {
1497 addAttribute( propertyNode, "text", stackup_item->GetMaterial() );
1498 propertyNode = appendNode( generalNode, "Property" );
1499 addAttribute( propertyNode, "text", wxString::Format( "Type : %s",
1500 stackup_item->GetTypeName() ) );
1501 wxXmlNode* dielectricNode = appendNode( specNode, "Dielectric" );
1502 addAttribute( dielectricNode, "type", "DIELECTRIC_CONSTANT" );
1503 propertyNode = appendNode( dielectricNode, "Property" );
1504 addAttribute( propertyNode, "value",
1505 floatVal( stackup_item->GetEpsilonR( sublayer_id ) ) );
1506 dielectricNode = appendNode( specNode, "Dielectric" );
1507 addAttribute( dielectricNode, "type", "LOSS_TANGENT" );
1508 propertyNode = appendNode( dielectricNode, "Property" );
1509 addAttribute( propertyNode, "value",
1510 floatVal( stackup_item->GetLossTangent( sublayer_id ) ) );
1511 break;
1512 }
1514 addAttribute( propertyNode, "text", stackup_item->GetTypeName() );
1515 propertyNode = appendNode( generalNode, "Property" );
1516 addAttribute( propertyNode, "text", wxString::Format( "Color : %s",
1517 stackup_item->GetColor() ) );
1518 propertyNode = appendNode( generalNode, "Property" );
1519 addAttribute( propertyNode, "text", wxString::Format( "Type : %s",
1520 stackup_item->GetTypeName() ) );
1521 break;
1523 {
1524 addAttribute( propertyNode, "text", "SOLDERMASK" );
1525 propertyNode = appendNode( generalNode, "Property" );
1526 addAttribute( propertyNode, "text", wxString::Format( "Color : %s",
1527 stackup_item->GetColor() ) );
1528 propertyNode = appendNode( generalNode, "Property" );
1529 addAttribute( propertyNode, "text", wxString::Format( "Type : %s",
1530 stackup_item->GetTypeName() ) );
1531
1532 // Generate Epsilon R if > 1.0 (value <= 1.0 means not specified)
1533 if( stackup_item->GetEpsilonR( sublayer_id ) > 1.0 )
1534 {
1535 wxXmlNode* dielectricNode = appendNode( specNode, "Dielectric" );
1536 addAttribute( dielectricNode, "type", "DIELECTRIC_CONSTANT" );
1537 propertyNode = appendNode( dielectricNode, "Property" );
1538 addAttribute( propertyNode, "value", floatVal( stackup_item->GetEpsilonR( sublayer_id ) ) );
1539 }
1540
1541 // Generate LossTangent if > 0.0 (value <= 0.0 means not specified)
1542 if( stackup_item->GetLossTangent( sublayer_id ) > 0.0 )
1543 {
1544 wxXmlNode* dielectricNode = appendNode( specNode, "Dielectric" );
1545 addAttribute( dielectricNode, "type", "LOSS_TANGENT" );
1546 propertyNode = appendNode( dielectricNode, "Property" );
1547 addAttribute( propertyNode, "value", floatVal( stackup_item->GetLossTangent( sublayer_id ) ) );
1548 }
1549 break;
1550 }
1551 default:
1552 break;
1553 }
1554 }
1555 }
1556}
1557
1558
1559void PCB_IO_IPC2581::addCadHeader( wxXmlNode* aEcadNode )
1560{
1561 wxXmlNode* cadHeaderNode = appendNode( aEcadNode, "CadHeader" );
1562 addAttribute( cadHeaderNode, "units", m_units_str );
1563
1564 m_cad_header_node = cadHeaderNode;
1565
1566 generateCadSpecs( cadHeaderNode );
1567}
1568
1569
1571{
1572 return ( aLayer >= F_Cu && aLayer <= User_9 ) || aLayer == UNDEFINED_LAYER;
1573}
1574
1575
1576void PCB_IO_IPC2581::addLayerAttributes( wxXmlNode* aNode, PCB_LAYER_ID aLayer )
1577{
1578 switch( aLayer )
1579 {
1580 case F_Adhes:
1581 case B_Adhes:
1582 addAttribute( aNode, "layerFunction", "GLUE" );
1583 addAttribute( aNode, "polarity", "POSITIVE" );
1584 addAttribute( aNode, "side", aLayer == F_Adhes ? "TOP" : "BOTTOM" );
1585 break;
1586 case F_Paste:
1587 case B_Paste:
1588 addAttribute( aNode, "layerFunction", "SOLDERPASTE" );
1589 addAttribute( aNode, "polarity", "POSITIVE" );
1590 addAttribute( aNode, "side", aLayer == F_Paste ? "TOP" : "BOTTOM" );
1591 break;
1592 case F_SilkS:
1593 case B_SilkS:
1594 addAttribute( aNode, "layerFunction", "SILKSCREEN" );
1595 addAttribute( aNode, "polarity", "POSITIVE" );
1596 addAttribute( aNode, "side", aLayer == F_SilkS ? "TOP" : "BOTTOM" );
1597 break;
1598 case F_Mask:
1599 case B_Mask:
1600 addAttribute( aNode, "layerFunction", "SOLDERMASK" );
1601 addAttribute( aNode, "polarity", "POSITIVE" );
1602 addAttribute( aNode, "side", aLayer == F_Mask ? "TOP" : "BOTTOM" );
1603 break;
1604 case Edge_Cuts:
1605 addAttribute( aNode, "layerFunction", "BOARD_OUTLINE" );
1606 addAttribute( aNode, "polarity", "POSITIVE" );
1607 addAttribute( aNode, "side", "ALL" );
1608 break;
1609 case B_CrtYd:
1610 case F_CrtYd:
1611 addAttribute( aNode, "layerFunction", "COURTYARD" );
1612 addAttribute( aNode, "polarity", "POSITIVE" );
1613 addAttribute( aNode, "side", aLayer == F_CrtYd ? "TOP" : "BOTTOM" );
1614 break;
1615 case B_Fab:
1616 case F_Fab:
1617 addAttribute( aNode, "layerFunction", "ASSEMBLY" );
1618 addAttribute( aNode, "polarity", "POSITIVE" );
1619 addAttribute( aNode, "side", aLayer == F_Fab ? "TOP" : "BOTTOM" );
1620 break;
1621 case Dwgs_User:
1622 case Cmts_User:
1623 case Eco1_User:
1624 case Eco2_User:
1625 case Margin:
1626 case User_1:
1627 case User_2:
1628 case User_3:
1629 case User_4:
1630 case User_5:
1631 case User_6:
1632 case User_7:
1633 case User_8:
1634 case User_9:
1635 addAttribute( aNode, "layerFunction", "DOCUMENT" );
1636 addAttribute( aNode, "polarity", "POSITIVE" );
1637 addAttribute( aNode, "side", "NONE" );
1638 break;
1639
1640 default:
1641 if( IsCopperLayer( aLayer ) )
1642 {
1643 addAttribute( aNode, "layerFunction", "CONDUCTOR" );
1644 addAttribute( aNode, "polarity", "POSITIVE" );
1645 addAttribute( aNode, "side",
1646 aLayer == F_Cu ? "TOP"
1647 : aLayer == B_Cu ? "BOTTOM"
1648 : "INTERNAL" );
1649 }
1650
1651 break; // Do not handle other layers
1652 }
1653}
1654
1655
1656void PCB_IO_IPC2581::generateStackup( wxXmlNode* aCadLayerNode )
1657{
1658 BOARD_DESIGN_SETTINGS& dsnSettings = m_board->GetDesignSettings();
1659 BOARD_STACKUP& stackup = dsnSettings.GetStackupDescriptor();
1660 stackup.SynchronizeWithBoard( &dsnSettings );
1661
1662 wxXmlNode* stackupNode = appendNode( aCadLayerNode, "Stackup" );
1663 addAttribute( stackupNode, "name", "Primary_Stackup" );
1664 addAttribute( stackupNode, "overallThickness", floatVal( m_scale * stackup.BuildBoardThicknessFromStackup() ) );
1665 addAttribute( stackupNode, "tolPlus", "0.0" );
1666 addAttribute( stackupNode, "tolMinus", "0.0" );
1667 addAttribute( stackupNode, "whereMeasured", "MASK" );
1668
1669 if( m_version > 'B' )
1670 addAttribute( stackupNode, "stackupStatus", "PROPOSED" );
1671
1672 wxXmlNode* stackupGroup = appendNode( stackupNode, "StackupGroup" );
1673 addAttribute( stackupGroup, "name", "Primary_Stackup_Group" );
1674 addAttribute( stackupGroup, "thickness", floatVal( m_scale * stackup.BuildBoardThicknessFromStackup() ) );
1675 addAttribute( stackupGroup, "tolPlus", "0.0" );
1676 addAttribute( stackupGroup, "tolMinus", "0.0" );
1677
1678 std::vector<BOARD_STACKUP_ITEM*> layers = stackup.GetList();
1679 std::set<PCB_LAYER_ID> added_layers;
1680
1681 for( int i = 0; i < stackup.GetCount(); i++ )
1682 {
1683 BOARD_STACKUP_ITEM* stackup_item = layers.at( i );
1684
1685 for( int sublayer_id = 0; sublayer_id < stackup_item->GetSublayersCount(); sublayer_id++ )
1686 {
1687
1688 wxXmlNode* stackupLayer = appendNode( stackupGroup, "StackupLayer" );
1689 wxString ly_name = stackup_item->GetLayerName();
1690
1691 if( ly_name.IsEmpty() )
1692 {
1693 if( IsValidLayer( stackup_item->GetBrdLayerId() ) )
1694 ly_name = m_board->GetLayerName( stackup_item->GetBrdLayerId() );
1695
1696 if( ly_name.IsEmpty() && stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1697 {
1698 ly_name = wxString::Format( "DIELECTRIC_%d", stackup_item->GetDielectricLayerId() );
1699
1700 if( sublayer_id > 0 )
1701 ly_name += wxString::Format( "_%d", sublayer_id );
1702 }
1703 }
1704
1705 wxString spec_name = genString( ly_name, "SPEC_LAYER" );
1706 ly_name = genString( ly_name, "LAYER" );
1707
1708 addAttribute( stackupLayer, "layerOrGroupRef", ly_name );
1709 addAttribute( stackupLayer, "thickness", floatVal( m_scale * stackup_item->GetThickness() ) );
1710 addAttribute( stackupLayer, "tolPlus", "0.0" );
1711 addAttribute( stackupLayer, "tolMinus", "0.0" );
1712 addAttribute( stackupLayer, "sequence", wxString::Format( "%d", i ) );
1713
1714 wxXmlNode* specLayerNode = appendNode( stackupLayer, "SpecRef" );
1715 addAttribute( specLayerNode, "id", spec_name );
1716 }
1717 }
1718}
1719
1720
1721void PCB_IO_IPC2581::generateCadLayers( wxXmlNode* aCadLayerNode )
1722{
1723
1724 BOARD_DESIGN_SETTINGS& dsnSettings = m_board->GetDesignSettings();
1725 BOARD_STACKUP& stackup = dsnSettings.GetStackupDescriptor();
1726 stackup.SynchronizeWithBoard( &dsnSettings );
1727
1728 std::vector<BOARD_STACKUP_ITEM*> layers = stackup.GetList();
1729 std::set<PCB_LAYER_ID> added_layers;
1730
1731 for( int i = 0; i < stackup.GetCount(); i++ )
1732 {
1733 BOARD_STACKUP_ITEM* stackup_item = layers.at( i );
1734
1735 if( !isValidLayerFor2581( stackup_item->GetBrdLayerId() ) )
1736 continue;
1737
1738 for( int sublayer_id = 0; sublayer_id < stackup_item->GetSublayersCount(); sublayer_id++ )
1739 {
1740 wxXmlNode* cadLayerNode = appendNode( aCadLayerNode, "Layer" );
1741 wxString ly_name = stackup_item->GetLayerName();
1742
1743 if( ly_name.IsEmpty() )
1744 {
1745
1746 if( IsValidLayer( stackup_item->GetBrdLayerId() ) )
1747 ly_name = m_board->GetLayerName( stackup_item->GetBrdLayerId() );
1748
1749 if( ly_name.IsEmpty() && stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1750 {
1751 ly_name = wxString::Format( "DIELECTRIC_%d", stackup_item->GetDielectricLayerId() );
1752
1753 if( sublayer_id > 0 )
1754 ly_name += wxString::Format( "_%d", sublayer_id );
1755 }
1756 }
1757
1758 ly_name = genString( ly_name, "LAYER" );
1759
1760 addAttribute( cadLayerNode, "name", ly_name );
1761
1762 if( stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1763 {
1764 if( stackup_item->GetTypeName() == KEY_CORE )
1765 addAttribute( cadLayerNode, "layerFunction", "DIELCORE" );
1766 else
1767 addAttribute( cadLayerNode, "layerFunction", "DIELPREG" );
1768
1769 addAttribute( cadLayerNode, "polarity", "POSITIVE" );
1770 addAttribute( cadLayerNode, "side", "INTERNAL" );
1771 continue;
1772 }
1773 else
1774 {
1775 added_layers.insert( stackup_item->GetBrdLayerId() );
1776 addLayerAttributes( cadLayerNode, stackup_item->GetBrdLayerId() );
1777 m_layer_name_map.emplace( stackup_item->GetBrdLayerId(), ly_name );
1778 }
1779 }
1780 }
1781
1782 LSEQ layer_seq = m_board->GetEnabledLayers().Seq();
1783
1784 for( PCB_LAYER_ID layer : layer_seq )
1785 {
1786 if( added_layers.find( layer ) != added_layers.end() || !isValidLayerFor2581( layer ) )
1787 continue;
1788
1789 wxString ly_name = genLayerString( layer, "LAYER" );
1790 m_layer_name_map.emplace( layer, ly_name );
1791 added_layers.insert( layer );
1792 wxXmlNode* cadLayerNode = appendNode( aCadLayerNode, "Layer" );
1793 addAttribute( cadLayerNode, "name", ly_name );
1794
1795 addLayerAttributes( cadLayerNode, layer );
1796 }
1797}
1798
1799
1800void PCB_IO_IPC2581::generateDrillLayers( wxXmlNode* aCadLayerNode )
1801{
1802 for( BOARD_ITEM* item : m_board->Tracks() )
1803 {
1804 if( item->Type() == PCB_VIA_T )
1805 {
1806 PCB_VIA* via = static_cast<PCB_VIA*>( item );
1807 m_drill_layers[std::make_pair( via->TopLayer(), via->BottomLayer() )].push_back( via );
1808 }
1809 }
1810
1811 for( FOOTPRINT* fp : m_board->Footprints() )
1812 {
1813 for( PAD* pad : fp->Pads() )
1814 {
1815 if( pad->HasDrilledHole() )
1816 m_drill_layers[std::make_pair( F_Cu, B_Cu )].push_back( pad );
1817 else if( pad->HasHole() )
1818 m_slot_holes[std::make_pair( F_Cu, B_Cu )].push_back( pad );
1819 }
1820 }
1821
1822 for( const auto& [layers, vec] : m_drill_layers )
1823 {
1824 wxXmlNode* drillNode = appendNode( aCadLayerNode, "Layer" );
1825 drillNode->AddAttribute( "name", genLayersString( layers.first, layers.second, "DRILL" ) );
1826 addAttribute( drillNode, "layerFunction", "DRILL" );
1827 addAttribute( drillNode, "polarity", "POSITIVE" );
1828 addAttribute( drillNode, "side", "ALL" );
1829
1830 wxXmlNode* spanNode = appendNode( drillNode, "Span" );
1831 addAttribute( spanNode, "fromLayer", genLayerString( layers.first, "LAYER" ) );
1832 addAttribute( spanNode, "toLayer", genLayerString( layers.second, "LAYER" ) );
1833 }
1834
1835 for( const auto& [layers, vec] : m_slot_holes )
1836 {
1837 wxXmlNode* drillNode = appendNode( aCadLayerNode, "Layer" );
1838 drillNode->AddAttribute( "name", genLayersString( layers.first, layers.second, "SLOT" ) );
1839
1840 addAttribute( drillNode, "layerFunction", "ROUT" );
1841 addAttribute( drillNode, "polarity", "POSITIVE" );
1842 addAttribute( drillNode, "side", "ALL" );
1843
1844 wxXmlNode* spanNode = appendNode( drillNode, "Span" );
1845 addAttribute( spanNode, "fromLayer", genLayerString( layers.first, "LAYER" ) );
1846 addAttribute( spanNode, "toLayer", genLayerString( layers.second, "LAYER" ) );
1847 }
1848}
1849
1850
1851void PCB_IO_IPC2581::generateAuxilliaryLayers( wxXmlNode* aCadLayerNode )
1852{
1853 for( BOARD_ITEM* item : m_board->Tracks() )
1854 {
1855 if( item->Type() != PCB_VIA_T )
1856 continue;
1857
1858 PCB_VIA* via = static_cast<PCB_VIA*>( item );
1859
1860 std::vector<std::tuple<auxLayerType, PCB_LAYER_ID, PCB_LAYER_ID>> new_layers;
1861
1862 if( via->Padstack().IsFilled().value_or( false ) )
1863 new_layers.emplace_back( auxLayerType::FILLING, via->TopLayer(), via->BottomLayer() );
1864
1865 if( via->Padstack().IsCapped().value_or( false ) )
1866 new_layers.emplace_back( auxLayerType::CAPPING, via->TopLayer(), via->BottomLayer() );
1867
1868 for( PCB_LAYER_ID layer : { via->TopLayer(), via->BottomLayer() } )
1869 {
1870 if( via->Padstack().IsPlugged( layer ).value_or( false ) )
1871 new_layers.emplace_back( auxLayerType::PLUGGING, layer, UNDEFINED_LAYER );
1872
1873 if( via->Padstack().IsCovered( layer ).value_or( false ) )
1874 new_layers.emplace_back( auxLayerType::COVERING, layer, UNDEFINED_LAYER );
1875
1876 if( via->Padstack().IsTented( layer ).value_or( false ) )
1877 new_layers.emplace_back( auxLayerType::TENTING, layer, UNDEFINED_LAYER );
1878 }
1879
1880 for( auto& tuple : new_layers )
1881 m_auxilliary_Layers[tuple].push_back( via );
1882 }
1883
1884 for( const auto& [layers, vec] : m_auxilliary_Layers )
1885 {
1886 bool add_node = true;
1887
1888 wxString name;
1889 wxString layerFunction;
1890
1891 // clang-format off: suggestion is inconsitent
1892 switch( std::get<0>(layers) )
1893 {
1895 name = "COVERING";
1896 layerFunction = "COATINGNONCOND";
1897 break;
1899 name = "PLUGGING";
1900 layerFunction = "HOLEFILL";
1901 break;
1903 name = "TENTING";
1904 layerFunction = "COATINGNONCOND";
1905 break;
1907 name = "FILLING";
1908 layerFunction = "HOLEFILL";
1909 break;
1911 name = "CAPPING";
1912 layerFunction = "COATINGCOND";
1913 break;
1914 default:
1915 add_node = false;
1916 break;
1917 }
1918 // clang-format on: suggestion is inconsitent
1919
1920 if( add_node && !vec.empty() )
1921 {
1922 wxXmlNode* node = appendNode( aCadLayerNode, "LAYER" );
1923 addAttribute( node, "layerFunction", layerFunction );
1924 addAttribute( node, "polarity", "POSITIVE" );
1925
1926 if( std::get<2>( layers ) == UNDEFINED_LAYER )
1927 {
1928 addAttribute( node, "name", genLayerString( std::get<1>( layers ), TO_UTF8( name ) ) );
1929 addAttribute( node, "side", IsFrontLayer( std::get<1>( layers ) ) ? "TOP" : "BOTTOM" );
1930 }
1931 else
1932 {
1933 addAttribute( node, "name",
1934 genLayersString( std::get<1>( layers ), std::get<2>( layers ), TO_UTF8( name ) ) );
1935
1936 const bool first_external = std::get<1>( layers ) == F_Cu || std::get<1>( layers ) == B_Cu;
1937 const bool second_external = std::get<2>( layers ) == F_Cu || std::get<2>( layers ) == B_Cu;
1938
1939 if( first_external )
1940 {
1941 if( second_external )
1942 addAttribute( node, "side", "ALL" );
1943 else
1944 addAttribute( node, "side", "FRONT" );
1945 }
1946 else
1947 {
1948 if( second_external )
1949 addAttribute( node, "side", "BACK" );
1950 else
1951 addAttribute( node, "side", "INTERNAL" );
1952 }
1953
1954 wxXmlNode* spanNode = appendNode( node, "SPAN" );
1955 addAttribute( spanNode, "fromLayer", genLayerString( std::get<1>( layers ), "LAYER" ) );
1956 addAttribute( spanNode, "toLayer", genLayerString( std::get<2>( layers ), "LAYER" ) );
1957 }
1958 }
1959 }
1960}
1961
1962
1963void PCB_IO_IPC2581::generateStepSection( wxXmlNode* aCadNode )
1964{
1965 wxXmlNode* stepNode = appendNode( aCadNode, "Step" );
1966 wxFileName fn( m_board->GetFileName() );
1967 addAttribute( stepNode, "name", genString( fn.GetName(), "BOARD" ) );
1968
1969 if( m_version > 'B' )
1970 addAttribute( stepNode, "type", "BOARD" );
1971
1972 wxXmlNode* datumNode = appendNode( stepNode, "Datum" );
1973 addAttribute( datumNode, "x", "0.0" );
1974 addAttribute( datumNode, "y", "0.0" );
1975
1976 generateProfile( stepNode );
1977 generateComponents( stepNode );
1978
1979 m_last_padstack = insertNode( stepNode, "NonstandardAttribute" );
1980 addAttribute( m_last_padstack, "name", "FOOTPRINT_COUNT" );
1981 addAttribute( m_last_padstack, "type", "INTEGER" );
1982 addAttribute( m_last_padstack, "value", wxString::Format( "%zu", m_board->Footprints().size() ) );
1983
1984 generateLayerFeatures( stepNode );
1985 generateLayerSetDrill( stepNode );
1986 generateLayerSetAuxilliary( stepNode );
1987}
1988
1989
1990void PCB_IO_IPC2581::addPad( wxXmlNode* aContentNode, const PAD* aPad, PCB_LAYER_ID aLayer )
1991{
1992 wxXmlNode* padNode = appendNode( aContentNode, "Pad" );
1993 FOOTPRINT* fp = aPad->GetParentFootprint();
1994
1995 addPadStack( padNode, aPad );
1996
1997 if( aPad->GetOrientation() != ANGLE_0 )
1998 {
1999 wxXmlNode* xformNode = appendNode( padNode, "Xform" );
2000 EDA_ANGLE angle = aPad->GetOrientation().Normalize();
2001
2002 xformNode->AddAttribute( "rotation", floatVal( angle.AsDegrees() ) );
2003 }
2004
2005 addLocationNode( padNode, *aPad, false );
2006 addShape( padNode, *aPad, aLayer );
2007
2008 if( fp )
2009 {
2010 wxXmlNode* pinRefNode = appendNode( padNode, "PinRef" );
2011
2012 addAttribute( pinRefNode, "componentRef", componentName( fp ) );
2013 addAttribute( pinRefNode, "pin", pinName( aPad ) );
2014 }
2015}
2016
2017
2018void PCB_IO_IPC2581::addVia( wxXmlNode* aContentNode, const PCB_VIA* aVia, PCB_LAYER_ID aLayer )
2019{
2020 if( !aVia->FlashLayer( aLayer ) )
2021 return;
2022
2023 wxXmlNode* padNode = appendNode( aContentNode, "Pad" );
2024
2025 addPadStack( padNode, aVia );
2026 addLocationNode( padNode, aVia->GetPosition().x, aVia->GetPosition().y );
2027
2028 PAD dummy( nullptr );
2029 int hole = aVia->GetDrillValue();
2030 dummy.SetDrillSize( VECTOR2I( hole, hole ) );
2031 dummy.SetPosition( aVia->GetStart() );
2032 dummy.SetSize( aLayer, VECTOR2I( aVia->GetWidth( aLayer ), aVia->GetWidth( aLayer ) ) );
2033
2034 addShape( padNode, dummy, aLayer );
2035}
2036
2037
2038void PCB_IO_IPC2581::addPadStack( wxXmlNode* aPadNode, const PAD* aPad )
2039{
2040 size_t hash = hash_fp_item( aPad, 0 );
2041 wxString name = wxString::Format( "PADSTACK_%zu", m_padstack_dict.size() + 1 );
2042 auto [ th_pair, success ] = m_padstack_dict.emplace( hash, name );
2043
2044 addAttribute( aPadNode, "padstackDefRef", th_pair->second );
2045
2046 // If we did not insert a new padstack, then we have already added it to the XML
2047 // and we don't need to add it again.
2048 if( !success )
2049 return;
2050
2051 wxXmlNode* padStackDefNode = new wxXmlNode( wxXML_ELEMENT_NODE, "PadStackDef" );
2052 addAttribute( padStackDefNode, "name", name );
2054 m_padstacks.push_back( padStackDefNode );
2055
2056 if( m_last_padstack )
2057 {
2058 insertNodeAfter( m_last_padstack, padStackDefNode );
2059 m_last_padstack = padStackDefNode;
2060 }
2061
2062 // Only handle round holes here because IPC2581 does not support non-round holes
2063 // These will be handled in a slot layer
2064 if( aPad->HasDrilledHole() )
2065 {
2066 wxXmlNode* padStackHoleNode = appendNode( padStackDefNode, "PadstackHoleDef" );
2067 padStackHoleNode->AddAttribute( "name",
2068 wxString::Format( "%s%d_%d",
2069 aPad->GetAttribute() == PAD_ATTRIB::PTH ? "PTH" : "NPTH",
2070 aPad->GetDrillSizeX(), aPad->GetDrillSizeY() ) );
2071
2072 addAttribute( padStackHoleNode, "diameter", floatVal( m_scale * aPad->GetDrillSizeX() ) );
2073 addAttribute( padStackHoleNode, "platingStatus",
2074 aPad->GetAttribute() == PAD_ATTRIB::PTH ? "PLATED" : "NONPLATED" );
2075 addAttribute( padStackHoleNode, "plusTol", "0.0" );
2076 addAttribute( padStackHoleNode, "minusTol", "0.0" );
2077 addXY( padStackHoleNode, aPad->GetOffset( PADSTACK::ALL_LAYERS ) );
2078 }
2079
2080 LSEQ layer_seq = aPad->GetLayerSet().Seq();
2081
2082 for( PCB_LAYER_ID layer : layer_seq )
2083 {
2084 FOOTPRINT* fp = aPad->GetParentFootprint();
2085
2086 if( !m_board->IsLayerEnabled( layer ) )
2087 continue;
2088
2089 wxXmlNode* padStackPadDefNode = appendNode( padStackDefNode, "PadstackPadDef" );
2090 addAttribute( padStackPadDefNode, "layerRef", m_layer_name_map[layer] );
2091 addAttribute( padStackPadDefNode, "padUse", "REGULAR" );
2092 addLocationNode( padStackPadDefNode, aPad->GetOffset( PADSTACK::ALL_LAYERS ).x, aPad->GetOffset( PADSTACK::ALL_LAYERS ).y );
2093
2094 if( aPad->HasHole() || !aPad->FlashLayer( layer ) )
2095 {
2096 PCB_SHAPE shape( nullptr, SHAPE_T::CIRCLE );
2097 shape.SetStart( aPad->GetOffset( PADSTACK::ALL_LAYERS ) );
2098 shape.SetEnd( shape.GetStart() + aPad->GetDrillSize() / 2 );
2099 addShape( padStackPadDefNode, shape );
2100 }
2101 else
2102 {
2103 addShape( padStackPadDefNode, *aPad, layer );
2104 }
2105 }
2106}
2107
2108
2109void PCB_IO_IPC2581::addPadStack( wxXmlNode* aContentNode, const PCB_VIA* aVia )
2110{
2111 size_t hash = hash_fp_item( aVia, 0 );
2112 wxString name = wxString::Format( "PADSTACK_%zu", m_padstack_dict.size() + 1 );
2113 auto [ via_pair, success ] = m_padstack_dict.emplace( hash, name );
2114
2115 addAttribute( aContentNode, "padstackDefRef", via_pair->second );
2116
2117 // If we did not insert a new padstack, then we have already added it to the XML
2118 // and we don't need to add it again.
2119 if( !success )
2120 return;
2121
2122 wxXmlNode* padStackDefNode = new wxXmlNode( wxXML_ELEMENT_NODE, "PadStackDef" );
2123 insertNodeAfter( m_last_padstack, padStackDefNode );
2124 m_last_padstack = padStackDefNode;
2125 addAttribute( padStackDefNode, "name", name );
2127
2128 wxXmlNode* padStackHoleNode = appendNode( padStackDefNode, "PadstackHoleDef" );
2129 addAttribute( padStackHoleNode, "name", wxString::Format( "PH%d", aVia->GetDrillValue() ) );
2130 padStackHoleNode->AddAttribute( "diameter", floatVal( m_scale * aVia->GetDrillValue() ) );
2131 addAttribute( padStackHoleNode, "platingStatus", "VIA" );
2132 addAttribute( padStackHoleNode, "plusTol", "0.0" );
2133 addAttribute( padStackHoleNode, "minusTol", "0.0" );
2134 addAttribute( padStackHoleNode, "x", "0.0" );
2135 addAttribute( padStackHoleNode, "y", "0.0" );
2136
2137 LSEQ layer_seq = aVia->GetLayerSet().Seq();
2138
2139 auto addPadShape{ [&]( PCB_LAYER_ID layer, const PCB_VIA* aVia, const wxString& name,
2140 bool drill ) -> void
2141 {
2142 PCB_SHAPE shape( nullptr, SHAPE_T::CIRCLE );
2143
2144 if( drill )
2145 shape.SetEnd( { KiROUND( aVia->GetDrillValue() / 2.0 ), 0 } );
2146 else
2147 shape.SetEnd( { KiROUND( aVia->GetWidth( layer ) / 2.0 ), 0 } );
2148
2149 wxXmlNode* padStackPadDefNode =
2150 appendNode( padStackDefNode, "PadstackPadDef" );
2151 addAttribute( padStackPadDefNode, "layerRef", name );
2152 addAttribute( padStackPadDefNode, "padUse", "REGULAR" );
2153
2154 addLocationNode( padStackPadDefNode, 0.0, 0.0 );
2155 addShape( padStackPadDefNode, shape );
2156 } };
2157
2158 for( PCB_LAYER_ID layer : layer_seq )
2159 {
2160 if( !aVia->FlashLayer( layer ) || !m_board->IsLayerEnabled( layer ) )
2161 continue;
2162
2163 addPadShape( layer, aVia, m_layer_name_map[layer], false );
2164 }
2165
2166 if( aVia->Padstack().IsFilled().value_or( false ) )
2167 addPadShape( UNDEFINED_LAYER, aVia, genLayersString( aVia->TopLayer(), aVia->BottomLayer(), "FILLING" ), true );
2168
2169 if( aVia->Padstack().IsCapped().value_or( false ) )
2170 addPadShape( UNDEFINED_LAYER, aVia, genLayersString( aVia->TopLayer(), aVia->BottomLayer(), "CAPPING" ), true );
2171
2172 for( PCB_LAYER_ID layer : { aVia->TopLayer(), aVia->BottomLayer() } )
2173 {
2174 if( aVia->Padstack().IsPlugged( layer ).value_or( false ) )
2175 addPadShape( layer, aVia, genLayerString( layer, "PLUGGING" ), true );
2176
2177 if( aVia->Padstack().IsCovered( layer ).value_or( false ) )
2178 addPadShape( layer, aVia, genLayerString( layer, "COVERING" ), false );
2179
2180 if( aVia->Padstack().IsTented( layer ).value_or( false ) )
2181 addPadShape( layer, aVia, genLayerString( layer, "TENTING" ), false );
2182 }
2183}
2184
2185
2186void PCB_IO_IPC2581::ensureBackdrillSpecs( const wxString& aPadstackName, const PADSTACK& aPadstack )
2187{
2188 if( m_padstack_backdrill_specs.find( aPadstackName ) != m_padstack_backdrill_specs.end() )
2189 return;
2190
2191 const PADSTACK::DRILL_PROPS& secondary = aPadstack.SecondaryDrill();
2192
2193 if( secondary.start == UNDEFINED_LAYER || secondary.end == UNDEFINED_LAYER )
2194 return;
2195
2196 if( secondary.size.x <= 0 && secondary.size.y <= 0 )
2197 return;
2198
2199 if( !m_cad_header_node )
2200 return;
2201
2202 auto layerHasRef = [&]( PCB_LAYER_ID aLayer ) -> bool
2203 {
2204 return m_layer_name_map.find( aLayer ) != m_layer_name_map.end();
2205 };
2206
2207 if( !layerHasRef( secondary.start ) || !layerHasRef( secondary.end ) )
2208 return;
2209
2210 BOARD_DESIGN_SETTINGS& dsnSettings = m_board->GetDesignSettings();
2211 BOARD_STACKUP& stackup = dsnSettings.GetStackupDescriptor();
2212 stackup.SynchronizeWithBoard( &dsnSettings );
2213
2214 auto createSpec = [&]( const PADSTACK::DRILL_PROPS& aDrill, const wxString& aSpecName ) -> wxString
2215 {
2216 if( aDrill.start == UNDEFINED_LAYER || aDrill.end == UNDEFINED_LAYER )
2217 return wxString();
2218
2219 auto startLayer = m_layer_name_map.find( aDrill.start );
2220 auto endLayer = m_layer_name_map.find( aDrill.end );
2221
2222 if( startLayer == m_layer_name_map.end() || endLayer == m_layer_name_map.end() )
2223 return wxString();
2224
2225 wxXmlNode* specNode = appendNode( m_cad_header_node, "Spec" );
2226 addAttribute( specNode, "name", aSpecName );
2227
2228 wxXmlNode* backdrillNode = appendNode( specNode, "Backdrill" );
2229 addAttribute( backdrillNode, "startLayerRef", startLayer->second );
2230 addAttribute( backdrillNode, "mustNotCutLayerRef", endLayer->second );
2231
2232 int stubLength = stackup.GetLayerDistance( aDrill.start, aDrill.end );
2233
2234 if( stubLength < 0 )
2235 stubLength = 0;
2236
2237 addAttribute( backdrillNode, "maxStubLength", floatVal( m_scale * stubLength ) );
2238
2240
2241 if( aDrill.start == F_Cu )
2242 pm_mode = aPadstack.FrontPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
2243 else if( aDrill.start == B_Cu )
2244 pm_mode = aPadstack.BackPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
2245
2246 bool isPostMachined = ( pm_mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ||
2248
2249 addAttribute( backdrillNode, "postMachining", isPostMachined ? wxT( "true" )
2250 : wxT( "false" ) );
2251
2252 m_backdrill_spec_nodes[aSpecName] = specNode;
2253
2254 return aSpecName;
2255 };
2256
2257 int specIndex = m_backdrill_spec_index + 1;
2258
2259 const PADSTACK::DRILL_PROPS& primary = aPadstack.Drill();
2260 wxString primarySpec = createSpec( primary, wxString::Format( wxT( "BD_%dA" ), specIndex ) );
2261
2262 wxString secondarySpec = createSpec( secondary, wxString::Format( wxT( "BD_%dB" ), specIndex ) );
2263
2264 if( primarySpec.IsEmpty() && secondarySpec.IsEmpty() )
2265 return;
2266
2267 m_backdrill_spec_index = specIndex;
2268 m_padstack_backdrill_specs.emplace( aPadstackName, std::make_pair( primarySpec, secondarySpec ) );
2269}
2270
2271
2272void PCB_IO_IPC2581::addBackdrillSpecRefs( wxXmlNode* aHoleNode, const wxString& aPadstackName )
2273{
2274 auto it = m_padstack_backdrill_specs.find( aPadstackName );
2275
2276 if( it == m_padstack_backdrill_specs.end() )
2277 return;
2278
2279 auto addRef = [&]( const wxString& aSpecName )
2280 {
2281 if( aSpecName.IsEmpty() )
2282 return;
2283
2284 wxXmlNode* specRefNode = appendNode( aHoleNode, "SpecRef" );
2285 addAttribute( specRefNode, "id", aSpecName );
2286 m_backdrill_spec_used.insert( aSpecName );
2287 };
2288
2289 addRef( it->second.first );
2290 addRef( it->second.second );
2291}
2292
2293
2295{
2296 if( !m_cad_header_node )
2297 return;
2298
2299 auto it = m_backdrill_spec_nodes.begin();
2300
2301 while( it != m_backdrill_spec_nodes.end() )
2302 {
2303 if( m_backdrill_spec_used.find( it->first ) == m_backdrill_spec_used.end() )
2304 {
2305 wxXmlNode* specNode = it->second;
2306
2307 if( specNode )
2308 {
2309 m_cad_header_node->RemoveChild( specNode );
2310 delete specNode;
2311 }
2312
2313 it = m_backdrill_spec_nodes.erase( it );
2314 }
2315 else
2316 {
2317 ++it;
2318 }
2319 }
2320}
2321
2322
2323bool PCB_IO_IPC2581::addPolygonNode( wxXmlNode* aParentNode,
2324 const SHAPE_LINE_CHAIN& aPolygon, FILL_T aFillType,
2325 int aWidth, LINE_STYLE aDashType )
2326{
2327 wxXmlNode* polygonNode = nullptr;
2328
2329 if( aPolygon.PointCount() < 3 )
2330 return false;
2331
2332 auto make_node =
2333 [&]()
2334 {
2335 polygonNode = appendNode( aParentNode, "Polygon" );
2336 wxXmlNode* polybeginNode = appendNode( polygonNode, "PolyBegin" );
2337
2338 const std::vector<VECTOR2I>& pts = aPolygon.CPoints();
2339 addXY( polybeginNode, pts[0] );
2340
2341 for( size_t ii = 1; ii < pts.size(); ++ii )
2342 {
2343 wxXmlNode* polyNode = appendNode( polygonNode, "PolyStepSegment" );
2344 addXY( polyNode, pts[ii] );
2345 }
2346
2347 wxXmlNode* polyendNode = appendNode( polygonNode, "PolyStepSegment" );
2348 addXY( polyendNode, pts[0] );
2349 };
2350
2351 // Allow the case where we don't want line/fill information in the polygon
2352 if( aFillType == FILL_T::NO_FILL )
2353 {
2354 make_node();
2355 // If we specify a line width, we need to add a LineDescRef node and
2356 // since this is only valid for a non-filled polygon, we need to create
2357 // the fillNode as well
2358 if( aWidth > 0 )
2359 addLineDesc( polygonNode, aWidth, aDashType, true );
2360 }
2361 else
2362 {
2363 wxCHECK( aWidth == 0, false );
2364 make_node();
2365 }
2366
2367 addFillDesc( polygonNode, aFillType );
2368
2369 return true;
2370}
2371
2372
2373bool PCB_IO_IPC2581::addPolygonCutouts( wxXmlNode* aParentNode,
2374 const SHAPE_POLY_SET::POLYGON& aPolygon )
2375{
2376 for( size_t ii = 1; ii < aPolygon.size(); ++ii )
2377 {
2378 wxCHECK2( aPolygon[ii].PointCount() >= 3, continue );
2379
2380 wxXmlNode* cutoutNode = appendNode( aParentNode, "Cutout" );
2381 wxXmlNode* polybeginNode = appendNode( cutoutNode, "PolyBegin" );
2382
2383 const std::vector<VECTOR2I>& hole = aPolygon[ii].CPoints();
2384 addXY( polybeginNode, hole[0] );
2385
2386 for( size_t jj = 1; jj < hole.size(); ++jj )
2387 {
2388 wxXmlNode* polyNode = appendNode( cutoutNode, "PolyStepSegment" );
2389 addXY( polyNode, hole[jj] );
2390 }
2391
2392 wxXmlNode* polyendNode = appendNode( cutoutNode, "PolyStepSegment" );
2393 addXY( polyendNode, hole[0] );
2394 }
2395
2396 return true;
2397}
2398
2399
2400bool PCB_IO_IPC2581::addOutlineNode( wxXmlNode* aParentNode, const SHAPE_POLY_SET& aPolySet,
2401 int aWidth, LINE_STYLE aDashType )
2402{
2403 if( aPolySet.OutlineCount() == 0 )
2404 return false;
2405
2406 wxXmlNode* outlineNode = appendNode( aParentNode, "Outline" );
2407
2408 // Outlines can only have one polygon according to the IPC-2581 spec, so
2409 // if there are more than one, we need to combine them into a single polygon
2410 const SHAPE_LINE_CHAIN* outline = &aPolySet.Outline( 0 );
2411 SHAPE_LINE_CHAIN bbox_outline;
2412 BOX2I bbox = outline->BBox();
2413
2414 if( aPolySet.OutlineCount() > 1 )
2415 {
2416 for( int ii = 1; ii < aPolySet.OutlineCount(); ++ii )
2417 {
2418 wxCHECK2( aPolySet.Outline( ii ).PointCount() >= 3, continue );
2419 bbox.Merge( aPolySet.Outline( ii ).BBox() );
2420 }
2421
2422 bbox_outline.Append( bbox.GetLeft(), bbox.GetTop() );
2423 bbox_outline.Append( bbox.GetRight(), bbox.GetTop() );
2424 bbox_outline.Append( bbox.GetRight(), bbox.GetBottom() );
2425 bbox_outline.Append( bbox.GetLeft(), bbox.GetBottom() );
2426 outline = &bbox_outline;
2427 }
2428
2429
2430 if( !addPolygonNode( outlineNode, *outline ) )
2431 wxLogTrace( traceIpc2581, wxS( "Failed to add polygon to outline" ) );
2432
2433 if( !outlineNode->GetChildren() )
2434 {
2435 aParentNode->RemoveChild( outlineNode );
2436 delete outlineNode;
2437 return false;
2438 }
2439
2440 addLineDesc( outlineNode, aWidth, aDashType );
2441
2442 return true;
2443}
2444
2445
2446bool PCB_IO_IPC2581::addContourNode( wxXmlNode* aParentNode, const SHAPE_POLY_SET& aPolySet,
2447 int aOutline, FILL_T aFillType, int aWidth, LINE_STYLE aDashType )
2448{
2449 if( aPolySet.OutlineCount() < ( aOutline + 1 ) )
2450 return false;
2451
2452 wxXmlNode* contourNode = appendNode( aParentNode, "Contour" );
2453
2454 if( addPolygonNode( contourNode, aPolySet.Outline( aOutline ), aFillType, aWidth, aDashType ) )
2455 {
2456 // Do not attempt to add cutouts to shapes that are already hollow
2457 if( aFillType != FILL_T::NO_FILL )
2458 addPolygonCutouts( contourNode, aPolySet.Polygon( aOutline ) );
2459 }
2460 else
2461 {
2462 aParentNode->RemoveChild( contourNode );
2463 delete contourNode;
2464 return false;
2465 }
2466
2467 return true;
2468}
2469
2470
2471void PCB_IO_IPC2581::generateProfile( wxXmlNode* aStepNode )
2472{
2473 SHAPE_POLY_SET board_outline;
2474
2475 if( ! m_board->GetBoardPolygonOutlines( board_outline ) )
2476 {
2477 Report( _( "Board outline is invalid or missing. Please run DRC." ), RPT_SEVERITY_ERROR );
2478 return;
2479 }
2480
2481 wxXmlNode* profileNode = appendNode( aStepNode, "Profile" );
2482
2483 if( !addPolygonNode( profileNode, board_outline.Outline( 0 ) ) )
2484 {
2485 wxLogTrace( traceIpc2581, wxS( "Failed to add polygon to profile" ) );
2486 aStepNode->RemoveChild( profileNode );
2487 delete profileNode;
2488 }
2489}
2490
2491
2492static bool isOppositeSideSilk( const FOOTPRINT* aFootprint, PCB_LAYER_ID aLayer )
2493{
2494 if( !aFootprint )
2495 return false;
2496
2497 if( aLayer != F_SilkS && aLayer != B_SilkS )
2498 return false;
2499
2500 if( aFootprint->IsFlipped() )
2501 return aLayer == F_SilkS;
2502
2503 return aLayer == B_SilkS;
2504}
2505
2506
2507wxXmlNode* PCB_IO_IPC2581::addPackage( wxXmlNode* aContentNode, FOOTPRINT* aFp )
2508{
2509 std::unique_ptr<FOOTPRINT> fp( static_cast<FOOTPRINT*>( aFp->Clone() ) );
2510 fp->SetParentGroup( nullptr );
2511 fp->SetPosition( { 0, 0 } );
2512 fp->SetOrientation( ANGLE_0 );
2513
2514 size_t hash = hash_fp_item( fp.get(), HASH_POS | REL_COORD );
2515 wxString name = genString( wxString::Format( "%s_%zu",
2516 fp->GetFPID().GetLibItemName().wx_str(),
2517 m_footprint_dict.size() + 1 ) );
2518
2519 auto [ iter, success ] = m_footprint_dict.emplace( hash, name );
2520 addAttribute( aContentNode, "packageRef", iter->second );
2521
2522 if( !success)
2523 return nullptr;
2524
2525 // Package and Component nodes are at the same level, so we need to find the parent
2526 // which should be the Step node
2527 wxXmlNode* packageNode = new wxXmlNode( wxXML_ELEMENT_NODE, "Package" );
2528 wxXmlNode* otherSideViewNode = nullptr; // Only set this if we have elements on the back side
2529
2530 addAttribute( packageNode, "name", name );
2531 addAttribute( packageNode, "type", "OTHER" ); // TODO: Replace with actual package type once we encode this
2532
2533 // We don't specially identify pin 1 in our footprints, so we need to guess
2534 if( fp->FindPadByNumber( "1" ) )
2535 addAttribute( packageNode, "pinOne", "1" );
2536 else if ( fp->FindPadByNumber( "A1" ) )
2537 addAttribute( packageNode, "pinOne", "A1" );
2538 else if ( fp->FindPadByNumber( "A" ) )
2539 addAttribute( packageNode, "pinOne", "A" );
2540 else if ( fp->FindPadByNumber( "a" ) )
2541 addAttribute( packageNode, "pinOne", "a" );
2542 else if ( fp->FindPadByNumber( "a1" ) )
2543 addAttribute( packageNode, "pinOne", "a1" );
2544 else if ( fp->FindPadByNumber( "Anode" ) )
2545 addAttribute( packageNode, "pinOne", "Anode" );
2546 else if ( fp->FindPadByNumber( "ANODE" ) )
2547 addAttribute( packageNode, "pinOne", "ANODE" );
2548 else
2549 addAttribute( packageNode, "pinOne", "UNKNOWN" );
2550
2551 addAttribute( packageNode, "pinOneOrientation", "OTHER" );
2552
2553 const SHAPE_POLY_SET& courtyard = fp->GetCourtyard( F_CrtYd );
2554 const SHAPE_POLY_SET& courtyard_back = fp->GetCourtyard( B_CrtYd );
2555
2556 if( courtyard.OutlineCount() > 0 )
2557 addOutlineNode( packageNode, courtyard, courtyard.Outline( 0 ).Width(), LINE_STYLE::SOLID );
2558
2559 if( courtyard_back.OutlineCount() > 0 )
2560 {
2561 if( m_version > 'B' )
2562 {
2563 otherSideViewNode = appendNode( packageNode, "OtherSideView" );
2564 addOutlineNode( otherSideViewNode, courtyard_back, courtyard_back.Outline( 0 ).Width(),
2566 }
2567 }
2568
2569 if( !courtyard.OutlineCount() && !courtyard_back.OutlineCount() )
2570 {
2571 SHAPE_POLY_SET bbox = fp->GetBoundingHull();
2572 addOutlineNode( packageNode, bbox );
2573 }
2574
2575 wxXmlNode* pickupPointNode = appendNode( packageNode, "PickupPoint" );
2576 addAttribute( pickupPointNode, "x", "0.0" );
2577 addAttribute( pickupPointNode, "y", "0.0" );
2578
2579 std::map<PCB_LAYER_ID, std::map<bool, std::vector<BOARD_ITEM*>>> elements;
2580
2581 for( BOARD_ITEM* item : fp->GraphicalItems() )
2582 {
2583 PCB_LAYER_ID layer = item->GetLayer();
2584
2588 if( layer != F_SilkS && layer != B_SilkS && layer != F_Fab && layer != B_Fab )
2589 continue;
2590
2591 if( m_version == 'B' && isOppositeSideSilk( fp.get(), layer ) )
2592 continue;
2593
2594 bool is_abs = true;
2595
2596 if( item->Type() == PCB_SHAPE_T )
2597 {
2598 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2599
2600 // Circles and Rectanges only have size information so we need to place them in
2601 // a separate node that has a location
2602 if( shape->GetShape() == SHAPE_T::CIRCLE || shape->GetShape() == SHAPE_T::RECTANGLE )
2603 is_abs = false;
2604 }
2605
2606 elements[item->GetLayer()][is_abs].push_back( item );
2607 }
2608
2609 auto add_base_node =
2610 [&]( PCB_LAYER_ID aLayer ) -> wxXmlNode*
2611 {
2612 wxXmlNode* parent = packageNode;
2613 bool is_back = aLayer == B_SilkS || aLayer == B_Fab;
2614
2615 if( is_back && m_version > 'B' )
2616 {
2617 if( !otherSideViewNode )
2618 otherSideViewNode = new wxXmlNode( wxXML_ELEMENT_NODE, "OtherSideView" );
2619
2620 parent = otherSideViewNode;
2621 }
2622
2623 wxString name;
2624
2625 if( aLayer == F_SilkS || aLayer == B_SilkS )
2626 name = "SilkScreen";
2627 else if( aLayer == F_Fab || aLayer == B_Fab )
2628 name = "AssemblyDrawing";
2629 else
2630 wxASSERT( false );
2631
2632 wxXmlNode* new_node = appendNode( parent, name );
2633 return new_node;
2634 };
2635
2636 auto add_marking_node =
2637 [&]( wxXmlNode* aNode ) -> wxXmlNode*
2638 {
2639 wxXmlNode* marking_node = appendNode( aNode, "Marking" );
2640 addAttribute( marking_node, "markingUsage", "NONE" );
2641 return marking_node;
2642 };
2643
2644 std::map<PCB_LAYER_ID, wxXmlNode*> layer_nodes;
2645 std::map<PCB_LAYER_ID, BOX2I> layer_bbox;
2646
2647 for( auto layer : { F_Fab, B_Fab } )
2648 {
2649 if( elements.find( layer ) != elements.end() )
2650 {
2651 if( elements[layer][true].size() > 0 )
2652 layer_bbox[layer] = elements[layer][true][0]->GetBoundingBox();
2653 else if( elements[layer][false].size() > 0 )
2654 layer_bbox[layer] = elements[layer][false][0]->GetBoundingBox();
2655 }
2656 }
2657
2658 for( auto& [layer, map] : elements )
2659 {
2660 wxXmlNode* layer_node = add_base_node( layer );
2661 wxXmlNode* marking_node = add_marking_node( layer_node );
2662 wxXmlNode* group_node = appendNode( marking_node, "UserSpecial" );
2663 bool update_bbox = false;
2664
2665 if( layer == F_Fab || layer == B_Fab )
2666 {
2667 layer_nodes[layer] = layer_node;
2668 update_bbox = true;
2669 }
2670
2671 for( auto& [is_abs, vec] : map )
2672 {
2673 for( BOARD_ITEM* item : vec )
2674 {
2675 wxXmlNode* output_node = nullptr;
2676
2677 if( update_bbox )
2678 layer_bbox[layer].Merge( item->GetBoundingBox() );
2679
2680 if( !is_abs )
2681 output_node = add_marking_node( layer_node );
2682 else
2683 output_node = group_node;
2684
2685 switch( item->Type() )
2686 {
2687 case PCB_TEXT_T:
2688 {
2689 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
2690
2691 if( text->IsKnockout() )
2692 addKnockoutText( output_node, text );
2693 else
2694 addText( output_node, text, text->GetFontMetrics() );
2695
2696 break;
2697 }
2698
2699 case PCB_TEXTBOX_T:
2700 {
2701 PCB_TEXTBOX* text = static_cast<PCB_TEXTBOX*>( item );
2702 addText( output_node, text, text->GetFontMetrics() );
2703
2704 // We want to force this to be a polygon to get absolute coordinates
2705 if( text->IsBorderEnabled() )
2706 {
2707 SHAPE_POLY_SET poly_set;
2708 text->GetEffectiveShape()->TransformToPolygon( poly_set, 0, ERROR_INSIDE );
2709 addContourNode( output_node, poly_set, 0, FILL_T::NO_FILL,
2710 text->GetBorderWidth() );
2711 }
2712
2713 break;
2714 }
2715
2716 case PCB_SHAPE_T:
2717 {
2718 if( !is_abs )
2719 addLocationNode( output_node, *static_cast<PCB_SHAPE*>( item ) );
2720
2721 addShape( output_node, *static_cast<PCB_SHAPE*>( item ) );
2722
2723 break;
2724 }
2725
2726 default: break;
2727 }
2728 }
2729 }
2730
2731 if( group_node->GetChildren() == nullptr )
2732 {
2733 marking_node->RemoveChild( group_node );
2734 layer_node->RemoveChild( marking_node );
2735 delete group_node;
2736 delete marking_node;
2737 }
2738 }
2739
2740 for( auto&[layer, bbox] : layer_bbox )
2741 {
2742 if( bbox.GetWidth() > 0 )
2743 {
2744 wxXmlNode* outlineNode = insertNode( layer_nodes[layer], "Outline" );
2745
2746 SHAPE_LINE_CHAIN outline;
2747 std::vector<VECTOR2I> points( 4 );
2748 points[0] = bbox.GetPosition();
2749 points[2] = bbox.GetEnd();
2750 points[1].x = points[0].x;
2751 points[1].y = points[2].y;
2752 points[3].x = points[2].x;
2753 points[3].y = points[0].y;
2754
2755 outline.Append( points );
2756 addPolygonNode( outlineNode, outline, FILL_T::NO_FILL, 0 );
2757 addLineDesc( outlineNode, 0, LINE_STYLE::SOLID );
2758 }
2759 }
2760
2761 std::map<wxString, wxXmlNode*> pin_nodes;
2762
2763 for( size_t ii = 0; ii < fp->Pads().size(); ++ii )
2764 {
2765 PAD* pad = fp->Pads()[ii];
2766 wxString name = pinName( pad );
2767 wxXmlNode* pinNode = nullptr;
2768
2769 auto [ it, inserted ] = pin_nodes.emplace( name, nullptr );
2770
2771 if( inserted )
2772 {
2773 pinNode = appendNode( packageNode, "Pin" );
2774 it->second = pinNode;
2775
2776 addAttribute( pinNode, "number", name );
2777
2778 m_net_pin_dict[pad->GetNetCode()].emplace_back(
2779 genString( fp->GetReference(), "CMP" ), name );
2780
2781 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
2782 addAttribute( pinNode, "electricalType", "MECHANICAL" );
2783 else if( pad->IsOnCopperLayer() )
2784 addAttribute( pinNode, "electricalType", "ELECTRICAL" );
2785 else
2786 addAttribute( pinNode, "electricalType", "UNDEFINED" );
2787
2788 if( pad->HasHole() )
2789 addAttribute( pinNode, "type", "THRU" );
2790 else
2791 addAttribute( pinNode, "type", "SURFACE" );
2792
2793 if( pad->GetFPRelativeOrientation() != ANGLE_0 )//|| fp->IsFlipped() )
2794 {
2795 wxXmlNode* xformNode = appendNode( pinNode, "Xform" );
2796 EDA_ANGLE pad_angle = pad->GetFPRelativeOrientation().Normalize();
2797
2798 if( fp->IsFlipped() )
2799 pad_angle = ( pad_angle.Invert() - ANGLE_180 ).Normalize();
2800
2801 if( pad_angle != ANGLE_0 )
2802 xformNode->AddAttribute( "rotation", floatVal( pad_angle.AsDegrees() ) );
2803 }
2804 }
2805 else
2806 {
2807 pinNode = it->second;
2808 }
2809
2810 addLocationNode( pinNode, *pad, true );
2811 addShape( pinNode, *pad, pad->GetLayer() );
2812
2813 // We just need the padstack, we don't need the reference here. The reference will be
2814 // created in the LayerFeature set
2815 wxXmlNode dummy;
2816 addPadStack( &dummy, pad );
2817 }
2818
2819 return packageNode;
2820}
2821
2822
2823void PCB_IO_IPC2581::generateComponents( wxXmlNode* aStepNode )
2824{
2825 std::vector<wxXmlNode*> componentNodes;
2826 std::vector<wxXmlNode*> packageNodes;
2827 std::set<wxString> packageNames;
2828
2829 bool generate_unique = m_OEMRef.empty();
2830
2831 for( FOOTPRINT* fp : m_board->Footprints() )
2832 {
2833 wxXmlNode* componentNode = new wxXmlNode( wxXML_ELEMENT_NODE, "Component" );
2834 addAttribute( componentNode, "refDes", componentName( fp ) );
2835 wxXmlNode* pkg = addPackage( componentNode, fp );
2836
2837 if( pkg )
2838 packageNodes.push_back( pkg );
2839
2840 wxString name;
2841
2842 PCB_FIELD* field = nullptr;
2843
2844 if( !generate_unique )
2845 field = fp->GetField( m_OEMRef );
2846
2847 if( field && !field->GetText().empty() )
2848 {
2849 name = field->GetShownText( false );
2850 }
2851 else
2852 {
2853 name = wxString::Format( "%s_%s_%s", fp->GetFPID().GetFullLibraryName(),
2854 fp->GetFPID().GetLibItemName().wx_str(),
2855 fp->GetValue() );
2856 }
2857
2858 if( !m_OEMRef_dict.emplace( fp, name ).second )
2859 Report( _( "Duplicate footprint pointers encountered; IPC-2581 output may be incorrect." ),
2861
2862 addAttribute( componentNode, "part", genString( name, "REF" ) );
2863 addAttribute( componentNode, "layerRef", m_layer_name_map[fp->GetLayer()] );
2864
2865 if( fp->GetAttributes() & FP_THROUGH_HOLE )
2866 addAttribute( componentNode, "mountType", "THMT" );
2867 else if( fp->GetAttributes() & FP_SMD )
2868 addAttribute( componentNode, "mountType", "SMT" );
2869 else
2870 addAttribute( componentNode, "mountType", "OTHER" );
2871
2872 if( fp->GetOrientation() != ANGLE_0 || fp->IsFlipped() )
2873 {
2874 wxXmlNode* xformNode = appendNode( componentNode, "Xform" );
2875
2876 EDA_ANGLE fp_angle = fp->GetOrientation().Normalize();
2877
2878 if( fp->IsFlipped() )
2879 fp_angle = ( fp_angle.Invert() - ANGLE_180 ).Normalize();
2880
2881 if( fp_angle != ANGLE_0 )
2882 addAttribute( xformNode, "rotation", floatVal( fp_angle.AsDegrees(), 2 ) );
2883
2884 if( fp->IsFlipped() )
2885 addAttribute( xformNode, "mirror", "true" );
2886 }
2887
2888 addLocationNode( componentNode, fp->GetPosition().x, fp->GetPosition().y );
2889
2890 componentNodes.push_back( componentNode );
2891 }
2892
2893 for( wxXmlNode* padstack : m_padstacks )
2894 {
2895 insertNode( aStepNode, padstack );
2896 m_last_padstack = padstack;
2897 }
2898
2899 for( wxXmlNode* pkg : packageNodes )
2900 aStepNode->AddChild( pkg );
2901
2902 for( wxXmlNode* cmp : componentNodes )
2903 aStepNode->AddChild( cmp );
2904}
2905
2906
2907void PCB_IO_IPC2581::generateLogicalNets( wxXmlNode* aStepNode )
2908{
2909 for( auto& [ net, pin_pair] : m_net_pin_dict )
2910 {
2911 wxXmlNode* netNode = appendNode( aStepNode, "LogicalNet" );
2912 addAttribute( netNode, "name",
2913 genString( m_board->GetNetInfo().GetNetItem( net )->GetNetname(), "NET" ) ) ;
2914
2915 for( auto& [cmp, pin] : pin_pair )
2916 {
2917 wxXmlNode* netPinNode = appendNode( netNode, "PinRef" );
2918 addAttribute( netPinNode, "componentRef", cmp );
2919 addAttribute( netPinNode, "pin", pin );
2920 }
2921 //TODO: Finish
2922 }
2923}
2924
2925//TODO: Add PhyNetGroup section
2926
2927void PCB_IO_IPC2581::generateLayerFeatures( wxXmlNode* aStepNode )
2928{
2929 LSEQ layers = m_board->GetEnabledLayers().Seq();
2930 const NETINFO_LIST& nets = m_board->GetNetInfo();
2931 std::vector<std::unique_ptr<FOOTPRINT>> footprints;
2932
2933 // To avoid the overhead of repeatedly cycling through the layers and nets,
2934 // we pre-sort the board items into a map of layer -> net -> items
2935 std::map<PCB_LAYER_ID, std::map<int, std::vector<BOARD_ITEM*>>> elements;
2936
2937 std::for_each( m_board->Tracks().begin(), m_board->Tracks().end(),
2938 [&layers, &elements]( PCB_TRACK* aTrack )
2939 {
2940 if( aTrack->Type() == PCB_VIA_T )
2941 {
2942 PCB_VIA* via = static_cast<PCB_VIA*>( aTrack );
2943
2944 for( PCB_LAYER_ID layer : layers )
2945 {
2946 if( via->FlashLayer( layer ) )
2947 elements[layer][via->GetNetCode()].push_back( via );
2948 }
2949 }
2950 else
2951 {
2952 elements[aTrack->GetLayer()][aTrack->GetNetCode()].push_back( aTrack );
2953 }
2954 } );
2955
2956 std::for_each( m_board->Zones().begin(), m_board->Zones().end(),
2957 [ &elements ]( ZONE* zone )
2958 {
2959 LSEQ zone_layers = zone->GetLayerSet().Seq();
2960
2961 for( PCB_LAYER_ID layer : zone_layers )
2962 elements[layer][zone->GetNetCode()].push_back( zone );
2963 } );
2964
2965 for( BOARD_ITEM* item : m_board->Drawings() )
2966 {
2967 if( BOARD_CONNECTED_ITEM* conn_it = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
2968 elements[conn_it->GetLayer()][conn_it->GetNetCode()].push_back( conn_it );
2969 else
2970 elements[item->GetLayer()][0].push_back( item );
2971 }
2972
2973 for( FOOTPRINT* fp : m_board->Footprints() )
2974 {
2975 for( PCB_FIELD* field : fp->GetFields() )
2976 elements[field->GetLayer()][0].push_back( field );
2977
2978 for( BOARD_ITEM* item : fp->GraphicalItems() )
2979 elements[item->GetLayer()][0].push_back( item );
2980
2981 for( PAD* pad : fp->Pads() )
2982 {
2983 LSEQ pad_layers = pad->GetLayerSet().Seq();
2984
2985 for( PCB_LAYER_ID layer : pad_layers )
2986 {
2987 if( pad->FlashLayer( layer ) )
2988 elements[layer][pad->GetNetCode()].push_back( pad );
2989 }
2990 }
2991 }
2992
2993 for( PCB_LAYER_ID layer : layers )
2994 {
2995 if( m_progressReporter )
2996 m_progressReporter->SetMaxProgress( nets.GetNetCount() * layers.size() );
2997
2998 wxXmlNode* layerNode = appendNode( aStepNode, "LayerFeature" );
2999 addAttribute( layerNode, "layerRef", m_layer_name_map[layer] );
3000
3001 auto process_net = [&] ( int net )
3002 {
3003 std::vector<BOARD_ITEM*>& vec = elements[layer][net];
3004
3005 if( vec.empty() )
3006 return;
3007
3008 std::stable_sort( vec.begin(), vec.end(),
3009 []( BOARD_ITEM* a, BOARD_ITEM* b )
3010 {
3011 if( a->GetParentFootprint() == b->GetParentFootprint() )
3012 return a->Type() < b->Type();
3013
3014 return a->GetParentFootprint() < b->GetParentFootprint();
3015 } );
3016
3017 generateLayerSetNet( layerNode, layer, vec );
3018 };
3019
3020 for( const NETINFO_ITEM* net : nets )
3021 {
3022 if( m_progressReporter )
3023 {
3024 m_progressReporter->Report( wxString::Format( _( "Exporting Layer %s, Net %s" ),
3025 m_board->GetLayerName( layer ),
3026 net->GetNetname() ) );
3027 m_progressReporter->AdvanceProgress();
3028 }
3029
3030 process_net( net->GetNetCode() );
3031 }
3032
3033 if( layerNode->GetChildren() == nullptr )
3034 {
3035 aStepNode->RemoveChild( layerNode );
3036 delete layerNode;
3037 }
3038 }
3039}
3040
3041
3042void PCB_IO_IPC2581::generateLayerSetDrill( wxXmlNode* aLayerNode )
3043{
3044 int hole_count = 1;
3045
3046 for( const auto& [layers, vec] : m_drill_layers )
3047 {
3048 wxXmlNode* layerNode = appendNode( aLayerNode, "LayerFeature" );
3049 layerNode->AddAttribute( "layerRef", genLayersString( layers.first, layers.second, "DRILL" ) );
3050
3051 for( BOARD_ITEM* item : vec )
3052 {
3053 if( item->Type() == PCB_VIA_T )
3054 {
3055 PCB_VIA* via = static_cast<PCB_VIA*>( item );
3056 auto it = m_padstack_dict.find( hash_fp_item( via, 0 ) );
3057
3058 if( it == m_padstack_dict.end() )
3059 {
3060 Report( _( "Via uses unsupported padstack; omitted from drill data." ),
3062 continue;
3063 }
3064
3065 wxXmlNode* padNode = appendNode( layerNode, "Set" );
3066 addAttribute( padNode, "geometry", it->second );
3067
3068 if( via->GetNetCode() > 0 )
3069 addAttribute( padNode, "net", genString( via->GetNetname(), "NET" ) );
3070
3071 wxXmlNode* holeNode = appendNode( padNode, "Hole" );
3072 addAttribute( holeNode, "name", wxString::Format( "H%d", hole_count++ ) );
3073 addAttribute( holeNode, "diameter", floatVal( m_scale * via->GetDrillValue() ) );
3074 addAttribute( holeNode, "platingStatus", "VIA" );
3075 addAttribute( holeNode, "plusTol", "0.0" );
3076 addAttribute( holeNode, "minusTol", "0.0" );
3077 addXY( holeNode, via->GetPosition() );
3078 addBackdrillSpecRefs( holeNode, it->second );
3079 }
3080 else if( item->Type() == PCB_PAD_T )
3081 {
3082 PAD* pad = static_cast<PAD*>( item );
3083 auto it = m_padstack_dict.find( hash_fp_item( pad, 0 ) );
3084
3085 if( it == m_padstack_dict.end() )
3086 {
3087 Report( _( "Pad uses unsupported padstack; hole was omitted from drill data." ),
3089 continue;
3090 }
3091
3092 wxXmlNode* padNode = appendNode( layerNode, "Set" );
3093 addAttribute( padNode, "geometry", it->second );
3094
3095 if( pad->GetNetCode() > 0 )
3096 addAttribute( padNode, "net", genString( pad->GetNetname(), "NET" ) );
3097
3098 wxXmlNode* holeNode = appendNode( padNode, "Hole" );
3099 addAttribute( holeNode, "name", wxString::Format( "H%d", hole_count++ ) );
3100 addAttribute( holeNode, "diameter", floatVal( m_scale * pad->GetDrillSizeX() ) );
3101 addAttribute( holeNode, "platingStatus",
3102 pad->GetAttribute() == PAD_ATTRIB::PTH ? "PLATED" : "NONPLATED" );
3103 addAttribute( holeNode, "plusTol", "0.0" );
3104 addAttribute( holeNode, "minusTol", "0.0" );
3105 addXY( holeNode, pad->GetPosition() );
3106 addBackdrillSpecRefs( holeNode, it->second );
3107 }
3108 }
3109 }
3110
3111 hole_count = 1;
3112
3113 for( const auto& [layers, vec] : m_slot_holes )
3114 {
3115 wxXmlNode* layerNode = appendNode( aLayerNode, "LayerFeature" );
3116 layerNode->AddAttribute( "layerRef", genLayersString( layers.first, layers.second, "SLOT" ) );
3117
3118 for( PAD* pad : vec )
3119 {
3120 wxXmlNode* padNode = appendNode( layerNode, "Set" );
3121
3122 if( pad->GetNetCode() > 0 )
3123 addAttribute( padNode, "net", genString( pad->GetNetname(), "NET" ) );
3124
3125 addSlotCavity( padNode, *pad, wxString::Format( "SLOT%d", hole_count++ ) );
3126 }
3127 }
3128}
3129
3130
3131void PCB_IO_IPC2581::generateLayerSetNet( wxXmlNode* aLayerNode, PCB_LAYER_ID aLayer,
3132 std::vector<BOARD_ITEM*>& aItems )
3133{
3134 auto it = aItems.begin();
3135 wxXmlNode* layerSetNode = appendNode( aLayerNode, "Set" );
3136 wxXmlNode* featureSetNode = appendNode( layerSetNode, "Features" );
3137 wxXmlNode* specialNode = appendNode( featureSetNode, "UserSpecial" );
3138
3139 bool has_via = false;
3140 bool has_pad = false;
3141
3142 wxXmlNode* padSetNode = nullptr;
3143
3144 wxXmlNode* viaSetNode = nullptr;
3145
3146 wxXmlNode* teardropLayerSetNode = nullptr;
3147 wxXmlNode* teardropFeatureSetNode = nullptr;
3148
3149 bool teardrop_warning = false;
3150
3151 if( BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( *it );
3152 IsCopperLayer( aLayer ) && item )
3153 {
3154 if( item->GetNetCode() > 0 )
3155 addAttribute( layerSetNode, "net", genString( item->GetNetname(), "NET" ) );
3156 }
3157
3158 auto add_track =
3159 [&]( PCB_TRACK* track )
3160 {
3161 if( track->Type() == PCB_TRACE_T )
3162 {
3163 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
3164 shape.SetStart( track->GetStart() );
3165 shape.SetEnd( track->GetEnd() );
3166 shape.SetWidth( track->GetWidth() );
3167 addShape( specialNode, shape );
3168 }
3169 else if( track->Type() == PCB_ARC_T )
3170 {
3171 PCB_ARC* arc = static_cast<PCB_ARC*>( track );
3172 PCB_SHAPE shape( nullptr, SHAPE_T::ARC );
3173 shape.SetArcGeometry( arc->GetStart(), arc->GetMid(), arc->GetEnd() );
3174 shape.SetWidth( arc->GetWidth() );
3175 addShape( specialNode, shape );
3176 }
3177 else
3178 {
3179 if( !viaSetNode )
3180 {
3181 if( !has_pad )
3182 {
3183 viaSetNode = layerSetNode;
3184 has_via = true;
3185 }
3186 else
3187 {
3188 viaSetNode = appendNode( layerSetNode, "Set" );
3189
3190 if( track->GetNetCode() > 0 )
3191 addAttribute( viaSetNode, "net", genString( track->GetNetname(), "NET" ) );
3192 }
3193
3194 addAttribute( viaSetNode, "padUsage", "VIA" );
3195 }
3196
3197 addVia( viaSetNode, static_cast<PCB_VIA*>( track ), aLayer );
3198 }
3199 };
3200
3201 auto add_zone =
3202 [&]( ZONE* zone )
3203 {
3204 wxXmlNode* zoneFeatureNode = specialNode;
3205
3206 if( zone->IsTeardropArea() )
3207 {
3208 if( m_version > 'B' )
3209 {
3210 if( !teardropFeatureSetNode )
3211 {
3212 teardropLayerSetNode = appendNode( aLayerNode, "Set" );
3213 addAttribute( teardropLayerSetNode, "geometryUsage", "TEARDROP" );
3214
3215 if( zone->GetNetCode() > 0 )
3216 {
3217 addAttribute( teardropLayerSetNode, "net",
3218 genString( zone->GetNetname(), "NET" ) );
3219 }
3220
3221 wxXmlNode* new_teardrops = appendNode( teardropLayerSetNode, "Features" );
3222 addLocationNode( new_teardrops, 0.0, 0.0 );
3223 teardropFeatureSetNode = appendNode( new_teardrops, "UserSpecial" );
3224 }
3225
3226 zoneFeatureNode = teardropFeatureSetNode;
3227 }
3228 else if( !teardrop_warning )
3229 {
3230 Report( _( "Teardrops are not supported in IPC-2581 revision B; they were exported as zones." ),
3232 teardrop_warning = true;
3233 }
3234 }
3235 else
3236 {
3237 if( FOOTPRINT* fp = zone->GetParentFootprint() )
3238 {
3239 wxXmlNode* tempSetNode = appendNode( aLayerNode, "Set" );
3240 wxString refDes = componentName( zone->GetParentFootprint() );
3241 addAttribute( tempSetNode, "componentRef", refDes );
3242 wxXmlNode* newFeatures = appendNode( tempSetNode, "Features" );
3243 addLocationNode( newFeatures, 0.0, 0.0 );
3244 zoneFeatureNode = appendNode( newFeatures, "UserSpecial" );
3245 }
3246 }
3247
3248 SHAPE_POLY_SET& zone_shape = *zone->GetFilledPolysList( aLayer );
3249
3250 for( int ii = 0; ii < zone_shape.OutlineCount(); ++ii )
3251 addContourNode( zoneFeatureNode, zone_shape, ii );
3252 };
3253
3254 auto add_shape =
3255 [&] ( PCB_SHAPE* shape )
3256 {
3257 FOOTPRINT* fp = shape->GetParentFootprint();
3258
3259 if( fp )
3260 {
3261 wxXmlNode* tempSetNode = appendNode( aLayerNode, "Set" );
3262
3263 if( m_version > 'B' )
3264 addAttribute( tempSetNode, "geometryUsage", "GRAPHIC" );
3265
3266 bool link_to_component = true;
3267
3268 if( m_version == 'B' && isOppositeSideSilk( fp, shape->GetLayer() ) )
3269 link_to_component = false;
3270
3271 if( link_to_component )
3272 addAttribute( tempSetNode, "componentRef", componentName( fp ) );
3273
3274 wxXmlNode* tempFeature = appendNode( tempSetNode, "Features" );
3275 addLocationNode( tempFeature, *shape );
3276
3277 EDA_ANGLE fp_angle = fp->GetOrientation().Normalize();
3278
3279 if( fp_angle != ANGLE_0 )
3280 {
3281 wxXmlNode* xformNode = appendNode( tempFeature, "Xform" );
3282 addAttribute( xformNode, "rotation", floatVal( fp_angle.AsDegrees(), 2 ) );
3283 }
3284
3285 addShape( tempFeature, *shape );
3286 }
3287 else if( shape->GetShape() == SHAPE_T::CIRCLE
3288 || shape->GetShape() == SHAPE_T::RECTANGLE
3289 || shape->GetShape() == SHAPE_T::POLY )
3290 {
3291 wxXmlNode* tempSetNode = appendNode( aLayerNode, "Set" );
3292
3293 if( shape->GetNetCode() > 0 )
3294 addAttribute( tempSetNode, "net", genString( shape->GetNetname(), "NET" ) );
3295
3296 wxXmlNode* tempFeature = appendNode( tempSetNode, "Features" );
3297 addLocationNode( tempFeature, *shape );
3298 addShape( tempFeature, *shape );
3299 }
3300 else
3301 {
3302 addShape( specialNode, *shape );
3303 }
3304 };
3305
3306 auto add_text =
3307 [&] ( BOARD_ITEM* text )
3308 {
3309 EDA_TEXT* text_item;
3310 FOOTPRINT* fp = text->GetParentFootprint();
3311
3312 if( PCB_TEXT* tmp_text = dynamic_cast<PCB_TEXT*>( text ) )
3313 text_item = static_cast<EDA_TEXT*>( tmp_text );
3314 else if( PCB_TEXTBOX* tmp_text = dynamic_cast<PCB_TEXTBOX*>( text ) )
3315 text_item = static_cast<EDA_TEXT*>( tmp_text );
3316
3317 if( !text_item->IsVisible() || text_item->GetShownText( false ).empty() )
3318 return;
3319
3320 wxXmlNode* tempSetNode = appendNode( aLayerNode, "Set" );
3321
3322 if( m_version > 'B' )
3323 addAttribute( tempSetNode, "geometryUsage", "TEXT" );
3324
3325 bool link_to_component = fp != nullptr;
3326
3327 if( m_version == 'B' && fp && isOppositeSideSilk( fp, text->GetLayer() ) )
3328 link_to_component = false;
3329
3330 if( link_to_component )
3331 addAttribute( tempSetNode, "componentRef", componentName( fp ) );
3332
3333 wxXmlNode* nonStandardAttributeNode = appendNode( tempSetNode, "NonstandardAttribute" );
3334 addAttribute( nonStandardAttributeNode, "name", "TEXT" );
3335 addAttribute( nonStandardAttributeNode, "value", text_item->GetShownText( false ) );
3336 addAttribute( nonStandardAttributeNode, "type", "STRING" );
3337
3338 wxXmlNode* tempFeature = appendNode( tempSetNode, "Features" );
3339 addLocationNode( tempFeature, 0.0, 0.0 );
3340
3341 if( text->Type() == PCB_TEXT_T && static_cast<PCB_TEXT*>( text )->IsKnockout() )
3342 addKnockoutText( tempFeature, static_cast<PCB_TEXT*>( text ) );
3343 else
3344 addText( tempFeature, text_item, text->GetFontMetrics() );
3345
3346 if( text->Type() == PCB_TEXTBOX_T )
3347 {
3348 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( text );
3349
3350 if( textbox->IsBorderEnabled() )
3351 addShape( tempFeature, *static_cast<PCB_SHAPE*>( textbox ) );
3352 }
3353 };
3354
3355 auto add_pad =
3356 [&]( PAD* pad )
3357 {
3358 if( !padSetNode )
3359 {
3360 if( !has_via )
3361 {
3362 padSetNode = layerSetNode;
3363 has_pad = true;
3364 }
3365 else
3366 {
3367 padSetNode = appendNode( aLayerNode, "Set" );
3368
3369 if( pad->GetNetCode() > 0 )
3370 addAttribute( padSetNode, "net", genString( pad->GetNetname(), "NET" ) );
3371 }
3372 }
3373
3374 FOOTPRINT* fp = pad->GetParentFootprint();
3375
3376 if( fp && fp->IsFlipped() )
3377 addPad( padSetNode, pad, FlipLayer( aLayer ) );
3378 else
3379 addPad( padSetNode, pad, aLayer );
3380 };
3381
3382 for( BOARD_ITEM* item : aItems )
3383 {
3384 switch( item->Type() )
3385 {
3386 case PCB_TRACE_T:
3387 case PCB_ARC_T:
3388 case PCB_VIA_T:
3389 add_track( static_cast<PCB_TRACK*>( item ) );
3390 break;
3391
3392 case PCB_ZONE_T:
3393 add_zone( static_cast<ZONE*>( item ) );
3394 break;
3395
3396 case PCB_PAD_T:
3397 add_pad( static_cast<PAD*>( item ) );
3398 break;
3399
3400 case PCB_SHAPE_T:
3401 add_shape( static_cast<PCB_SHAPE*>( item ) );
3402 break;
3403
3404 case PCB_TEXT_T:
3405 case PCB_TEXTBOX_T:
3406 case PCB_FIELD_T:
3407 add_text( item );
3408 break;
3409
3410 case PCB_DIMENSION_T:
3411 case PCB_TARGET_T:
3412 case PCB_DIM_ALIGNED_T:
3413 case PCB_DIM_LEADER_T:
3414 case PCB_DIM_CENTER_T:
3415 case PCB_DIM_RADIAL_T:
3417 //TODO: Add support for dimensions
3418 break;
3419
3420 default:
3421 wxLogTrace( traceIpc2581, wxS( "Unhandled type %s" ),
3422 ENUM_MAP<KICAD_T>::Instance().ToString( item->Type() ) );
3423 }
3424 }
3425
3426 if( specialNode->GetChildren() == nullptr )
3427 {
3428 featureSetNode->RemoveChild( specialNode );
3429 delete specialNode;
3430 }
3431
3432 if( featureSetNode->GetChildren() == nullptr )
3433 {
3434 layerSetNode->RemoveChild( featureSetNode );
3435 delete featureSetNode;
3436 }
3437
3438 if( layerSetNode->GetChildren() == nullptr )
3439 {
3440 aLayerNode->RemoveChild( layerSetNode );
3441 delete layerSetNode;
3442 }
3443}
3444
3446{
3447 int hole_count = 1;
3448
3449 for( const auto& [layers, vec] : m_auxilliary_Layers )
3450 {
3451 hole_count = 1;
3452 bool add_node = true;
3453
3454 wxString name;
3455 bool hole = false;
3456
3457 // clang-format off: suggestion is inconsitent
3458 switch( std::get<0>(layers) )
3459 {
3461 name = "COVERING";
3462 break;
3464 name = "PLUGGING";
3465 hole = true;
3466 break;
3468 name = "TENTING";
3469 break;
3471 name = "FILLING";
3472 hole = true;
3473 break;
3475 name = "CAPPING";
3476 hole = true;
3477 break;
3478 default:
3479 add_node = false;
3480 break;
3481 }
3482 // clang-format on: suggestion is inconsitent
3483
3484 if( !add_node )
3485 continue;
3486
3487 wxXmlNode* layerNode = appendNode( aStepNode, "LayerFeature" );
3488 if( std::get<2>( layers ) == UNDEFINED_LAYER )
3489 layerNode->AddAttribute( "layerRef", genLayerString( std::get<1>( layers ), TO_UTF8( name ) ) );
3490 else
3491 layerNode->AddAttribute( "layerRef", genLayersString( std::get<1>( layers ),
3492 std::get<2>( layers ), TO_UTF8( name ) ) );
3493
3494 for( BOARD_ITEM* item : vec )
3495 {
3496 if( item->Type() == PCB_VIA_T )
3497 {
3498 PCB_VIA* via = static_cast<PCB_VIA*>( item );
3499
3500 PCB_SHAPE shape( nullptr, SHAPE_T::CIRCLE );
3501
3502 if( hole )
3503 shape.SetEnd( { KiROUND( via->GetDrillValue() / 2.0 ), 0 } );
3504 else
3505 shape.SetEnd( { KiROUND( via->GetWidth( std::get<1>( layers ) ) / 2.0 ), 0 } );
3506
3507 wxXmlNode* padNode = appendNode( layerNode, "Pad" );
3508 addPadStack( padNode, via );
3509
3510 addLocationNode( padNode, 0.0, 0.0 );
3511 addShape( padNode, shape );
3512 }
3513 }
3514 }
3515}
3516
3517
3519{
3520 if( m_progressReporter )
3521 m_progressReporter->AdvancePhase( _( "Generating BOM section" ) );
3522
3523 wxXmlNode* avl = appendNode( m_xml_root, "Avl" );
3524 addAttribute( avl, "name", "Primary_Vendor_List" );
3525
3526 wxXmlNode* header = appendNode( avl, "AvlHeader" );
3527 addAttribute( header, "title", "BOM" );
3528 addAttribute( header, "source", "KiCad" );
3529 addAttribute( header, "author", "OWNER" );
3530 addAttribute( header, "datetime", wxDateTime::Now().FormatISOCombined() );
3531 addAttribute( header, "version", "1" );
3532
3533 std::set<wxString> unique_parts;
3534 std::map<wxString,wxString> unique_vendors;
3535
3536 for( auto& [fp, name] : m_OEMRef_dict )
3537 {
3538 auto [ it, success ] = unique_parts.insert( name );
3539
3540 if( !success )
3541 continue;
3542
3543 wxXmlNode* part = appendNode( avl, "AvlItem" );
3544 addAttribute( part, "OEMDesignNumber", genString( name, "REF" ) );
3545
3546 PCB_FIELD* nums[2] = { fp->GetField( m_mpn ), fp->GetField( m_distpn ) };
3547 PCB_FIELD* company[2] = { fp->GetField( m_mfg ), nullptr };
3548 wxString company_name[2] = { m_mfg, m_dist };
3549
3550 for ( int ii = 0; ii < 2; ++ii )
3551 {
3552 if( nums[ii] )
3553 {
3554 wxString mpn_name = nums[ii]->GetShownText( false );
3555
3556 if( mpn_name.empty() )
3557 continue;
3558
3559 wxXmlNode* vmpn = appendNode( part, "AvlVmpn" );
3560 addAttribute( vmpn, "qualified", "false" );
3561 addAttribute( vmpn, "chosen", "false" );
3562
3563 wxXmlNode* mpn = appendNode( vmpn, "AvlMpn" );
3564 addAttribute( mpn, "name", mpn_name );
3565
3566 wxXmlNode* vendor = appendNode( vmpn, "AvlVendor" );
3567
3568 wxString name = wxT( "UNKNOWN" );
3569
3570 // If the field resolves, then use that field content unless it is empty
3571 if( !ii && company[ii] )
3572 {
3573 wxString tmp = company[ii]->GetShownText( false );
3574
3575 if( !tmp.empty() )
3576 name = tmp;
3577 }
3578 // If it doesn't resolve but there is content from the dialog, use the static content
3579 else if( !ii && !company_name[ii].empty() )
3580 {
3581 name = company_name[ii];
3582 }
3583 else if( ii && !m_dist.empty() )
3584 {
3585 name = m_dist;
3586 }
3587
3588 auto [vendor_id, inserted] = unique_vendors.emplace(
3589 name,
3590 wxString::Format( "VENDOR_%zu", unique_vendors.size() ) );
3591
3592 addAttribute( vendor, "enterpriseRef", vendor_id->second );
3593
3594 if( inserted )
3595 {
3596 wxXmlNode* new_vendor = new wxXmlNode( wxXML_ELEMENT_NODE, "Enterprise" );
3597 addAttribute( new_vendor, "id", vendor_id->second );
3598 addAttribute( new_vendor, "name", name );
3599 addAttribute( new_vendor, "code", "NONE" );
3600 insertNodeAfter( m_enterpriseNode, new_vendor );
3601 m_enterpriseNode = new_vendor;
3602 }
3603 }
3604 }
3605 }
3606
3607 return avl;
3608}
3609
3610
3611void PCB_IO_IPC2581::SaveBoard( const wxString& aFileName, BOARD* aBoard,
3612 const std::map<std::string, UTF8>* aProperties )
3613{
3614 m_board = aBoard;
3616 m_backdrill_spec_nodes.clear();
3617 m_backdrill_spec_used.clear();
3619 m_cad_header_node = nullptr;
3620 m_layer_name_map.clear();
3621 m_units_str = "MILLIMETER";
3622 m_scale = 1.0 / PCB_IU_PER_MM;
3623 m_sigfig = 6;
3624
3625 if( auto it = aProperties->find( "units" ); it != aProperties->end() )
3626 {
3627 if( it->second == "inch" )
3628 {
3629 m_units_str = "INCH";
3630 m_scale = ( 1.0 / 25.4 ) / PCB_IU_PER_MM;
3631 }
3632 }
3633
3634 if( auto it = aProperties->find( "sigfig" ); it != aProperties->end() )
3635 m_sigfig = std::stoi( it->second );
3636
3637 if( auto it = aProperties->find( "version" ); it != aProperties->end() )
3638 m_version = it->second.c_str()[0];
3639
3640 if( auto it = aProperties->find( "OEMRef" ); it != aProperties->end() )
3641 m_OEMRef = it->second.wx_str();
3642
3643 if( auto it = aProperties->find( "mpn" ); it != aProperties->end() )
3644 m_mpn = it->second.wx_str();
3645
3646 if( auto it = aProperties->find( "mfg" ); it != aProperties->end() )
3647 m_mfg = it->second.wx_str();
3648
3649 if( auto it = aProperties->find( "dist" ); it != aProperties->end() )
3650 m_dist = it->second.wx_str();
3651
3652 if( auto it = aProperties->find( "distpn" ); it != aProperties->end() )
3653 m_distpn = it->second.wx_str();
3654
3655 if( m_version == 'B' )
3656 {
3657 for( char c = 'a'; c <= 'z'; ++c )
3658 m_acceptable_chars.insert( c );
3659
3660 for( char c = 'A'; c <= 'Z'; ++c )
3661 m_acceptable_chars.insert( c );
3662
3663 for( char c = '0'; c <= '9'; ++c )
3664 m_acceptable_chars.insert( c );
3665
3666 // Add special characters
3667 std::string specialChars = "_\\-.+><";
3668
3669 for( char c : specialChars )
3670 m_acceptable_chars.insert( c );
3671 }
3672
3673 m_xml_doc = new wxXmlDocument();
3675
3677
3678 if( m_progressReporter )
3679 {
3680 m_progressReporter->SetNumPhases( 7 );
3681 m_progressReporter->BeginPhase( 1 );
3682 m_progressReporter->Report( _( "Generating logistic section" ) );
3683 }
3684
3687
3688 wxXmlNode* ecad_node = generateEcadSection();
3689 generateBOMSection( ecad_node );
3691
3692 if( m_progressReporter )
3693 {
3694 m_progressReporter->AdvancePhase( _( "Saving file" ) );
3695 }
3696
3697 wxFileOutputStreamWithProgress out_stream( aFileName );
3698 double written_bytes = 0.0;
3699 double last_yield = 0.0;
3700
3701 // This is a rough estimation of the size of the spaces in the file
3702 // We just need to total to be slightly larger than the value of the
3703 // progress bar, so accurately counting spaces is not terribly important
3705
3706 auto update_progress = [&]( size_t aBytes )
3707 {
3708 written_bytes += aBytes;
3709 double percent = written_bytes / static_cast<double>( m_total_bytes );
3710
3711 if( m_progressReporter )
3712 {
3713 // Only update every percent
3714 if( last_yield + 0.01 < percent )
3715 {
3716 last_yield = percent;
3717 m_progressReporter->SetCurrentProgress( percent );
3718 }
3719 }
3720 };
3721
3722 out_stream.SetProgressCallback( update_progress );
3723
3724 if( !m_xml_doc->Save( out_stream ) )
3725 {
3726 Report( _( "Failed to save IPC-2581 data to buffer." ), RPT_SEVERITY_ERROR );
3727 return;
3728 }
3729
3730 size_t size = out_stream.GetSize();
3731}
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:79
virtual bool IsKnockout() const
Definition board_item.h:319
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:655
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:473
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:248
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:224
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:257
bool IsFlipped() const
Definition footprint.h:434
const wxString & GetReference() const
Definition footprint.h:661
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) 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:141
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:342
std::optional< bool > IsPlugged(PCB_LAYER_ID aSide) const
DRILL_PROPS & Drill()
Definition padstack.h:333
std::optional< bool > IsCapped() const
std::optional< bool > IsCovered(PCB_LAYER_ID aSide) const
DRILL_PROPS & SecondaryDrill()
Definition padstack.h:336
POST_MACHINING_PROPS & BackPostMachining()
Definition padstack.h:345
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:161
Definition pad.h:55
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition pad.h:556
int GetSizeX() const
Definition pad.h:281
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:3148
int GetRoundRectCornerRadius(PCB_LAYER_ID aLayer) const
Definition pad.cpp:861
bool FlashLayer(int aLayer, bool aOnlyCheckIfPermitted=false) const
Check to see whether the pad should be flashed on the specific layer.
Definition pad.cpp:410
int GetDrillSizeY() const
Definition pad.h:317
const VECTOR2I & GetDrillSize() const
Definition pad.h:313
PAD_ATTRIB GetAttribute() const
Definition pad.h:559
const wxString & GetNumber() const
Definition pad.h:137
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition pad.h:300
VECTOR2I GetPosition() const override
Definition pad.h:209
int GetDrillSizeX() const
Definition pad.h:315
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:196
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1612
const PADSTACK & Padstack() const
Definition pad.h:329
const VECTOR2I & GetOffset(PCB_LAYER_ID aLayer) const
Definition pad.h:325
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.h:416
PAD_DRILL_SHAPE GetDrillShape() const
Definition pad.h:433
int GetChamferPositions(PCB_LAYER_ID aLayer) const
Definition pad.h:830
double GetChamferRectRatio(PCB_LAYER_ID aLayer) const
Definition pad.h:813
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:1667
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:2464
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition pad.h:265
const VECTOR2I & GetMid() const
Definition pcb_track.h:347
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.
wxString GetShownText(bool aAllowExtraText, int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition pcb_text.cpp:139
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
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:415
@ 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:82
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:84
@ FP_THROUGH_HOLE
Definition footprint.h:81
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
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:259
PCB_LAYER_ID start
Definition padstack.h:262
PCB_LAYER_ID end
Definition padstack.h:263
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
Definition padstack.h:260
std::optional< PAD_DRILL_POST_MACHINING_MODE > mode
Definition padstack.h:273
@ 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