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, int aSigFig ) const
263{
264 wxString str = wxString::FromCDouble( aVal, aSigFig == -1 ? m_sigfig : aSigFig );
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 );
609 finalPoly.Fracture();
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 );
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 );
914 }
915
916 addContourNode( entry_node, shape );
917 break;
918 }
919 default:
920 wxLogError( "Unknown pad type" );
921 break;
922 }
923
924 if( !name.empty() )
925 {
926 m_std_shape_dict.emplace( hash, name );
927 wxXmlNode* shape_node = appendNode( aContentNode, "StandardPrimitiveRef" );
928 addAttribute( shape_node, "id", name );
929 }
930}
931
932
933void PCB_IO_IPC2581::addShape( wxXmlNode* aContentNode, const PCB_SHAPE& aShape )
934{
935 size_t hash = shapeHash( aShape );
936 auto iter = m_user_shape_dict.find( hash );
937 wxString name;
938
939 if( iter != m_user_shape_dict.end() )
940 {
941 wxXmlNode* shape_node = appendNode( aContentNode, "UserPrimitiveRef" );
942 addAttribute( shape_node, "id", iter->second );
943 return;
944 }
945
946 switch( aShape.GetShape() )
947 {
948 case SHAPE_T::CIRCLE:
949 {
950 name = wxString::Format( "UCIRCLE_%zu", m_user_shape_dict.size() + 1 );
951 m_user_shape_dict.emplace( hash, name );
952 int diameter = aShape.GetRadius() * 2.0;
953 int width = aShape.GetStroke().GetWidth();
954 LINE_STYLE dash = aShape.GetStroke().GetLineStyle();
955
956
957 wxXmlNode* entry_node = appendNode( m_shape_user_node, "EntryUser" );
958 addAttribute( entry_node, "id", name );
959 wxXmlNode* special_node = appendNode( entry_node, "UserSpecial" );
960
961 wxXmlNode* circle_node = appendNode( special_node, "Circle" );
962
963 if( aShape.GetFillMode() == FILL_T::NO_FILL )
964 {
965 addAttribute( circle_node, "diameter", floatVal( m_scale * diameter ) );
966 addLineDesc( circle_node, width, dash, true );
967 }
968 else
969 {
970 // IPC2581 does not allow strokes on filled elements
971 addAttribute( circle_node, "diameter", floatVal( m_scale * ( diameter + width ) ) );
972 }
973
974 addFillDesc( circle_node, aShape.GetFillMode() );
975
976 break;
977 }
978
979 case SHAPE_T::RECTANGLE:
980 {
981 name = wxString::Format( "URECT_%zu", m_user_shape_dict.size() + 1 );
982 m_user_shape_dict.emplace( hash, name );
983
984 wxXmlNode* entry_node = appendNode( m_shape_user_node, "EntryUser" );
985 addAttribute( entry_node, "id", name );
986 wxXmlNode* special_node = appendNode( entry_node, "UserSpecial" );
987
988 int width = std::abs( aShape.GetRectangleWidth() );
989 int height = std::abs( aShape.GetRectangleHeight() );
990 int stroke_width = aShape.GetStroke().GetWidth();
991 LINE_STYLE dash = aShape.GetStroke().GetLineStyle();
992
993 wxXmlNode* rect_node = appendNode( special_node, "RectRound" );
994 addLineDesc( rect_node, aShape.GetStroke().GetWidth(), aShape.GetStroke().GetLineStyle(),
995 true );
996
997 if( aShape.GetFillMode() == FILL_T::NO_FILL )
998 {
999 addAttribute( rect_node, "upperRight", "false" );
1000 addAttribute( rect_node, "upperLeft", "false" );
1001 addAttribute( rect_node, "lowerRight", "false" );
1002 addAttribute( rect_node, "lowerLeft", "false" );
1003 }
1004 else
1005 {
1006 addAttribute( rect_node, "upperRight", "true" );
1007 addAttribute( rect_node, "upperLeft", "true" );
1008 addAttribute( rect_node, "lowerRight", "true" );
1009 addAttribute( rect_node, "lowerLeft", "true" );
1010 width += stroke_width;
1011 height += stroke_width;
1012 }
1013
1014 addFillDesc( rect_node, aShape.GetFillMode() );
1015
1016 addAttribute( rect_node, "width", floatVal( m_scale * width ) );
1017 addAttribute( rect_node, "height", floatVal( m_scale * height ) );
1018 addAttribute( rect_node, "radius", floatVal( m_scale * ( stroke_width / 2.0 ) ) );
1019
1020 break;
1021 }
1022
1023 case SHAPE_T::POLY:
1024 {
1025 name = wxString::Format( "UPOLY_%zu", m_user_shape_dict.size() + 1 );
1026 m_user_shape_dict.emplace( hash, name );
1027
1028 wxXmlNode* entry_node = appendNode( m_shape_user_node, "EntryUser" );
1029 addAttribute( entry_node, "id", name );
1030
1031 // If we are stroking a polygon, we need two contours. This is only allowed
1032 // inside a "UserSpecial" shape
1033 wxXmlNode* special_node = appendNode( entry_node, "UserSpecial" );
1034
1035 const SHAPE_POLY_SET& poly_set = aShape.GetPolyShape();
1036
1037 for( int ii = 0; ii < poly_set.OutlineCount(); ++ii )
1038 {
1039 if( aShape.GetFillMode() != FILL_T::NO_FILL )
1040 {
1041 // IPC2581 does not allow strokes on filled elements
1042 addContourNode( special_node, poly_set, ii, FILL_T::FILLED_SHAPE, 0,
1043 LINE_STYLE::SOLID );
1044 }
1045
1046 addContourNode( special_node, poly_set, ii, FILL_T::NO_FILL,
1047 aShape.GetStroke().GetWidth(), aShape.GetStroke().GetLineStyle() );
1048 }
1049
1050 break;
1051 }
1052
1053 case SHAPE_T::ARC:
1054 {
1055 wxXmlNode* arc_node = appendNode( aContentNode, "Arc" );
1056 addXY( arc_node, aShape.GetStart(), "startX", "startY" );
1057 addXY( arc_node, aShape.GetEnd(), "endX", "endY" );
1058 addXY( arc_node, aShape.GetCenter(), "centerX", "centerY" );
1059
1060 //N.B. because our coordinate system is flipped, we need to flip the arc direction
1061 addAttribute( arc_node, "clockwise", !aShape.IsClockwiseArc() ? "true" : "false" );
1062
1063 if( aShape.GetStroke().GetWidth() > 0 )
1064 {
1065 addLineDesc( arc_node, aShape.GetStroke().GetWidth(),
1066 aShape.GetStroke().GetLineStyle(), true );
1067 }
1068
1069 break;
1070 }
1071
1072 case SHAPE_T::BEZIER:
1073 {
1074 wxXmlNode* polyline_node = appendNode( aContentNode, "Polyline" );
1075 std::vector<VECTOR2I> ctrlPoints = { aShape.GetStart(), aShape.GetBezierC1(),
1076 aShape.GetBezierC2(), aShape.GetEnd() };
1077 BEZIER_POLY converter( ctrlPoints );
1078 std::vector<VECTOR2I> points;
1079 converter.GetPoly( points, ARC_HIGH_DEF );
1080
1081 wxXmlNode* point_node = appendNode( polyline_node, "PolyBegin" );
1082 addXY( point_node, points[0] );
1083
1084 for( size_t i = 1; i < points.size(); i++ )
1085 {
1086 wxXmlNode* point_node = appendNode( polyline_node, "PolyStepSegment" );
1087 addXY( point_node, points[i] );
1088 }
1089
1090 if( aShape.GetStroke().GetWidth() > 0 )
1091 {
1092 addLineDesc( polyline_node, aShape.GetStroke().GetWidth(),
1093 aShape.GetStroke().GetLineStyle(), true );
1094 }
1095
1096 break;
1097 }
1098
1099 case SHAPE_T::SEGMENT:
1100 {
1101 wxXmlNode* line_node = appendNode( aContentNode, "Line" );
1102 addXY( line_node, aShape.GetStart(), "startX", "startY" );
1103 addXY( line_node, aShape.GetEnd(), "endX", "endY" );
1104
1105 if( aShape.GetStroke().GetWidth() > 0 )
1106 {
1107 addLineDesc( line_node, aShape.GetStroke().GetWidth(),
1108 aShape.GetStroke().GetLineStyle(), true );
1109 }
1110
1111 break;
1112 }
1113
1114 case SHAPE_T::UNDEFINED:
1115 wxFAIL;
1116 }
1117
1118 if( !name.empty() )
1119 {
1120 wxXmlNode* shape_node = appendNode( aContentNode, "UserPrimitiveRef" );
1121 addAttribute( shape_node, "id", name );
1122 }
1123
1124}
1125
1126
1127void PCB_IO_IPC2581::addSlotCavity( wxXmlNode* aNode, const PAD& aPad, const wxString& aName )
1128{
1129 wxXmlNode* slotNode = appendNode( aNode, "SlotCavity" );
1130 addAttribute( slotNode, "name", aName );
1131 addAttribute( slotNode, "platingStatus", aPad.GetAttribute() == PAD_ATTRIB::PTH ? "PLATED"
1132 : "NONPLATED" );
1133 addAttribute( slotNode, "plusTol", "0.0" );
1134 addAttribute( slotNode, "minusTol", "0.0" );
1135
1136 if( m_version > 'B' )
1137 addLocationNode( slotNode, 0.0, 0.0 );
1138
1139 SHAPE_POLY_SET poly_set;
1140 aPad.GetEffectiveShape( PADSTACK::ALL_LAYERS )->TransformToPolygon( poly_set, 0, ERROR_INSIDE );
1141
1142 addOutlineNode( slotNode, poly_set );
1143}
1144
1145
1147{
1148 wxXmlNode* logisticNode = appendNode( m_xml_root, "LogisticHeader" );
1149
1150 wxXmlNode* roleNode = appendNode( logisticNode, "Role" );
1151 addAttribute( roleNode, "id", "Owner" );
1152 addAttribute( roleNode, "roleFunction", "SENDER" );
1153
1154 m_enterpriseNode = appendNode( logisticNode, "Enterprise" );
1155 addAttribute( m_enterpriseNode, "id", "UNKNOWN" );
1156 addAttribute( m_enterpriseNode, "code", "NONE" );
1157
1158 wxXmlNode* personNode = appendNode( logisticNode, "Person" );
1159 addAttribute( personNode, "name", "UNKNOWN" );
1160 addAttribute( personNode, "enterpriseRef", "UNKNOWN" );
1161 addAttribute( personNode, "roleRef", "Owner" );
1162
1163 return logisticNode;
1164}
1165
1166
1168{
1169 if( m_progressReporter )
1170 m_progressReporter->AdvancePhase( _( "Generating history section" ) );
1171
1172 wxXmlNode* historyNode = appendNode( m_xml_root, "HistoryRecord" );
1173 addAttribute( historyNode, "number", "1" );
1174 addAttribute( historyNode, "origination", wxDateTime::Now().FormatISOCombined() );
1175 addAttribute( historyNode, "software", "KiCad EDA" );
1176 addAttribute( historyNode, "lastChange", wxDateTime::Now().FormatISOCombined() );
1177
1178 wxXmlNode* fileRevisionNode = appendNode( historyNode, "FileRevision" );
1179 addAttribute( fileRevisionNode, "fileRevisionId", "1" );
1180 addAttribute( fileRevisionNode, "comment", "NO COMMENT" );
1181 addAttribute( fileRevisionNode, "label", "NO LABEL" );
1182
1183 wxXmlNode* softwarePackageNode = appendNode( fileRevisionNode, "SoftwarePackage" );
1184 addAttribute( softwarePackageNode, "name", "KiCad" );
1185 addAttribute( softwarePackageNode, "revision", GetMajorMinorPatchVersion() );
1186 addAttribute( softwarePackageNode, "vendor", "KiCad EDA" );
1187
1188 wxXmlNode* certificationNode = appendNode( softwarePackageNode, "Certification" );
1189 addAttribute( certificationNode, "certificationStatus", "SELFTEST" );
1190
1191 return historyNode;
1192}
1193
1194
1195wxXmlNode* PCB_IO_IPC2581::generateBOMSection( wxXmlNode* aEcadNode )
1196{
1197 if( m_progressReporter )
1198 m_progressReporter->AdvancePhase( _( "Generating BOM section" ) );
1199
1200 struct REFDES
1201 {
1202 wxString m_name;
1203 wxString m_pkg;
1204 bool m_populate;
1205 wxString m_layer;
1206 };
1207
1208 struct BOM_ENTRY
1209 {
1210 BOM_ENTRY()
1211 {
1212 m_refdes = new std::vector<REFDES>();
1213 m_props = new std::map<wxString, wxString>();
1214 m_count = 0;
1215 m_pads = 0;
1216 }
1217
1218 ~BOM_ENTRY()
1219 {
1220 delete m_refdes;
1221 delete m_props;
1222 }
1223
1224 wxString m_OEMDesignRef; // String combining LIB+FP+VALUE
1225 int m_count;
1226 int m_pads;
1227 wxString m_type;
1228 std::vector<REFDES>* m_refdes;
1229 std::map<wxString, wxString>* m_props;
1230 };
1231
1232 std::set<std::unique_ptr<struct BOM_ENTRY>,
1233 std::function<bool( const std::unique_ptr<struct BOM_ENTRY>&,
1234 const std::unique_ptr<struct BOM_ENTRY>& )>> bom_entries(
1235 []( const std::unique_ptr<struct BOM_ENTRY>& a,
1236 const std::unique_ptr<struct BOM_ENTRY>& b )
1237 {
1238 return a->m_OEMDesignRef < b->m_OEMDesignRef;
1239 } );
1240
1241 for( FOOTPRINT* fp_it : m_board->Footprints() )
1242 {
1243 std::unique_ptr<FOOTPRINT> fp( static_cast<FOOTPRINT*>( fp_it->Clone() ) );
1244 fp->SetParentGroup( nullptr );
1245 fp->SetPosition( {0, 0} );
1246
1247 if( fp->GetLayer() != F_Cu )
1248 fp->Flip( fp->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
1249
1250 fp->SetOrientation( ANGLE_0 );
1251
1252 size_t hash = hash_fp_item( fp.get(), HASH_POS | REL_COORD );
1253 auto iter = m_footprint_dict.find( hash );
1254
1255 if( iter == m_footprint_dict.end() )
1256 {
1257 wxLogError( "Footprint %s not found in dictionary",
1258 fp->GetFPID().GetLibItemName().wx_str() );
1259 continue;
1260 }
1261
1262 auto entry = std::make_unique<struct BOM_ENTRY>();
1263
1266 if( auto it = m_OEMRef_dict.find( fp_it ); it != m_OEMRef_dict.end() )
1267 {
1268 entry->m_OEMDesignRef = it->second;
1269 }
1270 else
1271 {
1272 wxLogError( "Footprint %s not found in OEMRef dictionary",
1273 fp->GetFPID().GetLibItemName().wx_str() );
1274 }
1275
1276 entry->m_OEMDesignRef = genString( entry->m_OEMDesignRef, "REF" );
1277 entry->m_count = 1;
1278 entry->m_pads = fp->GetPadCount();
1279
1280 // TODO: The options are "ELECTRICAL", "MECHANICAL", "PROGRAMMABLE", "DOCUMENT", "MATERIAL"
1281 // We need to figure out how to determine this.
1282 if( entry->m_pads == 0 || fp_it->GetAttributes() & FP_EXCLUDE_FROM_BOM )
1283 entry->m_type = "DOCUMENT";
1284 else
1285 entry->m_type = "ELECTRICAL";
1286
1287 auto[ bom_iter, inserted ] = bom_entries.insert( std::move( entry ) );
1288
1289 if( !inserted )
1290 ( *bom_iter )->m_count++;
1291
1292 REFDES refdes;
1293 refdes.m_name = componentName( fp_it );
1294 refdes.m_pkg = fp->GetFPID().GetLibItemName().wx_str();
1295 refdes.m_populate = !fp->IsDNP() && !( fp->GetAttributes() & FP_EXCLUDE_FROM_BOM );
1296 refdes.m_layer = m_layer_name_map[fp_it->GetLayer()];
1297
1298 ( *bom_iter )->m_refdes->push_back( refdes );
1299
1300 // TODO: This amalgamates all the properties from all the footprints. We need to decide
1301 // if we want to group footprints by their properties
1302 for( PCB_FIELD* prop : fp->GetFields() )
1303 {
1304 // We don't need ref, footprint or datasheet in the BOM characteristics. Just value
1305 // and any additional fields the user has added. Ref and footprint are captured above.
1306 if( prop->IsMandatoryField() && !prop->IsValue() )
1307 continue;
1308
1309 ( *bom_iter )->m_props->emplace( prop->GetName(), prop->GetText() );
1310 }
1311 }
1312
1313 if( bom_entries.empty() )
1314 return nullptr;
1315
1316 wxFileName fn( m_board->GetFileName() );
1317
1318 wxXmlNode* bomNode = new wxXmlNode( wxXML_ELEMENT_NODE, "Bom" );
1319 m_xml_root->InsertChild( bomNode, aEcadNode );
1320 addAttribute( bomNode, "name", genString( fn.GetName(), "BOM" ) );
1321
1322 wxXmlNode* bomHeaderNode = appendNode( bomNode, "BomHeader" );
1323 addAttribute( bomHeaderNode, "revision", "1.0" );
1324 addAttribute( bomHeaderNode, "assembly", genString( fn.GetName() ) );
1325
1326 wxXmlNode* stepRefNode = appendNode( bomHeaderNode, "StepRef" );
1327 addAttribute( stepRefNode, "name", genString( fn.GetName(), "BOARD" ) );
1328
1329 for( const auto& entry : bom_entries )
1330 {
1331 wxXmlNode* bomEntryNode = appendNode( bomNode, "BomItem" );
1332 addAttribute( bomEntryNode, "OEMDesignNumberRef", entry->m_OEMDesignRef );
1333 addAttribute( bomEntryNode, "quantity", wxString::Format( "%d", entry->m_count ) );
1334 addAttribute( bomEntryNode, "pinCount", wxString::Format( "%d", entry->m_pads ) );
1335 addAttribute( bomEntryNode, "category", entry->m_type );
1336
1337 for( const REFDES& refdes : *( entry->m_refdes ) )
1338 {
1339 wxXmlNode* refdesNode = appendNode( bomEntryNode, "RefDes" );
1340 addAttribute( refdesNode, "name", refdes.m_name );
1341 addAttribute( refdesNode, "packageRef", genString( refdes.m_pkg, "PKG" ) );
1342 addAttribute( refdesNode, "populate", refdes.m_populate ? "true" : "false" );
1343 addAttribute( refdesNode, "layerRef", refdes.m_layer );
1344 }
1345
1346 wxXmlNode* characteristicsNode = appendNode( bomEntryNode, "Characteristics" );
1347 addAttribute( characteristicsNode, "category", entry->m_type );
1348
1349 for( const auto& prop : *( entry->m_props ) )
1350 {
1351 wxXmlNode* textualDefNode = appendNode( characteristicsNode, "Textual" );
1352 addAttribute( textualDefNode, "definitionSource", "KICAD" );
1353 addAttribute( textualDefNode, "textualCharacteristicName", prop.first );
1354 addAttribute( textualDefNode, "textualCharacteristicValue", prop.second );
1355 }
1356 }
1357
1358 return bomNode;
1359}
1360
1361
1363{
1364 if( m_progressReporter )
1365 m_progressReporter->AdvancePhase( _( "Generating CAD data" ) );
1366
1367 wxXmlNode* ecadNode = appendNode( m_xml_root, "Ecad" );
1368 addAttribute( ecadNode, "name", "Design" );
1369
1370 addCadHeader( ecadNode );
1371
1372 wxXmlNode* cadDataNode = appendNode( ecadNode, "CadData" );
1373 generateCadLayers( cadDataNode );
1374 generateDrillLayers( cadDataNode);
1375 generateStackup( cadDataNode );
1376 generateStepSection( cadDataNode );
1377
1378 return ecadNode;
1379}
1380
1381
1382void PCB_IO_IPC2581::generateCadSpecs( wxXmlNode* aCadLayerNode )
1383{
1385 BOARD_STACKUP& stackup = dsnSettings.GetStackupDescriptor();
1386 stackup.SynchronizeWithBoard( &dsnSettings );
1387
1388 std::vector<BOARD_STACKUP_ITEM*> layers = stackup.GetList();
1389 std::set<PCB_LAYER_ID> added_layers;
1390
1391 for( int i = 0; i < stackup.GetCount(); i++ )
1392 {
1393 BOARD_STACKUP_ITEM* stackup_item = layers.at( i );
1394
1395 for( int sublayer_id = 0; sublayer_id < stackup_item->GetSublayersCount(); sublayer_id++ )
1396 {
1397 wxString ly_name = stackup_item->GetLayerName();
1398
1399 if( ly_name.IsEmpty() )
1400 {
1401 if( IsValidLayer( stackup_item->GetBrdLayerId() ) )
1402 ly_name = m_board->GetLayerName( stackup_item->GetBrdLayerId() );
1403
1404 if( ly_name.IsEmpty() && stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1405 {
1406 ly_name = wxString::Format( "DIELECTRIC_%d", stackup_item->GetDielectricLayerId() );
1407
1408 if( sublayer_id > 0 )
1409 ly_name += wxString::Format( "_%d", sublayer_id );
1410 }
1411 }
1412
1413 ly_name = genString( ly_name, "SPEC_LAYER" );
1414
1415 wxXmlNode* specNode = appendNode( aCadLayerNode, "Spec" );
1416 addAttribute( specNode, "name", ly_name );
1417 wxXmlNode* generalNode = appendNode( specNode, "General" );
1418 addAttribute( generalNode, "type", "MATERIAL" );
1419 wxXmlNode* propertyNode = appendNode( generalNode, "Property" );
1420
1421 switch ( stackup_item->GetType() )
1422 {
1424 {
1425 addAttribute( propertyNode, "text", "COPPER" );
1426 wxXmlNode* conductorNode = appendNode( specNode, "Conductor" );
1427 addAttribute( conductorNode, "type", "CONDUCTIVITY" );
1428 propertyNode = appendNode( conductorNode, "Property" );
1429 addAttribute( propertyNode, "unit", wxT( "SIEMENS/M" ) );
1430 addAttribute( propertyNode, "value", wxT( "5.959E7" ) );
1431 break;
1432 }
1434 {
1435 addAttribute( propertyNode, "text", stackup_item->GetMaterial() );
1436 propertyNode = appendNode( generalNode, "Property" );
1437 addAttribute( propertyNode, "text", wxString::Format( "Type : %s",
1438 stackup_item->GetTypeName() ) );
1439 wxXmlNode* dielectricNode = appendNode( specNode, "Dielectric" );
1440 addAttribute( dielectricNode, "type", "DIELECTRIC_CONSTANT" );
1441 propertyNode = appendNode( dielectricNode, "Property" );
1442 addAttribute( propertyNode, "value",
1443 floatVal( stackup_item->GetEpsilonR( sublayer_id ) ) );
1444 dielectricNode = appendNode( specNode, "Dielectric" );
1445 addAttribute( dielectricNode, "type", "LOSS_TANGENT" );
1446 propertyNode = appendNode( dielectricNode, "Property" );
1447 addAttribute( propertyNode, "value",
1448 floatVal( stackup_item->GetLossTangent( sublayer_id ) ) );
1449 break;
1450 }
1452 addAttribute( propertyNode, "text", stackup_item->GetTypeName() );
1453 propertyNode = appendNode( generalNode, "Property" );
1454 addAttribute( propertyNode, "text", wxString::Format( "Color : %s",
1455 stackup_item->GetColor() ) );
1456 propertyNode = appendNode( generalNode, "Property" );
1457 addAttribute( propertyNode, "text", wxString::Format( "Type : %s",
1458 stackup_item->GetTypeName() ) );
1459 break;
1461 addAttribute( propertyNode, "text", "SOLDERMASK" );
1462 propertyNode = appendNode( generalNode, "Property" );
1463 addAttribute( propertyNode, "text", wxString::Format( "Color : %s",
1464 stackup_item->GetColor() ) );
1465 propertyNode = appendNode( generalNode, "Property" );
1466 addAttribute( propertyNode, "text", wxString::Format( "Type : %s",
1467 stackup_item->GetTypeName() ) );
1468 break;
1469 default:
1470 break;
1471 }
1472 }
1473 }
1474}
1475
1476
1477void PCB_IO_IPC2581::addCadHeader( wxXmlNode* aEcadNode )
1478{
1479 wxXmlNode* cadHeaderNode = appendNode( aEcadNode, "CadHeader" );
1480 addAttribute( cadHeaderNode, "units", m_units_str );
1481
1482 generateCadSpecs( cadHeaderNode );
1483}
1484
1485
1487{
1488 return ( aLayer >= F_Cu && aLayer <= User_9 ) || aLayer == UNDEFINED_LAYER;
1489}
1490
1491
1492void PCB_IO_IPC2581::addLayerAttributes( wxXmlNode* aNode, PCB_LAYER_ID aLayer )
1493{
1494 switch( aLayer )
1495 {
1496 case F_Adhes:
1497 case B_Adhes:
1498 addAttribute( aNode, "layerFunction", "GLUE" );
1499 addAttribute( aNode, "polarity", "POSITIVE" );
1500 addAttribute( aNode, "side", aLayer == F_Adhes ? "TOP" : "BOTTOM" );
1501 break;
1502 case F_Paste:
1503 case B_Paste:
1504 addAttribute( aNode, "layerFunction", "SOLDERPASTE" );
1505 addAttribute( aNode, "polarity", "POSITIVE" );
1506 addAttribute( aNode, "side", aLayer == F_Paste ? "TOP" : "BOTTOM" );
1507 break;
1508 case F_SilkS:
1509 case B_SilkS:
1510 addAttribute( aNode, "layerFunction", "SILKSCREEN" );
1511 addAttribute( aNode, "polarity", "POSITIVE" );
1512 addAttribute( aNode, "side", aLayer == F_SilkS ? "TOP" : "BOTTOM" );
1513 break;
1514 case F_Mask:
1515 case B_Mask:
1516 addAttribute( aNode, "layerFunction", "SOLDERMASK" );
1517 addAttribute( aNode, "polarity", "POSITIVE" );
1518 addAttribute( aNode, "side", aLayer == F_Mask ? "TOP" : "BOTTOM" );
1519 break;
1520 case Edge_Cuts:
1521 addAttribute( aNode, "layerFunction", "BOARD_OUTLINE" );
1522 addAttribute( aNode, "polarity", "POSITIVE" );
1523 addAttribute( aNode, "side", "ALL" );
1524 break;
1525 case B_CrtYd:
1526 case F_CrtYd:
1527 addAttribute( aNode, "layerFunction", "COURTYARD" );
1528 addAttribute( aNode, "polarity", "POSITIVE" );
1529 addAttribute( aNode, "side", aLayer == F_CrtYd ? "TOP" : "BOTTOM" );
1530 break;
1531 case B_Fab:
1532 case F_Fab:
1533 addAttribute( aNode, "layerFunction", "ASSEMBLY" );
1534 addAttribute( aNode, "polarity", "POSITIVE" );
1535 addAttribute( aNode, "side", aLayer == F_Fab ? "TOP" : "BOTTOM" );
1536 break;
1537 case Dwgs_User:
1538 case Cmts_User:
1539 case Eco1_User:
1540 case Eco2_User:
1541 case Margin:
1542 case User_1:
1543 case User_2:
1544 case User_3:
1545 case User_4:
1546 case User_5:
1547 case User_6:
1548 case User_7:
1549 case User_8:
1550 case User_9:
1551 addAttribute( aNode, "layerFunction", "DOCUMENT" );
1552 addAttribute( aNode, "polarity", "POSITIVE" );
1553 addAttribute( aNode, "side", "NONE" );
1554 break;
1555
1556 default:
1557 if( IsCopperLayer( aLayer ) )
1558 {
1559 addAttribute( aNode, "layerFunction", "CONDUCTOR" );
1560 addAttribute( aNode, "polarity", "POSITIVE" );
1561 addAttribute( aNode, "side",
1562 aLayer == F_Cu ? "TOP"
1563 : aLayer == B_Cu ? "BOTTOM"
1564 : "INTERNAL" );
1565 }
1566
1567 break; // Do not handle other layers
1568 }
1569}
1570
1571
1572void PCB_IO_IPC2581::generateStackup( wxXmlNode* aCadLayerNode )
1573{
1575 BOARD_STACKUP& stackup = dsnSettings.GetStackupDescriptor();
1576 stackup.SynchronizeWithBoard( &dsnSettings );
1577
1578 wxXmlNode* stackupNode = appendNode( aCadLayerNode, "Stackup" );
1579 addAttribute( stackupNode, "name", "Primary_Stackup" );
1580 addAttribute( stackupNode, "overallThickness", floatVal( m_scale * stackup.BuildBoardThicknessFromStackup() ) );
1581 addAttribute( stackupNode, "tolPlus", "0.0" );
1582 addAttribute( stackupNode, "tolMinus", "0.0" );
1583 addAttribute( stackupNode, "whereMeasured", "MASK" );
1584
1585 if( m_version > 'B' )
1586 addAttribute( stackupNode, "stackupStatus", "PROPOSED" );
1587
1588 wxXmlNode* stackupGroup = appendNode( stackupNode, "StackupGroup" );
1589 addAttribute( stackupGroup, "name", "Primary_Stackup_Group" );
1590 addAttribute( stackupGroup, "thickness", floatVal( m_scale * stackup.BuildBoardThicknessFromStackup() ) );
1591 addAttribute( stackupGroup, "tolPlus", "0.0" );
1592 addAttribute( stackupGroup, "tolMinus", "0.0" );
1593
1594 std::vector<BOARD_STACKUP_ITEM*> layers = stackup.GetList();
1595 std::set<PCB_LAYER_ID> added_layers;
1596
1597 for( int i = 0; i < stackup.GetCount(); i++ )
1598 {
1599 BOARD_STACKUP_ITEM* stackup_item = layers.at( i );
1600
1601 for( int sublayer_id = 0; sublayer_id < stackup_item->GetSublayersCount(); sublayer_id++ )
1602 {
1603
1604 wxXmlNode* stackupLayer = appendNode( stackupGroup, "StackupLayer" );
1605 wxString ly_name = stackup_item->GetLayerName();
1606
1607 if( ly_name.IsEmpty() )
1608 {
1609 if( IsValidLayer( stackup_item->GetBrdLayerId() ) )
1610 ly_name = m_board->GetLayerName( stackup_item->GetBrdLayerId() );
1611
1612 if( ly_name.IsEmpty() && stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1613 {
1614 ly_name = wxString::Format( "DIELECTRIC_%d", stackup_item->GetDielectricLayerId() );
1615
1616 if( sublayer_id > 0 )
1617 ly_name += wxString::Format( "_%d", sublayer_id );
1618 }
1619 }
1620
1621 ly_name = genString( ly_name, "LAYER" );
1622
1623 addAttribute( stackupLayer, "layerOrGroupRef", ly_name );
1624 addAttribute( stackupLayer, "thickness", floatVal( m_scale * stackup_item->GetThickness() ) );
1625 addAttribute( stackupLayer, "tolPlus", "0.0" );
1626 addAttribute( stackupLayer, "tolMinus", "0.0" );
1627 addAttribute( stackupLayer, "sequence", wxString::Format( "%d", i ) );
1628
1629 wxXmlNode* specLayerNode = appendNode( stackupLayer, "SpecRef" );
1630 addAttribute( specLayerNode, "id", wxString::Format( "SPEC_%s", ly_name ) );
1631 }
1632 }
1633}
1634
1635
1636void PCB_IO_IPC2581::generateCadLayers( wxXmlNode* aCadLayerNode )
1637{
1638
1640 BOARD_STACKUP& stackup = dsnSettings.GetStackupDescriptor();
1641 stackup.SynchronizeWithBoard( &dsnSettings );
1642
1643 std::vector<BOARD_STACKUP_ITEM*> layers = stackup.GetList();
1644 std::set<PCB_LAYER_ID> added_layers;
1645
1646 for( int i = 0; i < stackup.GetCount(); i++ )
1647 {
1648 BOARD_STACKUP_ITEM* stackup_item = layers.at( i );
1649
1650 if( !isValidLayerFor2581( stackup_item->GetBrdLayerId() ) )
1651 continue;
1652
1653 for( int sublayer_id = 0; sublayer_id < stackup_item->GetSublayersCount(); sublayer_id++ )
1654 {
1655 wxXmlNode* cadLayerNode = appendNode( aCadLayerNode, "Layer" );
1656 wxString ly_name = stackup_item->GetLayerName();
1657
1658 if( ly_name.IsEmpty() )
1659 {
1660
1661 if( IsValidLayer( stackup_item->GetBrdLayerId() ) )
1662 ly_name = m_board->GetLayerName( stackup_item->GetBrdLayerId() );
1663
1664 if( ly_name.IsEmpty() && stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1665 {
1666 ly_name = wxString::Format( "DIELECTRIC_%d", stackup_item->GetDielectricLayerId() );
1667
1668 if( sublayer_id > 0 )
1669 ly_name += wxString::Format( "_%d", sublayer_id );
1670 }
1671 }
1672
1673 ly_name = genString( ly_name, "LAYER" );
1674
1675 addAttribute( cadLayerNode, "name", ly_name );
1676
1677 if( stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
1678 {
1679 if( stackup_item->GetTypeName() == KEY_CORE )
1680 addAttribute( cadLayerNode, "layerFunction", "DIELCORE" );
1681 else
1682 addAttribute( cadLayerNode, "layerFunction", "DIELPREG" );
1683
1684 addAttribute( cadLayerNode, "polarity", "POSITIVE" );
1685 addAttribute( cadLayerNode, "side", "INTERNAL" );
1686 continue;
1687 }
1688 else
1689 {
1690 added_layers.insert( stackup_item->GetBrdLayerId() );
1691 addLayerAttributes( cadLayerNode, stackup_item->GetBrdLayerId() );
1692 m_layer_name_map.emplace( stackup_item->GetBrdLayerId(), ly_name );
1693 }
1694 }
1695 }
1696
1697 LSEQ layer_seq = m_board->GetEnabledLayers().Seq();
1698
1699 for( PCB_LAYER_ID layer : layer_seq )
1700 {
1701 if( added_layers.find( layer ) != added_layers.end() || !isValidLayerFor2581( layer ) )
1702 continue;
1703
1704 wxString ly_name = genLayerString( layer, "LAYER" );
1705 m_layer_name_map.emplace( layer, ly_name );
1706 added_layers.insert( layer );
1707 wxXmlNode* cadLayerNode = appendNode( aCadLayerNode, "Layer" );
1708 addAttribute( cadLayerNode, "name", ly_name );
1709
1710 addLayerAttributes( cadLayerNode, layer );
1711 }
1712}
1713
1714
1715void PCB_IO_IPC2581::generateDrillLayers( wxXmlNode* aCadLayerNode )
1716{
1717 for( BOARD_ITEM* item : m_board->Tracks() )
1718 {
1719 if( item->Type() == PCB_VIA_T )
1720 {
1721 PCB_VIA* via = static_cast<PCB_VIA*>( item );
1722 m_drill_layers[std::make_pair( via->TopLayer(), via->BottomLayer() )].push_back( via );
1723 }
1724 }
1725
1726 for( FOOTPRINT* fp : m_board->Footprints() )
1727 {
1728 for( PAD* pad : fp->Pads() )
1729 {
1730 if( pad->HasDrilledHole() )
1731 m_drill_layers[std::make_pair( F_Cu, B_Cu )].push_back( pad );
1732 else if( pad->HasHole() )
1733 m_slot_holes[std::make_pair( F_Cu, B_Cu )].push_back( pad );
1734 }
1735 }
1736
1737 for( const auto& [layers, vec] : m_drill_layers )
1738 {
1739 wxXmlNode* drillNode = appendNode( aCadLayerNode, "Layer" );
1740 drillNode->AddAttribute( "name", genLayersString( layers.first, layers.second, "DRILL" ) );
1741 addAttribute( drillNode, "layerFunction", "DRILL" );
1742 addAttribute( drillNode, "polarity", "POSITIVE" );
1743 addAttribute( drillNode, "side", "ALL" );
1744
1745 wxXmlNode* spanNode = appendNode( drillNode, "Span" );
1746 addAttribute( spanNode, "fromLayer", genLayerString( layers.first, "LAYER" ) );
1747 addAttribute( spanNode, "toLayer", genLayerString( layers.second, "LAYER" ) );
1748 }
1749
1750 for( const auto& [layers, vec] : m_slot_holes )
1751 {
1752 wxXmlNode* drillNode = appendNode( aCadLayerNode, "Layer" );
1753 drillNode->AddAttribute( "name", genLayersString( layers.first, layers.second, "SLOT" ) );
1754
1755 addAttribute( drillNode, "layerFunction", "ROUT" );
1756 addAttribute( drillNode, "polarity", "POSITIVE" );
1757 addAttribute( drillNode, "side", "ALL" );
1758
1759 wxXmlNode* spanNode = appendNode( drillNode, "Span" );
1760 addAttribute( spanNode, "fromLayer", genLayerString( layers.first, "LAYER" ) );
1761 addAttribute( spanNode, "toLayer", genLayerString( layers.second, "LAYER" ) );
1762 }
1763}
1764
1765
1766void PCB_IO_IPC2581::generateStepSection( wxXmlNode* aCadNode )
1767{
1768 wxXmlNode* stepNode = appendNode( aCadNode, "Step" );
1769 wxFileName fn( m_board->GetFileName() );
1770 addAttribute( stepNode, "name", genString( fn.GetName(), "BOARD" ) );
1771
1772 if( m_version > 'B' )
1773 addAttribute( stepNode, "type", "BOARD" );
1774
1775 wxXmlNode* datumNode = appendNode( stepNode, "Datum" );
1776 addAttribute( datumNode, "x", "0.0" );
1777 addAttribute( datumNode, "y", "0.0" );
1778
1779 generateProfile( stepNode );
1780 generateComponents( stepNode );
1781
1782 m_last_padstack = insertNode( stepNode, "NonstandardAttribute" );
1783 addAttribute( m_last_padstack, "name", "FOOTPRINT_COUNT" );
1784 addAttribute( m_last_padstack, "type", "INTEGER" );
1785 addAttribute( m_last_padstack, "value",
1786 wxString::Format( "%zu", m_board->Footprints().size() ) );
1787
1788 generateLayerFeatures( stepNode );
1789 generateLayerSetDrill( stepNode );
1790}
1791
1792
1793void PCB_IO_IPC2581::addPad( wxXmlNode* aContentNode, const PAD* aPad, PCB_LAYER_ID aLayer )
1794{
1795 wxXmlNode* padNode = appendNode( aContentNode, "Pad" );
1796 FOOTPRINT* fp = aPad->GetParentFootprint();
1797
1798 addPadStack( padNode, aPad );
1799
1800 if( aPad->GetOrientation() != ANGLE_0 )
1801 {
1802 wxXmlNode* xformNode = appendNode( padNode, "Xform" );
1803 xformNode->AddAttribute( "rotation",
1804 floatVal( aPad->GetOrientation().Normalize().AsDegrees() ) );
1805
1806 if( fp && fp->IsFlipped() )
1807 addAttribute( xformNode, "mirror", "true" );
1808 }
1809
1810 addLocationNode( padNode, *aPad, false );
1811 addShape( padNode, *aPad, aLayer );
1812
1813 if( fp )
1814 {
1815 wxXmlNode* pinRefNode = appendNode( padNode, "PinRef" );
1816
1817 addAttribute( pinRefNode, "componentRef", componentName( fp ) );
1818 addAttribute( pinRefNode, "pin", pinName( aPad ) );
1819 }
1820}
1821
1822
1823void PCB_IO_IPC2581::addVia( wxXmlNode* aContentNode, const PCB_VIA* aVia, PCB_LAYER_ID aLayer )
1824{
1825 if( !aVia->FlashLayer( aLayer ) )
1826 return;
1827
1828 wxXmlNode* padNode = appendNode( aContentNode, "Pad" );
1829
1830 addPadStack( padNode, aVia );
1831 addLocationNode( padNode, aVia->GetPosition().x, aVia->GetPosition().y );
1832
1833 PAD dummy( nullptr );
1834 int hole = aVia->GetDrillValue();
1835 dummy.SetDrillSize( VECTOR2I( hole, hole ) );
1836 dummy.SetPosition( aVia->GetStart() );
1837 dummy.SetSize( aLayer, VECTOR2I( aVia->GetWidth( aLayer ), aVia->GetWidth( aLayer ) ) );
1838
1839 addShape( padNode, dummy, aLayer );
1840}
1841
1842
1843void PCB_IO_IPC2581::addPadStack( wxXmlNode* aPadNode, const PAD* aPad )
1844{
1845 size_t hash = hash_fp_item( aPad, 0 );
1846 wxString name = wxString::Format( "PADSTACK_%zu", m_padstack_dict.size() + 1 );
1847 auto [ th_pair, success ] = m_padstack_dict.emplace( hash, name );
1848
1849 addAttribute( aPadNode, "padstackDefRef", th_pair->second );
1850
1851 // If we did not insert a new padstack, then we have already added it to the XML
1852 // and we don't need to add it again.
1853 if( !success )
1854 return;
1855
1856 wxXmlNode* padStackDefNode = new wxXmlNode( wxXML_ELEMENT_NODE, "PadStackDef" );
1857 addAttribute( padStackDefNode, "name", name );
1858 m_padstacks.push_back( padStackDefNode );
1859
1860 if( m_last_padstack )
1861 {
1862 insertNodeAfter( m_last_padstack, padStackDefNode );
1863 m_last_padstack = padStackDefNode;
1864 }
1865
1866 // Only handle round holes here because IPC2581 does not support non-round holes
1867 // These will be handled in a slot layer
1868 if( aPad->HasDrilledHole() )
1869 {
1870 wxXmlNode* padStackHoleNode = appendNode( padStackDefNode, "PadstackHoleDef" );
1871 padStackHoleNode->AddAttribute( "name",
1872 wxString::Format( "%s%d_%d",
1873 aPad->GetAttribute() == PAD_ATTRIB::PTH ? "PTH" : "NPTH",
1874 aPad->GetDrillSizeX(), aPad->GetDrillSizeY() ) );
1875
1876 addAttribute( padStackHoleNode, "diameter", floatVal( m_scale * aPad->GetDrillSizeX() ) );
1877 addAttribute( padStackHoleNode, "platingStatus",
1878 aPad->GetAttribute() == PAD_ATTRIB::PTH ? "PLATED" : "NONPLATED" );
1879 addAttribute( padStackHoleNode, "plusTol", "0.0" );
1880 addAttribute( padStackHoleNode, "minusTol", "0.0" );
1881 addXY( padStackHoleNode, aPad->GetOffset( PADSTACK::ALL_LAYERS ) );
1882 }
1883
1884 LSEQ layer_seq = aPad->GetLayerSet().Seq();
1885
1886 for( PCB_LAYER_ID layer : layer_seq )
1887 {
1888 FOOTPRINT* fp = aPad->GetParentFootprint();
1889
1890 if( !m_board->IsLayerEnabled( layer ) )
1891 continue;
1892
1893 wxXmlNode* padStackPadDefNode = appendNode( padStackDefNode, "PadstackPadDef" );
1894 addAttribute( padStackPadDefNode, "layerRef", m_layer_name_map[layer] );
1895 addAttribute( padStackPadDefNode, "padUse", "REGULAR" );
1896 addLocationNode( padStackPadDefNode, aPad->GetOffset( PADSTACK::ALL_LAYERS ).x, aPad->GetOffset( PADSTACK::ALL_LAYERS ).y );
1897
1898 if( aPad->HasHole() || !aPad->FlashLayer( layer ) )
1899 {
1900 PCB_SHAPE shape( nullptr, SHAPE_T::CIRCLE );
1901 shape.SetStart( aPad->GetOffset( PADSTACK::ALL_LAYERS ) );
1902 shape.SetEnd( shape.GetStart() + aPad->GetDrillSize() / 2 );
1903 addShape( padStackPadDefNode, shape );
1904 }
1905 else
1906 {
1907 addShape( padStackPadDefNode, *aPad, layer );
1908 }
1909 }
1910}
1911
1912
1913void PCB_IO_IPC2581::addPadStack( wxXmlNode* aContentNode, const PCB_VIA* aVia )
1914{
1915 size_t hash = hash_fp_item( aVia, 0 );
1916 wxString name = wxString::Format( "PADSTACK_%zu", m_padstack_dict.size() + 1 );
1917 auto [ via_pair, success ] = m_padstack_dict.emplace( hash, name );
1918
1919 addAttribute( aContentNode, "padstackDefRef", via_pair->second );
1920
1921 // If we did not insert a new padstack, then we have already added it to the XML
1922 // and we don't need to add it again.
1923 if( !success )
1924 return;
1925
1926 wxXmlNode* padStackDefNode = new wxXmlNode( wxXML_ELEMENT_NODE, "PadStackDef" );
1927 insertNodeAfter( m_last_padstack, padStackDefNode );
1928 m_last_padstack = padStackDefNode;
1929 addAttribute( padStackDefNode, "name", name );
1930
1931 wxXmlNode* padStackHoleNode = appendNode( padStackDefNode, "PadstackHoleDef" );
1932 addAttribute( padStackHoleNode, "name", wxString::Format( "PH%d", aVia->GetDrillValue() ) );
1933 padStackHoleNode->AddAttribute( "diameter", floatVal( m_scale * aVia->GetDrillValue() ) );
1934 addAttribute( padStackHoleNode, "platingStatus", "VIA" );
1935 addAttribute( padStackHoleNode, "plusTol", "0.0" );
1936 addAttribute( padStackHoleNode, "minusTol", "0.0" );
1937 addAttribute( padStackHoleNode, "x", "0.0" );
1938 addAttribute( padStackHoleNode, "y", "0.0" );
1939
1940 LSEQ layer_seq = aVia->GetLayerSet().Seq();
1941
1942 for( PCB_LAYER_ID layer : layer_seq )
1943 {
1944 if( !aVia->FlashLayer( layer ) || !m_board->IsLayerEnabled( layer ) )
1945 continue;
1946
1947 PCB_SHAPE shape( nullptr, SHAPE_T::CIRCLE );
1948
1949 shape.SetEnd( { KiROUND( aVia->GetWidth( layer ) / 2.0 ), 0 } );
1950
1951 wxXmlNode* padStackPadDefNode = appendNode( padStackDefNode, "PadstackPadDef" );
1952 addAttribute( padStackPadDefNode, "layerRef", m_layer_name_map[layer] );
1953 addAttribute( padStackPadDefNode, "padUse", "REGULAR" );
1954
1955 addLocationNode( padStackPadDefNode, 0.0, 0.0 );
1956 addShape( padStackPadDefNode, shape );
1957 }
1958}
1959
1960
1961bool PCB_IO_IPC2581::addPolygonNode( wxXmlNode* aParentNode,
1962 const SHAPE_LINE_CHAIN& aPolygon, FILL_T aFillType,
1963 int aWidth, LINE_STYLE aDashType )
1964{
1965 wxXmlNode* polygonNode = nullptr;
1966
1967 if( aPolygon.PointCount() < 3 )
1968 return false;
1969
1970 auto make_node =
1971 [&]()
1972 {
1973 polygonNode = appendNode( aParentNode, "Polygon" );
1974 wxXmlNode* polybeginNode = appendNode( polygonNode, "PolyBegin" );
1975
1976 const std::vector<VECTOR2I>& pts = aPolygon.CPoints();
1977 addXY( polybeginNode, pts[0] );
1978
1979 for( size_t ii = 1; ii < pts.size(); ++ii )
1980 {
1981 wxXmlNode* polyNode = appendNode( polygonNode, "PolyStepSegment" );
1982 addXY( polyNode, pts[ii] );
1983 }
1984
1985 wxXmlNode* polyendNode = appendNode( polygonNode, "PolyStepSegment" );
1986 addXY( polyendNode, pts[0] );
1987 };
1988
1989 // Allow the case where we don't want line/fill information in the polygon
1990 if( aFillType == FILL_T::NO_FILL )
1991 {
1992 make_node();
1993 // If we specify a line width, we need to add a LineDescRef node and
1994 // since this is only valid for a non-filled polygon, we need to create
1995 // the fillNode as well
1996 if( aWidth > 0 )
1997 addLineDesc( polygonNode, aWidth, aDashType, true );
1998 }
1999 else
2000 {
2001 wxCHECK( aWidth == 0, false );
2002 make_node();
2003 }
2004
2005 addFillDesc( polygonNode, aFillType );
2006
2007 return true;
2008}
2009
2010
2011bool PCB_IO_IPC2581::addPolygonCutouts( wxXmlNode* aParentNode,
2012 const SHAPE_POLY_SET::POLYGON& aPolygon )
2013{
2014 for( size_t ii = 1; ii < aPolygon.size(); ++ii )
2015 {
2016 wxCHECK2( aPolygon[ii].PointCount() >= 3, continue );
2017
2018 wxXmlNode* cutoutNode = appendNode( aParentNode, "Cutout" );
2019 wxXmlNode* polybeginNode = appendNode( cutoutNode, "PolyBegin" );
2020
2021 const std::vector<VECTOR2I>& hole = aPolygon[ii].CPoints();
2022 addXY( polybeginNode, hole[0] );
2023
2024 for( size_t jj = 1; jj < hole.size(); ++jj )
2025 {
2026 wxXmlNode* polyNode = appendNode( cutoutNode, "PolyStepSegment" );
2027 addXY( polyNode, hole[jj] );
2028 }
2029
2030 wxXmlNode* polyendNode = appendNode( cutoutNode, "PolyStepSegment" );
2031 addXY( polyendNode, hole[0] );
2032 }
2033
2034 return true;
2035}
2036
2037
2038bool PCB_IO_IPC2581::addOutlineNode( wxXmlNode* aParentNode, const SHAPE_POLY_SET& aPolySet,
2039 int aWidth, LINE_STYLE aDashType )
2040{
2041 if( aPolySet.OutlineCount() == 0 )
2042 return false;
2043
2044 wxXmlNode* outlineNode = appendNode( aParentNode, "Outline" );
2045
2046 // Outlines can only have one polygon according to the IPC-2581 spec, so
2047 // if there are more than one, we need to combine them into a single polygon
2048 const SHAPE_LINE_CHAIN* outline = &aPolySet.Outline( 0 );
2049 SHAPE_LINE_CHAIN bbox_outline;
2050 BOX2I bbox = outline->BBox();
2051
2052 if( aPolySet.OutlineCount() > 1 )
2053 {
2054 for( int ii = 1; ii < aPolySet.OutlineCount(); ++ii )
2055 {
2056 wxCHECK2( aPolySet.Outline( ii ).PointCount() >= 3, continue );
2057 bbox.Merge( aPolySet.Outline( ii ).BBox() );
2058 }
2059
2060 bbox_outline.Append( bbox.GetLeft(), bbox.GetTop() );
2061 bbox_outline.Append( bbox.GetRight(), bbox.GetTop() );
2062 bbox_outline.Append( bbox.GetRight(), bbox.GetBottom() );
2063 bbox_outline.Append( bbox.GetLeft(), bbox.GetBottom() );
2064 outline = &bbox_outline;
2065 }
2066
2067
2068 if( !addPolygonNode( outlineNode, *outline ) )
2069 wxLogTrace( traceIpc2581, wxS( "Failed to add polygon to outline" ) );
2070
2071 if( !outlineNode->GetChildren() )
2072 {
2073 aParentNode->RemoveChild( outlineNode );
2074 delete outlineNode;
2075 return false;
2076 }
2077
2078 addLineDesc( outlineNode, aWidth, aDashType );
2079
2080 return true;
2081}
2082
2083
2084bool PCB_IO_IPC2581::addContourNode( wxXmlNode* aParentNode, const SHAPE_POLY_SET& aPolySet,
2085 int aOutline, FILL_T aFillType, int aWidth,
2086 LINE_STYLE aDashType )
2087{
2088 if( aPolySet.OutlineCount() < ( aOutline + 1 ) )
2089 return false;
2090
2091 wxXmlNode* contourNode = appendNode( aParentNode, "Contour" );
2092
2093 if( addPolygonNode( contourNode, aPolySet.Outline( aOutline ), aFillType, aWidth, aDashType ) )
2094 {
2095 // Do not attempt to add cutouts to shapes that are already hollow
2096 if( aFillType != FILL_T::NO_FILL )
2097 addPolygonCutouts( contourNode, aPolySet.Polygon( aOutline ) );
2098 }
2099 else
2100 {
2101 aParentNode->RemoveChild( contourNode );
2102 delete contourNode;
2103 return false;
2104 }
2105
2106 return true;
2107}
2108
2109
2110void PCB_IO_IPC2581::generateProfile( wxXmlNode* aStepNode )
2111{
2112 SHAPE_POLY_SET board_outline;
2113
2114 if( ! m_board->GetBoardPolygonOutlines( board_outline ) )
2115 {
2116 wxLogError( "Failed to get board outline" );
2117 return;
2118 }
2119
2120 wxXmlNode* profileNode = appendNode( aStepNode, "Profile" );
2121
2122 if( !addPolygonNode( profileNode, board_outline.Outline( 0 ) ) )
2123 {
2124 wxLogTrace( traceIpc2581, wxS( "Failed to add polygon to profile" ) );
2125 aStepNode->RemoveChild( profileNode );
2126 delete profileNode;
2127 }
2128}
2129
2130
2131wxXmlNode* PCB_IO_IPC2581::addPackage( wxXmlNode* aContentNode, FOOTPRINT* aFp )
2132{
2133 std::unique_ptr<FOOTPRINT> fp( static_cast<FOOTPRINT*>( aFp->Clone() ) );
2134 fp->SetParentGroup( nullptr );
2135 fp->SetPosition( { 0, 0 } );
2136
2137 if( fp->GetLayer() != F_Cu )
2138 fp->Flip( fp->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
2139
2140 fp->SetOrientation( ANGLE_0 );
2141
2142 size_t hash = hash_fp_item( fp.get(), HASH_POS | REL_COORD );
2143 wxString name = genString( wxString::Format( "%s_%zu",
2144 fp->GetFPID().GetLibItemName().wx_str(),
2145 m_footprint_dict.size() + 1 ) );
2146
2147 auto [ iter, success ] = m_footprint_dict.emplace( hash, name );
2148 addAttribute( aContentNode, "packageRef", iter->second );
2149
2150 if( !success)
2151 return nullptr;
2152
2153 // Package and Component nodes are at the same level, so we need to find the parent
2154 // which should be the Step node
2155 wxXmlNode* packageNode = new wxXmlNode( wxXML_ELEMENT_NODE, "Package" );
2156 wxXmlNode* otherSideViewNode = nullptr; // Only set this if we have elements on the back side
2157
2158 addAttribute( packageNode, "name", name );
2159 addAttribute( packageNode, "type", "OTHER" ); // TODO: Replace with actual package type once we encode this
2160
2161 // We don't specially identify pin 1 in our footprints, so we need to guess
2162 if( fp->FindPadByNumber( "1" ) )
2163 addAttribute( packageNode, "pinOne", "1" );
2164 else if ( fp->FindPadByNumber( "A1" ) )
2165 addAttribute( packageNode, "pinOne", "A1" );
2166 else if ( fp->FindPadByNumber( "A" ) )
2167 addAttribute( packageNode, "pinOne", "A" );
2168 else if ( fp->FindPadByNumber( "a" ) )
2169 addAttribute( packageNode, "pinOne", "a" );
2170 else if ( fp->FindPadByNumber( "a1" ) )
2171 addAttribute( packageNode, "pinOne", "a1" );
2172 else if ( fp->FindPadByNumber( "Anode" ) )
2173 addAttribute( packageNode, "pinOne", "Anode" );
2174 else if ( fp->FindPadByNumber( "ANODE" ) )
2175 addAttribute( packageNode, "pinOne", "ANODE" );
2176 else
2177 addAttribute( packageNode, "pinOne", "UNKNOWN" );
2178
2179 addAttribute( packageNode, "pinOneOrientation", "OTHER" );
2180
2181 const SHAPE_POLY_SET& courtyard = fp->GetCourtyard( F_CrtYd );
2182 const SHAPE_POLY_SET& courtyard_back = fp->GetCourtyard( B_CrtYd );
2183
2184 if( courtyard.OutlineCount() > 0 )
2185 addOutlineNode( packageNode, courtyard, courtyard.Outline( 0 ).Width(), LINE_STYLE::SOLID );
2186
2187 if( courtyard_back.OutlineCount() > 0 )
2188 {
2189 otherSideViewNode = appendNode( packageNode, "OtherSideView" );
2190 addOutlineNode( otherSideViewNode, courtyard_back, courtyard_back.Outline( 0 ).Width(),
2191 LINE_STYLE::SOLID );
2192 }
2193
2194 if( !courtyard.OutlineCount() && !courtyard_back.OutlineCount() )
2195 {
2196 SHAPE_POLY_SET bbox = fp->GetBoundingHull();
2197 addOutlineNode( packageNode, bbox );
2198 }
2199
2200 wxXmlNode* pickupPointNode = appendNode( packageNode, "PickupPoint" );
2201 addAttribute( pickupPointNode, "x", "0.0" );
2202 addAttribute( pickupPointNode, "y", "0.0" );
2203
2204 std::map<PCB_LAYER_ID, std::map<bool, std::vector<BOARD_ITEM*>>> elements;
2205
2206 for( BOARD_ITEM* item : fp->GraphicalItems() )
2207 {
2208 PCB_LAYER_ID layer = item->GetLayer();
2209
2213 if( layer != F_SilkS && layer != B_SilkS && layer != F_Fab && layer != B_Fab )
2214 continue;
2215
2216 bool is_abs = true;
2217
2218 if( item->Type() == PCB_SHAPE_T )
2219 {
2220 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2221
2222 // Circles and Rectanges only have size information so we need to place them in
2223 // a separate node that has a location
2224 if( shape->GetShape() == SHAPE_T::CIRCLE || shape->GetShape() == SHAPE_T::RECTANGLE )
2225 is_abs = false;
2226 }
2227
2228 elements[item->GetLayer()][is_abs].push_back( item );
2229 }
2230
2231 auto add_base_node =
2232 [&]( PCB_LAYER_ID aLayer ) -> wxXmlNode*
2233 {
2234 wxXmlNode* parent = packageNode;
2235 bool is_back = aLayer == B_SilkS || aLayer == B_Fab;
2236
2237 if( is_back )
2238 {
2239 if( !otherSideViewNode )
2240 otherSideViewNode = new wxXmlNode( wxXML_ELEMENT_NODE, "OtherSideView" );
2241
2242 parent = otherSideViewNode;
2243 }
2244
2245 wxString name;
2246
2247 if( aLayer == F_SilkS || aLayer == B_SilkS )
2248 name = "SilkScreen";
2249 else if( aLayer == F_Fab || aLayer == B_Fab )
2250 name = "AssemblyDrawing";
2251 else
2252 wxASSERT( false );
2253
2254 wxXmlNode* new_node = appendNode( parent, name );
2255 return new_node;
2256 };
2257
2258 auto add_marking_node =
2259 [&]( wxXmlNode* aNode ) -> wxXmlNode*
2260 {
2261 wxXmlNode* marking_node = appendNode( aNode, "Marking" );
2262 addAttribute( marking_node, "markingUsage", "NONE" );
2263 return marking_node;
2264 };
2265
2266 std::map<PCB_LAYER_ID, wxXmlNode*> layer_nodes;
2267 std::map<PCB_LAYER_ID, BOX2I> layer_bbox;
2268
2269 for( auto layer : { F_Fab, B_Fab } )
2270 {
2271 if( elements.find( layer ) != elements.end() )
2272 {
2273 if( elements[layer][true].size() > 0 )
2274 layer_bbox[layer] = elements[layer][true][0]->GetBoundingBox();
2275 else if( elements[layer][false].size() > 0 )
2276 layer_bbox[layer] = elements[layer][false][0]->GetBoundingBox();
2277 }
2278 }
2279
2280 for( auto& [layer, map] : elements )
2281 {
2282 wxXmlNode* layer_node = add_base_node( layer );
2283 wxXmlNode* marking_node = add_marking_node( layer_node );
2284 wxXmlNode* group_node = appendNode( marking_node, "UserSpecial" );
2285 bool update_bbox = false;
2286
2287 if( layer == F_Fab || layer == B_Fab )
2288 {
2289 layer_nodes[layer] = layer_node;
2290 update_bbox = true;
2291 }
2292
2293 for( auto& [is_abs, vec] : map )
2294 {
2295 for( BOARD_ITEM* item : vec )
2296 {
2297 wxXmlNode* output_node = nullptr;
2298
2299 if( update_bbox )
2300 layer_bbox[layer].Merge( item->GetBoundingBox() );
2301
2302 if( !is_abs )
2303 output_node = add_marking_node( layer_node );
2304 else
2305 output_node = group_node;
2306
2307 switch( item->Type() )
2308 {
2309 case PCB_TEXT_T:
2310 {
2311 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
2312
2313 if( text->IsKnockout() )
2314 addKnockoutText( output_node, text );
2315 else
2316 addText( output_node, text, text->GetFontMetrics() );
2317
2318 break;
2319 }
2320
2321 case PCB_TEXTBOX_T:
2322 {
2323 PCB_TEXTBOX* text = static_cast<PCB_TEXTBOX*>( item );
2324 addText( output_node, text, text->GetFontMetrics() );
2325
2326 // We want to force this to be a polygon to get absolute coordinates
2327 if( text->IsBorderEnabled() )
2328 {
2329 SHAPE_POLY_SET poly_set;
2330 text->GetEffectiveShape()->TransformToPolygon( poly_set, 0, ERROR_INSIDE );
2331 addContourNode( output_node, poly_set, 0, FILL_T::NO_FILL,
2332 text->GetBorderWidth() );
2333 }
2334
2335 break;
2336 }
2337
2338 case PCB_SHAPE_T:
2339 {
2340 if( !is_abs )
2341 addLocationNode( output_node, *static_cast<PCB_SHAPE*>( item ) );
2342
2343 addShape( output_node, *static_cast<PCB_SHAPE*>( item ) );
2344
2345 break;
2346 }
2347
2348 default: break;
2349 }
2350 }
2351 }
2352
2353 if( group_node->GetChildren() == nullptr )
2354 {
2355 marking_node->RemoveChild( group_node );
2356 layer_node->RemoveChild( marking_node );
2357 delete group_node;
2358 delete marking_node;
2359 }
2360 }
2361
2362 for( auto&[layer, bbox] : layer_bbox )
2363 {
2364 if( bbox.GetWidth() > 0 )
2365 {
2366 wxXmlNode* outlineNode = insertNode( layer_nodes[layer], "Outline" );
2367
2368 SHAPE_LINE_CHAIN outline;
2369 std::vector<VECTOR2I> points( 4 );
2370 points[0] = bbox.GetPosition();
2371 points[2] = bbox.GetEnd();
2372 points[1].x = points[0].x;
2373 points[1].y = points[2].y;
2374 points[3].x = points[2].x;
2375 points[3].y = points[0].y;
2376
2377 outline.Append( points );
2378 addPolygonNode( outlineNode, outline, FILL_T::NO_FILL, 0 );
2379 addLineDesc( outlineNode, 0, LINE_STYLE::SOLID );
2380 }
2381 }
2382
2383 for( size_t ii = 0; ii < fp->Pads().size(); ++ii )
2384 {
2385 PAD* pad = fp->Pads()[ii];
2386 wxXmlNode* pinNode = appendNode( packageNode, "Pin" );
2387 wxString name = pinName( pad );
2388
2389 addAttribute( pinNode, "number", name );
2390
2391 m_net_pin_dict[pad->GetNetCode()].emplace_back(
2392 genString( fp->GetReference(), "CMP" ), name );
2393
2394 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
2395 addAttribute( pinNode, "electricalType", "MECHANICAL" );
2396 else if( pad->IsOnCopperLayer() )
2397 addAttribute( pinNode, "electricalType", "ELECTRICAL" );
2398 else
2399 addAttribute( pinNode, "electricalType", "UNDEFINED" );
2400
2401 if( pad->HasHole() )
2402 addAttribute( pinNode, "type", "THRU" );
2403 else
2404 addAttribute( pinNode, "type", "SURFACE" );
2405
2406 if( pad->GetFPRelativeOrientation() != ANGLE_0 )
2407 {
2408 wxXmlNode* xformNode = appendNode( pinNode, "Xform" );
2409 xformNode->AddAttribute(
2410 "rotation",
2411 floatVal( pad->GetFPRelativeOrientation().Normalize().AsDegrees() ) );
2412 }
2413
2414 addLocationNode( pinNode, *pad, true );
2415 addShape( pinNode, *pad, pad->GetLayer() );
2416
2417 // We just need the padstack, we don't need the reference here. The reference will be
2418 // created in the LayerFeature set
2419 wxXmlNode dummy;
2420 addPadStack( &dummy, pad );
2421 }
2422
2423 return packageNode;
2424}
2425
2426
2427void PCB_IO_IPC2581::generateComponents( wxXmlNode* aStepNode )
2428{
2429 std::vector<wxXmlNode*> componentNodes;
2430 std::vector<wxXmlNode*> packageNodes;
2431 std::set<wxString> packageNames;
2432
2433 bool generate_unique = m_OEMRef.empty();
2434
2435 for( FOOTPRINT* fp : m_board->Footprints() )
2436 {
2437 wxXmlNode* componentNode = new wxXmlNode( wxXML_ELEMENT_NODE, "Component" );
2438 addAttribute( componentNode, "refDes", componentName( fp ) );
2439 wxXmlNode* pkg = addPackage( componentNode, fp );
2440
2441 if( pkg )
2442 packageNodes.push_back( pkg );
2443
2444 wxString name;
2445
2446 PCB_FIELD* field = nullptr;
2447
2448 if( !generate_unique )
2449 field = fp->GetFieldByName( m_OEMRef );
2450
2451 if( field && !field->GetText().empty() )
2452 {
2453 name = field->GetShownText( false );
2454 }
2455 else
2456 {
2457 name = wxString::Format( "%s_%s_%s", fp->GetFPID().GetFullLibraryName(),
2458 fp->GetFPID().GetLibItemName().wx_str(),
2459 fp->GetValue() );
2460 }
2461
2462 if( !m_OEMRef_dict.emplace( fp, name ).second )
2463 wxLogError( "Duplicate footprint pointers. Please report this bug." );
2464
2465 addAttribute( componentNode, "part", genString( name, "REF" ) );
2466 addAttribute( componentNode, "layerRef", m_layer_name_map[fp->GetLayer()] );
2467
2468 if( fp->GetAttributes() & FP_THROUGH_HOLE )
2469 addAttribute( componentNode, "mountType", "THMT" );
2470 else if( fp->GetAttributes() & FP_SMD )
2471 addAttribute( componentNode, "mountType", "SMT" );
2472 else
2473 addAttribute( componentNode, "mountType", "OTHER" );
2474
2475 if( fp->GetOrientation() != ANGLE_0 || fp->GetLayer() != F_Cu )
2476 {
2477 wxXmlNode* xformNode = appendNode( componentNode, "Xform" );
2478
2479 EDA_ANGLE fp_angle = fp->GetOrientation().Normalize();
2480
2481 if( fp->GetLayer() == B_Cu )
2482 fp_angle = ( fp_angle.Invert() - ANGLE_180 ).Normalize();
2483
2484 if( fp_angle != ANGLE_0 )
2485 addAttribute( xformNode, "rotation", floatVal( fp_angle.AsDegrees(), 2 ) );
2486
2487 if( fp->GetLayer() != F_Cu )
2488 addAttribute( xformNode, "mirror", "true" );
2489 }
2490
2491 addLocationNode( componentNode, fp->GetPosition().x, fp->GetPosition().y );
2492
2493 componentNodes.push_back( componentNode );
2494 }
2495
2496 for( wxXmlNode* padstack : m_padstacks )
2497 {
2498 insertNode( aStepNode, padstack );
2499 m_last_padstack = padstack;
2500 }
2501
2502 for( wxXmlNode* pkg : packageNodes )
2503 aStepNode->AddChild( pkg );
2504
2505 for( wxXmlNode* cmp : componentNodes )
2506 aStepNode->AddChild( cmp );
2507}
2508
2509
2510void PCB_IO_IPC2581::generateLogicalNets( wxXmlNode* aStepNode )
2511{
2512 for( auto& [ net, pin_pair] : m_net_pin_dict )
2513 {
2514 wxXmlNode* netNode = appendNode( aStepNode, "LogicalNet" );
2515 addAttribute( netNode, "name",
2516 genString( m_board->GetNetInfo().GetNetItem( net )->GetNetname(), "NET" ) ) ;
2517
2518 for( auto& [cmp, pin] : pin_pair )
2519 {
2520 wxXmlNode* netPinNode = appendNode( netNode, "PinRef" );
2521 addAttribute( netPinNode, "componentRef", cmp );
2522 addAttribute( netPinNode, "pin", pin );
2523 }
2524 //TODO: Finish
2525 }
2526}
2527
2528//TODO: Add PhyNetGroup section
2529
2530void PCB_IO_IPC2581::generateLayerFeatures( wxXmlNode* aStepNode )
2531{
2532 LSEQ layers = m_board->GetEnabledLayers().Seq();
2533 const NETINFO_LIST& nets = m_board->GetNetInfo();
2534 std::vector<std::unique_ptr<FOOTPRINT>> footprints;
2535
2536 // To avoid the overhead of repeatedly cycling through the layers and nets,
2537 // we pre-sort the board items into a map of layer -> net -> items
2538 std::map<PCB_LAYER_ID, std::map<int, std::vector<BOARD_ITEM*>>> elements;
2539
2540 std::for_each( m_board->Tracks().begin(), m_board->Tracks().end(),
2541 [&layers, &elements]( PCB_TRACK* aTrack )
2542 {
2543 if( aTrack->Type() == PCB_VIA_T )
2544 {
2545 PCB_VIA* via = static_cast<PCB_VIA*>( aTrack );
2546
2547 for( PCB_LAYER_ID layer : layers )
2548 {
2549 if( via->FlashLayer( layer ) )
2550 elements[layer][via->GetNetCode()].push_back( via );
2551 }
2552 }
2553 else
2554 {
2555 elements[aTrack->GetLayer()][aTrack->GetNetCode()].push_back( aTrack );
2556 }
2557 } );
2558
2559 std::for_each( m_board->Zones().begin(), m_board->Zones().end(),
2560 [ &elements ]( ZONE* zone )
2561 {
2562 LSEQ zone_layers = zone->GetLayerSet().Seq();
2563
2564 for( PCB_LAYER_ID layer : zone_layers )
2565 elements[layer][zone->GetNetCode()].push_back( zone );
2566 } );
2567
2568 for( BOARD_ITEM* item : m_board->Drawings() )
2569 {
2570 if( BOARD_CONNECTED_ITEM* conn_it = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
2571 elements[conn_it->GetLayer()][conn_it->GetNetCode()].push_back( conn_it );
2572 else
2573 elements[item->GetLayer()][0].push_back( item );
2574 }
2575
2576 for( FOOTPRINT* fp : m_board->Footprints() )
2577 {
2578 for( PCB_FIELD* field : fp->GetFields() )
2579 elements[field->GetLayer()][0].push_back( field );
2580
2581 for( BOARD_ITEM* item : fp->GraphicalItems() )
2582 elements[item->GetLayer()][0].push_back( item );
2583
2584 for( PAD* pad : fp->Pads() )
2585 {
2586 LSEQ pad_layers = pad->GetLayerSet().Seq();
2587
2588 for( PCB_LAYER_ID layer : pad_layers )
2589 {
2590 if( pad->FlashLayer( layer ) )
2591 elements[layer][pad->GetNetCode()].push_back( pad );
2592 }
2593 }
2594 }
2595
2596 for( PCB_LAYER_ID layer : layers )
2597 {
2598 if( m_progressReporter )
2599 m_progressReporter->SetMaxProgress( nets.GetNetCount() * layers.size() );
2600
2601 wxXmlNode* layerNode = appendNode( aStepNode, "LayerFeature" );
2602 addAttribute( layerNode, "layerRef", m_layer_name_map[layer] );
2603
2604 auto process_net = [&] ( int net )
2605 {
2606 std::vector<BOARD_ITEM*>& vec = elements[layer][net];
2607
2608 if( vec.empty() )
2609 return;
2610
2611 std::stable_sort( vec.begin(), vec.end(),
2612 []( BOARD_ITEM* a, BOARD_ITEM* b )
2613 {
2614 if( a->GetParentFootprint() == b->GetParentFootprint() )
2615 return a->Type() < b->Type();
2616
2617 return a->GetParentFootprint() < b->GetParentFootprint();
2618 } );
2619
2620 generateLayerSetNet( layerNode, layer, vec );
2621 };
2622
2623 for( const NETINFO_ITEM* net : nets )
2624 {
2625 if( m_progressReporter )
2626 {
2627 m_progressReporter->Report( wxString::Format( _( "Exporting Layer %s, Net %s" ),
2628 m_board->GetLayerName( layer ),
2629 net->GetNetname() ) );
2630 m_progressReporter->AdvanceProgress();
2631 }
2632
2633 process_net( net->GetNetCode() );
2634 }
2635
2636 if( layerNode->GetChildren() == nullptr )
2637 {
2638 aStepNode->RemoveChild( layerNode );
2639 delete layerNode;
2640 }
2641 }
2642}
2643
2644
2645void PCB_IO_IPC2581::generateLayerSetDrill( wxXmlNode* aLayerNode )
2646{
2647 int hole_count = 1;
2648
2649 for( const auto& [layers, vec] : m_drill_layers )
2650 {
2651 wxXmlNode* layerNode = appendNode( aLayerNode, "LayerFeature" );
2652 layerNode->AddAttribute( "layerRef", genLayersString( layers.first, layers.second, "DRILL" ) );
2653
2654 for( BOARD_ITEM* item : vec )
2655 {
2656 if( item->Type() == PCB_VIA_T )
2657 {
2658 PCB_VIA* via = static_cast<PCB_VIA*>( item );
2659 auto it = m_padstack_dict.find( hash_fp_item( via, 0 ) );
2660
2661 if( it == m_padstack_dict.end() )
2662 {
2663 wxLogError( "Failed to find padstack for via" );
2664 continue;
2665 }
2666
2667 wxXmlNode* padNode = appendNode( layerNode, "Set" );
2668 addAttribute( padNode, "geometry", it->second );
2669
2670 if( via->GetNetCode() > 0 )
2671 addAttribute( padNode, "net", genString( via->GetNetname(), "NET" ) );
2672
2673 wxXmlNode* holeNode = appendNode( padNode, "Hole" );
2674 addAttribute( holeNode, "name", wxString::Format( "H%d", hole_count++ ) );
2675 addAttribute( holeNode, "diameter", floatVal( m_scale * via->GetDrillValue() ) );
2676 addAttribute( holeNode, "platingStatus", "VIA" );
2677 addAttribute( holeNode, "plusTol", "0.0" );
2678 addAttribute( holeNode, "minusTol", "0.0" );
2679 addXY( holeNode, via->GetPosition() );
2680 }
2681 else if( item->Type() == PCB_PAD_T )
2682 {
2683 PAD* pad = static_cast<PAD*>( item );
2684 auto it = m_padstack_dict.find( hash_fp_item( pad, 0 ) );
2685
2686 if( it == m_padstack_dict.end() )
2687 {
2688 wxLogError( "Failed to find padstack for pad" );
2689 continue;
2690 }
2691
2692 wxXmlNode* padNode = appendNode( layerNode, "Set" );
2693 addAttribute( padNode, "geometry", it->second );
2694
2695 if( pad->GetNetCode() > 0 )
2696 addAttribute( padNode, "net", genString( pad->GetNetname(), "NET" ) );
2697
2698 wxXmlNode* holeNode = appendNode( padNode, "Hole" );
2699 addAttribute( holeNode, "name", wxString::Format( "H%d", hole_count++ ) );
2700 addAttribute( holeNode, "diameter", floatVal( m_scale * pad->GetDrillSizeX() ) );
2701 addAttribute( holeNode, "platingStatus",
2702 pad->GetAttribute() == PAD_ATTRIB::PTH ? "PLATED" : "NONPLATED" );
2703 addAttribute( holeNode, "plusTol", "0.0" );
2704 addAttribute( holeNode, "minusTol", "0.0" );
2705 addXY( holeNode, pad->GetPosition() );
2706 }
2707 }
2708 }
2709
2710 hole_count = 1;
2711
2712 for( const auto& [layers, vec] : m_slot_holes )
2713 {
2714 wxXmlNode* layerNode = appendNode( aLayerNode, "LayerFeature" );
2715 layerNode->AddAttribute( "layerRef", genLayersString( layers.first, layers.second, "SLOT" ) );
2716
2717 for( PAD* pad : vec )
2718 {
2719 wxXmlNode* padNode = appendNode( layerNode, "Set" );
2720
2721 if( pad->GetNetCode() > 0 )
2722 addAttribute( padNode, "net", genString( pad->GetNetname(), "NET" ) );
2723
2724 addSlotCavity( padNode, *pad, wxString::Format( "SLOT%d", hole_count++ ) );
2725 }
2726 }
2727}
2728
2729
2730void PCB_IO_IPC2581::generateLayerSetNet( wxXmlNode* aLayerNode, PCB_LAYER_ID aLayer,
2731 std::vector<BOARD_ITEM*>& aItems )
2732{
2733 auto it = aItems.begin();
2734 wxXmlNode* layerSetNode = appendNode( aLayerNode, "Set" );
2735 wxXmlNode* featureSetNode = appendNode( layerSetNode, "Features" );
2736 wxXmlNode* specialNode = appendNode( featureSetNode, "UserSpecial" );
2737
2738 bool has_via = false;
2739 bool has_pad = false;
2740
2741 wxXmlNode* padSetNode = nullptr;
2742
2743 wxXmlNode* viaSetNode = nullptr;
2744
2745 wxXmlNode* teardropLayerSetNode = nullptr;
2746 wxXmlNode* teardropFeatureSetNode = nullptr;
2747
2748 if( BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( *it );
2749 IsCopperLayer( aLayer ) && item )
2750 {
2751 if( item->GetNetCode() > 0 )
2752 addAttribute( layerSetNode, "net", genString( item->GetNetname(), "NET" ) );
2753 }
2754
2755 auto add_track =
2756 [&]( PCB_TRACK* track )
2757 {
2758 if( track->Type() == PCB_TRACE_T )
2759 {
2760 PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
2761 shape.SetStart( track->GetStart() );
2762 shape.SetEnd( track->GetEnd() );
2763 shape.SetWidth( track->GetWidth() );
2764 addShape( specialNode, shape );
2765 }
2766 else if( track->Type() == PCB_ARC_T )
2767 {
2768 PCB_ARC* arc = static_cast<PCB_ARC*>( track );
2769 PCB_SHAPE shape( nullptr, SHAPE_T::ARC );
2770 shape.SetArcGeometry( arc->GetStart(), arc->GetMid(), arc->GetEnd() );
2771 shape.SetWidth( arc->GetWidth() );
2772 addShape( specialNode, shape );
2773 }
2774 else
2775 {
2776 if( !viaSetNode )
2777 {
2778 if( !has_pad )
2779 {
2780 viaSetNode = layerSetNode;
2781 has_via = true;
2782 }
2783 else
2784 {
2785 viaSetNode = appendNode( layerSetNode, "Set" );
2786
2787 if( track->GetNetCode() > 0 )
2788 addAttribute( viaSetNode, "net", genString( track->GetNetname(), "NET" ) );
2789 }
2790
2791 addAttribute( viaSetNode, "padUsage", "VIA" );
2792 }
2793
2794 addVia( viaSetNode, static_cast<PCB_VIA*>( track ), aLayer );
2795 }
2796 };
2797
2798 auto add_zone =
2799 [&]( ZONE* zone )
2800 {
2801 wxXmlNode* zoneFeatureNode = specialNode;
2802
2803 if( zone->IsTeardropArea() && m_version > 'B' )
2804 {
2805 if( !teardropFeatureSetNode )
2806 {
2807 teardropLayerSetNode = appendNode( aLayerNode, "Set" );
2808 addAttribute( teardropLayerSetNode, "geometryUsage", "TEARDROP" );
2809
2810 if( zone->GetNetCode() > 0 )
2811 {
2812 addAttribute( teardropLayerSetNode, "net",
2813 genString( zone->GetNetname(), "NET" ) );
2814 }
2815
2816 wxXmlNode* new_teardrops = appendNode( teardropLayerSetNode, "Features" );
2817 addLocationNode( new_teardrops, 0.0, 0.0 );
2818 teardropFeatureSetNode = appendNode( new_teardrops, "UserSpecial" );
2819 }
2820
2821 zoneFeatureNode = teardropFeatureSetNode;
2822 }
2823 else
2824 {
2825 if( FOOTPRINT* fp = zone->GetParentFootprint() )
2826 {
2827 wxXmlNode* tempSetNode = appendNode( aLayerNode, "Set" );
2828 wxString refDes = componentName( zone->GetParentFootprint() );
2829 addAttribute( tempSetNode, "componentRef", refDes );
2830 wxXmlNode* newFeatures = appendNode( tempSetNode, "Features" );
2831 addLocationNode( newFeatures, 0.0, 0.0 );
2832 zoneFeatureNode = appendNode( newFeatures, "UserSpecial" );
2833 }
2834 }
2835
2836 SHAPE_POLY_SET& zone_shape = *zone->GetFilledPolysList( aLayer );
2837
2838 for( int ii = 0; ii < zone_shape.OutlineCount(); ++ii )
2839 addContourNode( zoneFeatureNode, zone_shape, ii );
2840 };
2841
2842 auto add_shape =
2843 [&] ( PCB_SHAPE* shape )
2844 {
2845 FOOTPRINT* fp = shape->GetParentFootprint();
2846
2847 if( fp )
2848 {
2849 wxXmlNode* tempSetNode = appendNode( aLayerNode, "Set" );
2850
2851 if( m_version > 'B' )
2852 addAttribute( tempSetNode, "geometryUsage", "GRAPHIC" );
2853
2854 addAttribute( tempSetNode, "componentRef", componentName( fp ) );
2855
2856 wxXmlNode* tempFeature = appendNode( tempSetNode, "Features" );
2857 addLocationNode( tempFeature, *shape );
2858
2859 addShape( tempFeature, *shape );
2860 }
2861 else if( shape->GetShape() == SHAPE_T::CIRCLE
2862 || shape->GetShape() == SHAPE_T::RECTANGLE
2863 || shape->GetShape() == SHAPE_T::POLY )
2864 {
2865 wxXmlNode* tempSetNode = appendNode( aLayerNode, "Set" );
2866
2867 if( shape->GetNetCode() > 0 )
2868 addAttribute( tempSetNode, "net", genString( shape->GetNetname(), "NET" ) );
2869
2870 wxXmlNode* tempFeature = appendNode( tempSetNode, "Features" );
2871 addLocationNode( tempFeature, *shape );
2872 addShape( tempFeature, *shape );
2873 }
2874 else
2875 {
2876 addShape( specialNode, *shape );
2877 }
2878 };
2879
2880 auto add_text =
2881 [&] ( BOARD_ITEM* text )
2882 {
2883 EDA_TEXT* text_item;
2884 FOOTPRINT* fp = text->GetParentFootprint();
2885
2886 if( PCB_TEXT* tmp_text = dynamic_cast<PCB_TEXT*>( text ) )
2887 text_item = static_cast<EDA_TEXT*>( tmp_text );
2888 else if( PCB_TEXTBOX* tmp_text = dynamic_cast<PCB_TEXTBOX*>( text ) )
2889 text_item = static_cast<EDA_TEXT*>( tmp_text );
2890
2891 if( !text_item->IsVisible() || text_item->GetShownText( false ).empty() )
2892 return;
2893
2894 wxXmlNode* tempSetNode = appendNode( aLayerNode, "Set" );
2895
2896 if( m_version > 'B' )
2897 addAttribute( tempSetNode, "geometryUsage", "TEXT" );
2898
2899 if( fp )
2900 addAttribute( tempSetNode, "componentRef", componentName( fp ) );
2901
2902 wxXmlNode* nonStandardAttributeNode = appendNode( tempSetNode, "NonstandardAttribute" );
2903 addAttribute( nonStandardAttributeNode, "name", "TEXT" );
2904 addAttribute( nonStandardAttributeNode, "value", text_item->GetShownText( false ) );
2905 addAttribute( nonStandardAttributeNode, "type", "STRING" );
2906
2907 wxXmlNode* tempFeature = appendNode( tempSetNode, "Features" );
2908 addLocationNode( tempFeature, 0.0, 0.0 );
2909
2910 if( text->Type() == PCB_TEXT_T && static_cast<PCB_TEXT*>( text )->IsKnockout() )
2911 addKnockoutText( tempFeature, static_cast<PCB_TEXT*>( text ) );
2912 else
2913 addText( tempFeature, text_item, text->GetFontMetrics() );
2914
2915 if( text->Type() == PCB_TEXTBOX_T )
2916 {
2917 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( text );
2918
2919 if( textbox->IsBorderEnabled() )
2920 addShape( tempFeature, *static_cast<PCB_SHAPE*>( textbox ) );
2921 }
2922 };
2923
2924 auto add_pad =
2925 [&]( PAD* pad )
2926 {
2927 if( !padSetNode )
2928 {
2929 if( !has_via )
2930 {
2931 padSetNode = layerSetNode;
2932 has_pad = true;
2933 }
2934 else
2935 {
2936 padSetNode = appendNode( aLayerNode, "Set" );
2937
2938 if( pad->GetNetCode() > 0 )
2939 addAttribute( padSetNode, "net", genString( pad->GetNetname(), "NET" ) );
2940 }
2941 }
2942
2943 FOOTPRINT* fp = pad->GetParentFootprint();
2944
2945 if( fp && fp->IsFlipped() )
2946 addPad( padSetNode, pad, FlipLayer( aLayer ) );
2947 else
2948 addPad( padSetNode, pad, aLayer );
2949 };
2950
2951 for( BOARD_ITEM* item : aItems )
2952 {
2953 switch( item->Type() )
2954 {
2955 case PCB_TRACE_T:
2956 case PCB_ARC_T:
2957 case PCB_VIA_T:
2958 add_track( static_cast<PCB_TRACK*>( item ) );
2959 break;
2960
2961 case PCB_ZONE_T:
2962 add_zone( static_cast<ZONE*>( item ) );
2963 break;
2964
2965 case PCB_PAD_T:
2966 add_pad( static_cast<PAD*>( item ) );
2967 break;
2968
2969 case PCB_SHAPE_T:
2970 add_shape( static_cast<PCB_SHAPE*>( item ) );
2971 break;
2972
2973 case PCB_TEXT_T:
2974 case PCB_TEXTBOX_T:
2975 case PCB_FIELD_T:
2976 add_text( item );
2977 break;
2978
2979 case PCB_DIMENSION_T:
2980 case PCB_TARGET_T:
2981 case PCB_DIM_ALIGNED_T:
2982 case PCB_DIM_LEADER_T:
2983 case PCB_DIM_CENTER_T:
2984 case PCB_DIM_RADIAL_T:
2986 //TODO: Add support for dimensions
2987 break;
2988
2989 default:
2990 wxLogTrace( traceIpc2581, wxS( "Unhandled type %s" ),
2991 ENUM_MAP<KICAD_T>::Instance().ToString( item->Type() ) );
2992 }
2993 }
2994
2995 if( specialNode->GetChildren() == nullptr )
2996 {
2997 featureSetNode->RemoveChild( specialNode );
2998 delete specialNode;
2999 }
3000
3001 if( featureSetNode->GetChildren() == nullptr )
3002 {
3003 layerSetNode->RemoveChild( featureSetNode );
3004 delete featureSetNode;
3005 }
3006
3007 if( layerSetNode->GetChildren() == nullptr )
3008 {
3009 aLayerNode->RemoveChild( layerSetNode );
3010 delete layerSetNode;
3011 }
3012}
3013
3014
3016{
3017 if( m_progressReporter )
3018 m_progressReporter->AdvancePhase( _( "Generating BOM section" ) );
3019
3020 wxXmlNode* avl = appendNode( m_xml_root, "Avl" );
3021 addAttribute( avl, "name", "Primary_Vendor_List" );
3022
3023 wxXmlNode* header = appendNode( avl, "AvlHeader" );
3024 addAttribute( header, "title", "BOM" );
3025 addAttribute( header, "source", "KiCad" );
3026 addAttribute( header, "author", "OWNER" );
3027 addAttribute( header, "datetime", wxDateTime::Now().FormatISOCombined() );
3028 addAttribute( header, "version", "1" );
3029
3030 std::set<wxString> unique_parts;
3031 std::map<wxString,wxString> unique_vendors;
3032
3033 for( auto& [fp, name] : m_OEMRef_dict )
3034 {
3035 auto [ it, success ] = unique_parts.insert( name );
3036
3037 if( !success )
3038 continue;
3039
3040 wxXmlNode* part = appendNode( avl, "AvlItem" );
3041 addAttribute( part, "OEMDesignNumber", genString( name, "REF" ) );
3042
3043 PCB_FIELD* nums[2] = { fp->GetFieldByName( m_mpn ), fp->GetFieldByName( m_distpn ) };
3044 PCB_FIELD* company[2] = { fp->GetFieldByName( m_mfg ), nullptr };
3045 wxString company_name[2] = { m_mfg, m_dist };
3046
3047 for ( int ii = 0; ii < 2; ++ii )
3048 {
3049 if( nums[ii] )
3050 {
3051 wxString mpn_name = nums[ii]->GetShownText( false );
3052
3053 if( mpn_name.empty() )
3054 continue;
3055
3056 wxXmlNode* vmpn = appendNode( part, "AvlVmpn" );
3057 addAttribute( vmpn, "qualified", "false" );
3058 addAttribute( vmpn, "chosen", "false" );
3059
3060 wxXmlNode* mpn = appendNode( vmpn, "AvlMpn" );
3061 addAttribute( mpn, "name", mpn_name );
3062
3063 wxXmlNode* vendor = appendNode( vmpn, "AvlVendor" );
3064
3065 wxString name = wxT( "UNKNOWN" );
3066
3067 // If the field resolves, then use that field content unless it is empty
3068 if( !ii && company[ii] )
3069 {
3070 wxString tmp = company[ii]->GetShownText( false );
3071
3072 if( !tmp.empty() )
3073 name = tmp;
3074 }
3075 // If it doesn't resolve but there is content from the dialog, use the static content
3076 else if( !ii && !company_name[ii].empty() )
3077 {
3078 name = company_name[ii];
3079 }
3080 else if( ii && !m_dist.empty() )
3081 {
3082 name = m_dist;
3083 }
3084
3085 auto [vendor_id, inserted] = unique_vendors.emplace(
3086 name,
3087 wxString::Format( "VENDOR_%zu", unique_vendors.size() ) );
3088
3089 addAttribute( vendor, "enterpriseRef", vendor_id->second );
3090
3091 if( inserted )
3092 {
3093 wxXmlNode* new_vendor = new wxXmlNode( wxXML_ELEMENT_NODE, "Enterprise" );
3094 addAttribute( new_vendor, "id", vendor_id->second );
3095 addAttribute( new_vendor, "name", name );
3096 addAttribute( new_vendor, "code", "NONE" );
3097 insertNodeAfter( m_enterpriseNode, new_vendor );
3098 m_enterpriseNode = new_vendor;
3099 }
3100 }
3101 }
3102 }
3103
3104 return avl;
3105}
3106
3107
3108void PCB_IO_IPC2581::SaveBoard( const wxString& aFileName, BOARD* aBoard,
3109 const std::map<std::string, UTF8>* aProperties )
3110{
3111 m_board = aBoard;
3112 m_units_str = "MILLIMETER";
3113 m_scale = 1.0 / PCB_IU_PER_MM;
3114 m_sigfig = 6;
3115
3116 if( auto it = aProperties->find( "units" ); it != aProperties->end() )
3117 {
3118 if( it->second == "inch" )
3119 {
3120 m_units_str = "INCH";
3121 m_scale = ( 1.0 / 25.4 ) / PCB_IU_PER_MM;
3122 }
3123 }
3124
3125 if( auto it = aProperties->find( "sigfig" ); it != aProperties->end() )
3126 m_sigfig = std::stoi( it->second );
3127
3128 if( auto it = aProperties->find( "version" ); it != aProperties->end() )
3129 m_version = it->second.c_str()[0];
3130
3131 if( auto it = aProperties->find( "OEMRef" ); it != aProperties->end() )
3132 m_OEMRef = it->second.wx_str();
3133
3134 if( auto it = aProperties->find( "mpn" ); it != aProperties->end() )
3135 m_mpn = it->second.wx_str();
3136
3137 if( auto it = aProperties->find( "mfg" ); it != aProperties->end() )
3138 m_mfg = it->second.wx_str();
3139
3140 if( auto it = aProperties->find( "dist" ); it != aProperties->end() )
3141 m_dist = it->second.wx_str();
3142
3143 if( auto it = aProperties->find( "distpn" ); it != aProperties->end() )
3144 m_distpn = it->second.wx_str();
3145
3146 if( m_version == 'B' )
3147 {
3148 for( char c = 'a'; c <= 'z'; ++c )
3149 m_acceptable_chars.insert( c );
3150
3151 for( char c = 'A'; c <= 'Z'; ++c )
3152 m_acceptable_chars.insert( c );
3153
3154 for( char c = '0'; c <= '9'; ++c )
3155 m_acceptable_chars.insert( c );
3156
3157 // Add special characters
3158 std::string specialChars = "_\\-.+><";
3159
3160 for( char c : specialChars )
3161 m_acceptable_chars.insert( c );
3162 }
3163
3164 m_xml_doc = new wxXmlDocument();
3166
3168
3169 if( m_progressReporter )
3170 {
3173 m_progressReporter->Report( _( "Generating logistic section" ) );
3174 }
3175
3178
3179 wxXmlNode* ecad_node = generateEcadSection();
3180 generateBOMSection( ecad_node );
3182
3183 if( m_progressReporter )
3184 {
3185 m_progressReporter->AdvancePhase( _( "Saving file" ) );
3186 }
3187
3188 wxFileOutputStreamWithProgress out_stream( aFileName );
3189 double written_bytes = 0.0;
3190 double last_yield = 0.0;
3191
3192 // This is a rough estimation of the size of the spaces in the file
3193 // We just need to total to be slightly larger than the value of the
3194 // progress bar, so accurately counting spaces is not terribly important
3196
3197 auto update_progress = [&]( size_t aBytes )
3198 {
3199 written_bytes += aBytes;
3200 double percent = written_bytes / static_cast<double>( m_total_bytes );
3201
3202 if( m_progressReporter )
3203 {
3204 // Only update every percent
3205 if( last_yield + 0.01 < percent )
3206 {
3207 last_yield = percent;
3209 }
3210 }
3211 };
3212
3213 out_stream.SetProgressCallback( update_progress );
3214
3215 if( !m_xml_doc->Save( out_stream ) )
3216 {
3217 wxLogError( _( "Failed to save file to buffer" ) );
3218 return;
3219 }
3220
3221 size_t size = out_stream.GetSize();
3222}
int color
Definition: DXF_plotter.cpp:60
const char * name
Definition: DXF_plotter.cpp:59
@ ERROR_INSIDE
Definition: approximation.h:34
constexpr int ARC_HIGH_DEF
Definition: base_units.h:120
constexpr double PCB_IU_PER_MM
Pcbnew IU is 1 nanometer.
Definition: base_units.h:70
bool IsPrmSpecified(const wxString &aPrmValue)
@ BS_ITEM_TYPE_COPPER
Definition: board_stackup.h:45
@ BS_ITEM_TYPE_SILKSCREEN
Definition: board_stackup.h:51
@ BS_ITEM_TYPE_DIELECTRIC
Definition: board_stackup.h:46
@ BS_ITEM_TYPE_SOLDERMASK
Definition: board_stackup.h:49
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
wxString GetMajorMinorPatchVersion()
Get the major, minor and patch version in a string major.minor.patch This is extracted by CMake from ...
Bezier curves to polygon converter.
Definition: bezier_curves.h:38
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMaxError=10)
Convert a Bezier curve to a polygon.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
Container for design settings for a BOARD object.
BOARD_STACKUP & GetStackupDescriptor()
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
virtual bool IsKnockout() const
Definition: board_item.h:326
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
double GetEpsilonR(int aDielectricSubLayer=0) const
wxString GetColor(int aDielectricSubLayer=0) const
wxString GetLayerName() const
PCB_LAYER_ID GetBrdLayerId() const
int GetThickness(int aDielectricSubLayer=0) const
BOARD_STACKUP_ITEM_TYPE GetType() const
wxString GetMaterial(int aDielectricSubLayer=0) const
int GetDielectricLayerId() const
double GetLossTangent(int aDielectricSubLayer=0) const
Manage layers needed to make a physical board.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
int GetCount() const
bool SynchronizeWithBoard(BOARD_DESIGN_SETTINGS *aSettings)
Synchronize the BOARD_STACKUP_ITEM* list with the board.
int BuildBoardThicknessFromStackup() const
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:295
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:2536
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:879
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:817
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:843
const FOOTPRINTS & Footprints() const
Definition: board.h:336
const TRACKS & Tracks() const
Definition: board.h:334
const wxString & GetFileName() const
Definition: board.h:332
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:613
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:934
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:658
constexpr coord_type GetLeft() const
Definition: box2.h:228
constexpr coord_type GetRight() const
Definition: box2.h:217
constexpr coord_type GetTop() const
Definition: box2.h:229
constexpr coord_type GetBottom() const
Definition: box2.h:222
EDA_ANGLE Normalize()
Definition: eda_angle.h:221
double AsDegrees() const
Definition: eda_angle.h:113
EDA_ANGLE Invert() const
Definition: eda_angle.h:165
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:419
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:291
int GetRadius() const
Definition: eda_shape.cpp:840
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:873
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:210
int GetRectangleHeight() const
Definition: eda_shape.cpp:405
bool IsClockwiseArc() const
Definition: eda_shape.cpp:923
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:365
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:1958
EDA_ITEM * Clone() const override
Invoke a function on all children.
Definition: footprint.cpp:2129
std::deque< PAD * > & Pads()
Definition: footprint.h:204
bool IsFlipped() const
Definition: footprint.h:389
const wxString & GetReference() const
Definition: footprint.h:620
PROGRESS_REPORTER * m_progressReporter
Progress reporter to track the progress of the operation, may be nullptr.
Definition: io_base.h:226
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:37
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:295
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:435
int GetSizeX() const
Definition: pad.h:278
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:442
bool FlashLayer(int aLayer, bool aOnlyCheckIfPermitted=false) const
Check to see whether the pad should be flashed on the specific layer.
Definition: pad.cpp:341
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:512
int GetDrillSizeY() const
Definition: pad.h:307
const VECTOR2I & GetDrillSize() const
Definition: pad.h:303
PAD_ATTRIB GetAttribute() const
Definition: pad.h:438
const wxString & GetNumber() const
Definition: pad.h:134
const VECTOR2I & GetDelta(PCB_LAYER_ID aLayer) const
Definition: pad.h:297
VECTOR2I GetPosition() const override
Definition: pad.h:206
int GetDrillSizeX() const
Definition: pad.h:305
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition: pad.h:193
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
Definition: pad.cpp:1125
const VECTOR2I & GetOffset(PCB_LAYER_ID aLayer) const
Definition: pad.h:315
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition: pad.h:406
int GetChamferPositions(PCB_LAYER_ID aLayer) const
Definition: pad.h:703
double GetChamferRectRatio(PCB_LAYER_ID aLayer) const
Definition: pad.h:686
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:1171
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:262
const VECTOR2I & GetMid() const
Definition: pcb_track.h:305
void addText(wxXmlNode *aContentNode, EDA_TEXT *aShape, const KIFONT::METRICS &aFontMetrics)
wxString floatVal(double aVal, int aSigFig=-1) const
void generateLayerSetDrill(wxXmlNode *aStepNode)
wxString genLayerString(PCB_LAYER_ID aLayer, const char *aPrefix) const
wxXmlNode * generateContentSection()
Creates the Content section of the XML file.
wxXmlNode * appendNode(wxXmlNode *aParent, const wxString &aName)
void generateDrillLayers(wxXmlNode *aCadLayerNode)
wxXmlNode * addPackage(wxXmlNode *aStepNode, FOOTPRINT *aFootprint)
void generateComponents(wxXmlNode *aStepNode)
bool addContourNode(wxXmlNode *aParentNode, const SHAPE_POLY_SET &aPolySet, int aOutline=0, FILL_T aFillType=FILL_T::FILLED_SHAPE, int aWidth=0, LINE_STYLE aDashType=LINE_STYLE::SOLID)
wxString componentName(FOOTPRINT *aFootprint)
bool addOutlineNode(wxXmlNode *aParentNode, const SHAPE_POLY_SET &aPolySet, int aWidth=0, LINE_STYLE aDashType=LINE_STYLE::SOLID)
void generateStackup(wxXmlNode *aCadLayerNode)
bool addPolygonNode(wxXmlNode *aParentNode, const SHAPE_LINE_CHAIN &aPolygon, FILL_T aFillType=FILL_T::FILLED_SHAPE, int aWidth=0, LINE_STYLE aDashType=LINE_STYLE::SOLID)
void generateLayerSetNet(wxXmlNode *aLayerNode, PCB_LAYER_ID aLayer, std::vector< BOARD_ITEM * > &aItems)
bool isValidLayerFor2581(PCB_LAYER_ID aLayer)
void insertNodeAfter(wxXmlNode *aPrev, wxXmlNode *aNode)
void generateCadLayers(wxXmlNode *aCadLayerNode)
std::vector< wxXmlNode * > m_padstacks
wxString pinName(const PAD *aPad) const
void generateCadSpecs(wxXmlNode *aCadLayerNode)
void addVia(wxXmlNode *aContentNode, const PCB_VIA *aVia, PCB_LAYER_ID aLayer)
void addSlotCavity(wxXmlNode *aContentNode, const PAD &aPad, const wxString &aName)
wxXmlNode * m_last_padstack
size_t lineHash(int aWidth, LINE_STYLE aDashType)
std::map< size_t, wxString > m_std_shape_dict
void addAttribute(wxXmlNode *aNode, const wxString &aName, const wxString &aValue)
wxXmlNode * m_shape_user_node
wxXmlNode * generateAvlSection()
Creates the Approved Vendor List section.
wxXmlNode * generateHistorySection()
Creates the history section.
void addXY(wxXmlNode *aNode, const VECTOR2I &aVec, const char *aXName=nullptr, const char *aYName=nullptr)
wxXmlNode * m_shape_std_node
void addPadStack(wxXmlNode *aContentNode, const PAD *aPad)
std::map< FOOTPRINT *, wxString > m_OEMRef_dict
void clearLoadedFootprints()
Frees the memory allocated for the loaded footprints in m_loaded_footprints.
wxString m_units_str
std::map< std::pair< PCB_LAYER_ID, PCB_LAYER_ID >, std::vector< PAD * > > m_slot_holes
std::map< size_t, wxString > m_footprint_dict
wxXmlNode * generateLogisticSection()
Creates the logistical data header.
std::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
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)
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.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const std::vector< VECTOR2I > & CPoints() const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Represent a set of closed polygons.
void Fracture()
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the set.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
void InflateWithLinkedHoles(int aFactor, CORNER_STRATEGY aCornerStrategy, int aMaxError)
Perform outline inflation/deflation, using round corners.
int GetWidth() const
LINE_STYLE GetLineStyle() const
Handle a list of polygons defining a copper zone.
Definition: zone.h: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
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:405
FILL_T
Definition: eda_shape.h:56
@ FP_SMD
Definition: footprint.h:80
@ FP_EXCLUDE_FROM_BOM
Definition: footprint.h:82
@ FP_THROUGH_HOLE
Definition: footprint.h:79
static const wxChar traceIpc2581[]
This program source code file is part of KiCad, a free EDA CAD application.
static constexpr void hash_combine(std::size_t &seed)
This is a dummy function to take the final case of hash_combine below.
Definition: hash.h:32
static constexpr std::size_t hash_val(const Types &... args)
Definition: hash.h:51
size_t hash_fp_item(const EDA_ITEM *aItem, int aFlags)
Calculate hash of an EDA_ITEM.
Definition: hash_eda.cpp:55
Hashing functions for EDA_ITEMs.
@ HASH_POS
Definition: hash_eda.h:47
@ REL_COORD
Use coordinates relative to the parent object.
Definition: hash_eda.h:50
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayerId, int aCopperLayersCount)
Definition: layer_id.cpp:210
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:581
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:559
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:695
VECTOR2< double > VECTOR2D
Definition: vector2d.h:694