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