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