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