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