KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
exporter_step.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2022 Mark Roszko <mark.roszko@gmail.com>
5 * Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include "exporter_step.h"
27#include <advanced_config.h>
28#include <board.h>
30#include <footprint.h>
31#include <pcb_textbox.h>
32#include <pcb_table.h>
33#include <pcb_tablecell.h>
34#include <pcb_track.h>
35#include <pcb_shape.h>
36#include <pad.h>
37#include <zone.h>
38#include <fp_lib_table.h>
39#include "step_pcb_model.h"
40
41#include <pgm_base.h>
42#include <base_units.h>
43#include <filename_resolver.h>
44#include <trace_helpers.h>
45#include <project_pcb.h>
47
48#include <Message.hxx> // OpenCascade messenger
49#include <Message_PrinterOStream.hxx> // OpenCascade output messenger
50#include <Standard_Failure.hxx> // In open cascade
51
52#include <Standard_Version.hxx>
53
54#include <wx/crt.h>
55#include <wx/log.h>
56#include <core/profile.h> // To use GetRunningMicroSecs or another profiling utility
57
58#define OCC_VERSION_MIN 0x070500
59
60#if OCC_VERSION_HEX < OCC_VERSION_MIN
61#include <Message_Messenger.hxx>
62#endif
63
64
65void ReportMessage( const wxString& aMessage )
66{
67 wxPrintf( aMessage );
68 fflush( stdout ); // Force immediate printing (needed on mingw)
69}
70
71class KiCadPrinter : public Message_Printer
72{
73public:
74 KiCadPrinter( EXPORTER_STEP* aConverter ) : m_converter( aConverter ) {}
75
76protected:
77#if OCC_VERSION_HEX < OCC_VERSION_MIN
78 virtual void Send( const TCollection_ExtendedString& theString,
79 const Message_Gravity theGravity,
80 const Standard_Boolean theToPutEol ) const override
81 {
82 Send( TCollection_AsciiString( theString ), theGravity, theToPutEol );
83 }
84
85 virtual void Send( const TCollection_AsciiString& theString,
86 const Message_Gravity theGravity,
87 const Standard_Boolean theToPutEol ) const override
88#else
89 virtual void send( const TCollection_AsciiString& theString,
90 const Message_Gravity theGravity ) const override
91#endif
92 {
93 if( theGravity >= Message_Warning
94 || ( wxLog::IsAllowedTraceMask( traceKiCad2Step ) && theGravity == Message_Info ) )
95 {
96 ReportMessage( theString.ToCString() );
97
98#if OCC_VERSION_HEX < OCC_VERSION_MIN
99 if( theToPutEol )
100 ReportMessage( wxT( "\n" ) );
101#else
102 ReportMessage( wxT( "\n" ) );
103#endif
104 }
105
106 if( theGravity == Message_Warning )
108
109 if( theGravity >= Message_Alarm )
111
112 if( theGravity == Message_Fail )
114 }
115
116private:
118};
119
120
122 m_params( aParams ),
123 m_error( false ),
124 m_fail( false ),
125 m_warn( false ),
126 m_board( aBoard ),
127 m_pcbModel( nullptr )
128{
129 m_copperColor = COLOR4D( 0.7, 0.61, 0.0, 1.0 );
130
132 m_padColor = COLOR4D( 0.50, 0.50, 0.50, 1.0 );
133 else
135
136 // TODO: make configurable
138
139 // Init m_pcbBaseName to the board short filename (no path, no ext)
140 // m_pcbName is used later to identify items in step file
141 wxFileName fn( aBoard->GetFileName() );
142 m_pcbBaseName = fn.GetName();
143
144 // Remove the autosave prefix
146
147 m_resolver = std::make_unique<FILENAME_RESOLVER>();
148 m_resolver->Set3DConfigDir( wxT( "" ) );
149 // needed to add the project to the search stack
150 m_resolver->SetProject( aBoard->GetProject() );
151 m_resolver->SetProgramBase( &Pgm() );
152}
153
154
156{
157}
158
159
161 SHAPE_POLY_SET* aClipPolygon )
162{
163 bool hasdata = false;
164 std::vector<PAD*> padsMatchingNetFilter;
165 int maxError = m_board->GetDesignSettings().m_MaxError;
166
167 // Dump the pad holes into the PCB
168 for( PAD* pad : aFootprint->Pads() )
169 {
170 bool castellated = pad->GetProperty() == PAD_PROP::CASTELLATED;
171 std::shared_ptr<SHAPE_SEGMENT> holeShape = pad->GetEffectiveHoleShape();
172
173 SHAPE_POLY_SET holePoly;
174 holeShape->TransformToPolygon( holePoly, maxError, ERROR_INSIDE );
175
176 for( PCB_LAYER_ID pcblayer : pad->GetLayerSet().Seq() )
177 {
178 if( pad->IsOnLayer( pcblayer ) )
179 m_poly_holes[pcblayer].Append( holePoly );
180 }
181
182 if( pad->HasHole() )
183 {
184 int platingThickness = pad->GetAttribute() == PAD_ATTRIB::PTH ? m_platingThickness : 0;
185
186 if( m_pcbModel->AddHole( *holeShape, platingThickness, F_Cu, B_Cu, false, aOrigin, true,
187 true ) )
188 {
189 hasdata = true;
190 }
191
193 //if( m_layersToExport.Contains( F_SilkS ) || m_layersToExport.Contains( B_SilkS ) )
194 //{
195 // m_poly_holes[F_SilkS].Append( holePoly );
196 // m_poly_holes[B_SilkS].Append( holePoly );
197 //}
198 }
199
200 if( !m_params.m_NetFilter.IsEmpty() && !pad->GetNetname().Matches( m_params.m_NetFilter ) )
201 continue;
202
204 {
205 if( m_pcbModel->AddPadShape( pad, aOrigin, false,
206 castellated ? aClipPolygon : nullptr) )
207 hasdata = true;
208
210 {
211 for( PCB_LAYER_ID pcblayer : pad->GetLayerSet().Seq() )
212 {
213 if( pcblayer != F_Mask && pcblayer != B_Mask )
214 continue;
215
216 SHAPE_POLY_SET poly;
217 PCB_LAYER_ID cuLayer = ( pcblayer == F_Mask ) ? F_Cu : B_Cu;
218 pad->TransformShapeToPolygon( poly, cuLayer,
219 pad->GetSolderMaskExpansion( cuLayer ), maxError,
220 ERROR_INSIDE );
221
222 m_poly_shapes[pcblayer].Append( poly );
223 }
224 }
225 }
226
227 padsMatchingNetFilter.push_back( pad );
228 }
229
230 // Build 3D shapes of the footprint graphic items:
231 for( PCB_LAYER_ID pcblayer : m_layersToExport.Seq() )
232 {
233 if( IsCopperLayer( pcblayer ) && !m_params.m_ExportTracksVias )
234 continue;
235
236 SHAPE_POLY_SET buffer;
237
238 aFootprint->TransformFPShapesToPolySet( buffer, pcblayer, 0, maxError, ERROR_INSIDE,
239 true, /* include text */
240 true, /* include shapes */
241 false /* include private items */ );
242
243 if( m_params.m_NetFilter.IsEmpty() || !IsCopperLayer( pcblayer ) )
244 {
245 m_poly_shapes[pcblayer].Append( buffer );
246 }
247 else
248 {
249 // Only add shapes colliding with any matching pads
250 for( const SHAPE_POLY_SET::POLYGON& poly : buffer.CPolygons() )
251 {
252 for( PAD* pad : padsMatchingNetFilter )
253 {
254 if( !pad->IsOnLayer( pcblayer ) )
255 continue;
256
257 std::shared_ptr<SHAPE_POLY_SET> padPoly = pad->GetEffectivePolygon( pcblayer );
258 SHAPE_POLY_SET gfxPoly( poly );
259
260 if( padPoly->Collide( &gfxPoly ) )
261 {
262 m_poly_shapes[pcblayer].Append( gfxPoly );
263 break;
264 }
265 }
266 }
267 }
268 }
269
270 if( ( !(aFootprint->GetAttributes() & (FP_THROUGH_HOLE|FP_SMD)) ) && !m_params.m_IncludeUnspecified )
271 {
272 return hasdata;
273 }
274
275 if( ( aFootprint->GetAttributes() & FP_DNP ) && !m_params.m_IncludeDNP )
276 {
277 return hasdata;
278 }
279
280 // Prefetch the library for this footprint
281 // In case we need to resolve relative footprint paths
282 wxString libraryName = aFootprint->GetFPID().GetLibNickname();
283 wxString footprintBasePath = wxEmptyString;
284
285 double posX = aFootprint->GetPosition().x - aOrigin.x;
286 double posY = (aFootprint->GetPosition().y) - aOrigin.y;
287
288 if( m_board->GetProject() )
289 {
290 try
291 {
292 // FindRow() can throw an exception
293 const FP_LIB_TABLE_ROW* fpRow =
294 PROJECT_PCB::PcbFootprintLibs( m_board->GetProject() )->FindRow( libraryName, false );
295
296 if( fpRow )
297 footprintBasePath = fpRow->GetFullURI( true );
298 }
299 catch( ... )
300 {
301 // Do nothing if the libraryName is not found in lib table
302 }
303 }
304
305 // Exit early if we don't want to include footprint models
307 {
308 return hasdata;
309 }
310
311 bool componentFilter = !m_params.m_ComponentFilter.IsEmpty();
312 std::vector<wxString> componentFilterPatterns;
313
314 if( componentFilter )
315 {
316 wxStringTokenizer tokenizer( m_params.m_ComponentFilter, wxS( "," ), wxTOKEN_STRTOK );
317
318 while( tokenizer.HasMoreTokens() )
319 componentFilterPatterns.push_back( tokenizer.GetNextToken().Trim( false ) );
320
321 bool found = false;
322
323 for( const wxString& pattern : componentFilterPatterns )
324 {
325 if( aFootprint->GetReference().Matches( pattern ) )
326 {
327 found = true;
328 break;
329 }
330 }
331
332 if( !found )
333 return hasdata;
334 }
335
336 VECTOR2D newpos( pcbIUScale.IUTomm( posX ), pcbIUScale.IUTomm( posY ) );
337
338 for( const FP_3DMODEL& fp_model : aFootprint->Models() )
339 {
340 if( !fp_model.m_Show || fp_model.m_Filename.empty() )
341 continue;
342
343 std::vector<wxString> searchedPaths;
344 wxString mname = m_resolver->ResolvePath( fp_model.m_Filename, footprintBasePath, aFootprint );
345
346
347 if( mname.empty() || !wxFileName::FileExists( mname ) )
348 {
349 // the error path will return an empty name sometimes, at least report back the original filename
350 if( mname.empty() )
351 mname = fp_model.m_Filename;
352
353 ReportMessage( wxString::Format( wxT( "Could not add 3D model to %s.\n"
354 "File not found: %s\n" ),
355 aFootprint->GetReference(), mname ) );
356 continue;
357 }
358
359 std::string fname( mname.ToUTF8() );
360 std::string refName( aFootprint->GetReference().ToUTF8() );
361 try
362 {
363 bool bottomSide = aFootprint->GetLayer() == B_Cu;
364
365 // the rotation is stored in degrees but opencascade wants radians
366 VECTOR3D modelRot = fp_model.m_Rotation;
367 modelRot *= M_PI;
368 modelRot /= 180.0;
369
370 if( m_pcbModel->AddComponent( fname, refName, bottomSide,
371 newpos,
372 aFootprint->GetOrientation().AsRadians(),
373 fp_model.m_Offset, modelRot,
374 fp_model.m_Scale, m_params.m_SubstModels ) )
375 {
376 hasdata = true;
377 }
378 }
379 catch( const Standard_Failure& e )
380 {
381 ReportMessage( wxString::Format( wxT( "Could not add 3D model to %s.\n"
382 "OpenCASCADE error: %s\n" ),
383 aFootprint->GetReference(), e.GetMessageString() ) );
384 }
385
386 }
387
388 return hasdata;
389}
390
391
393{
394 bool skipCopper = !m_params.m_ExportTracksVias
395 || ( !m_params.m_NetFilter.IsEmpty()
396 && !aTrack->GetNetname().Matches( m_params.m_NetFilter ) );
397
398 int maxError = m_board->GetDesignSettings().m_MaxError;
399
401 {
402 if( aTrack->IsOnLayer( F_Mask ) )
403 {
404 SHAPE_POLY_SET poly;
405 aTrack->TransformShapeToPolygon( poly, F_Mask, 0, maxError, ERROR_INSIDE );
406
407 m_poly_shapes[F_Mask].Append( poly );
408 }
409
410 if( aTrack->IsOnLayer( B_Mask ) )
411 {
412 SHAPE_POLY_SET poly;
413 aTrack->TransformShapeToPolygon( poly, B_Mask, 0, maxError, ERROR_INSIDE );
414
415 m_poly_shapes[B_Mask].Append( poly );
416 }
417 }
418
419 if( aTrack->Type() == PCB_VIA_T )
420 {
421 PCB_VIA* via = static_cast<PCB_VIA*>( aTrack );
422
423 std::shared_ptr<SHAPE_SEGMENT> holeShape = via->GetEffectiveHoleShape();
424 SHAPE_POLY_SET holePoly;
425 holeShape->TransformToPolygon( holePoly, maxError, ERROR_INSIDE );
426
427 LSET layers( via->GetLayerSet() & m_layersToExport );
428
429 PCB_LAYER_ID top_layer, bot_layer;
430 via->LayerPair( &top_layer, &bot_layer );
431
432 if( !skipCopper )
433 {
434 for( PCB_LAYER_ID pcblayer : layers.Seq() )
435 {
436 const std::shared_ptr<SHAPE>& shape = via->GetEffectiveShape( pcblayer );
437
438 SHAPE_POLY_SET poly;
439 shape->TransformToPolygon( poly, maxError, ERROR_INSIDE );
440 m_poly_shapes[pcblayer].Append( poly );
441 m_poly_holes[pcblayer].Append( holePoly );
442 }
443
444 m_pcbModel->AddBarrel( *holeShape, top_layer, bot_layer, true, aOrigin );
445 }
446
448 //if( m_layersToExport.Contains( F_SilkS ) || m_layersToExport.Contains( B_SilkS ) )
449 //{
450 // m_poly_holes[F_SilkS].Append( holePoly );
451 // m_poly_holes[B_SilkS].Append( holePoly );
452 //}
453
454 m_pcbModel->AddHole( *holeShape, m_platingThickness, top_layer, bot_layer, true, aOrigin,
456
457 return true;
458 }
459
460 if( skipCopper )
461 return true;
462
463 PCB_LAYER_ID pcblayer = aTrack->GetLayer();
464
465 if( !m_layersToExport.Contains( pcblayer ) )
466 return false;
467
468 aTrack->TransformShapeToPolygon( m_poly_shapes[pcblayer], pcblayer, 0, maxError, ERROR_INSIDE );
469
470 return true;
471}
472
473
475{
476 for( ZONE* zone : m_board->Zones() )
477 {
478 LSET layers = zone->GetLayerSet();
479
480 if( ( layers & LSET::AllCuMask() ).count() && !m_params.m_NetFilter.IsEmpty()
481 && !zone->GetNetname().Matches( m_params.m_NetFilter ) )
482 {
483 continue;
484 }
485
486 for( PCB_LAYER_ID layer : layers.Seq() )
487 {
488 SHAPE_POLY_SET fill_shape;
489 zone->TransformSolidAreasShapesToPolygon( layer, fill_shape );
490 fill_shape.Unfracture();
491
492 fill_shape.SimplifyOutlines( ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel );
493
494 m_poly_shapes[layer].Append( fill_shape );
495 }
496 }
497}
498
499
501{
502 PCB_LAYER_ID pcblayer = aItem->GetLayer();
503
504 if( !m_layersToExport.Contains( pcblayer ) )
505 return false;
506
507 if( IsCopperLayer( pcblayer ) && !m_params.m_ExportTracksVias )
508 return false;
509
511 return false;
512
513 int maxError = m_board->GetDesignSettings().m_MaxError;
514
515 switch( aItem->Type() )
516 {
517 case PCB_SHAPE_T:
518 {
519 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
520
521 if( IsCopperLayer( pcblayer ) && !m_params.m_NetFilter.IsEmpty()
522 && !graphic->GetNetname().Matches( m_params.m_NetFilter ) )
523 {
524 return true;
525 }
526
527 graphic->TransformShapeToPolySet( m_poly_shapes[pcblayer], pcblayer, 0, maxError,
528 ERROR_INSIDE );
529
530 break;
531 }
532
533 case PCB_TEXT_T:
534 {
535 PCB_TEXT* text = static_cast<PCB_TEXT*>( aItem );
536
537 text->TransformTextToPolySet( m_poly_shapes[pcblayer], 0, maxError, ERROR_INSIDE );
538 break;
539 }
540
541 case PCB_TEXTBOX_T:
542 {
543 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( aItem );
544
545 textbox->TransformTextToPolySet( m_poly_shapes[pcblayer], 0, maxError, ERROR_INSIDE );
546 break;
547 }
548
549 case PCB_TABLE_T:
550 {
551 PCB_TABLE* table = static_cast<PCB_TABLE*>( aItem );
552
553 for( PCB_TABLECELL* cell : table->GetCells() )
554 cell->TransformTextToPolySet( m_poly_shapes[pcblayer], 0, maxError, ERROR_INSIDE );
555
556 table->DrawBorders(
557 [&]( const VECTOR2I& ptA, const VECTOR2I& ptB,
558 const STROKE_PARAMS& stroke )
559 {
560 SHAPE_SEGMENT seg( ptA, ptB, stroke.GetWidth() );
561 seg.TransformToPolygon( m_poly_shapes[pcblayer], maxError, ERROR_INSIDE );
562 } );
563
564 break;
565 }
566
567 default: wxFAIL_MSG( "buildGraphic3DShape: unhandled item type" );
568 }
569
570 return true;
571}
572
573
575{
576 // Specialize the STEP_PCB_MODEL generator for specific output format
577 // it can have some minor actions for the generator
578 switch( m_params.m_Format )
579 {
581 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_STEP );
582 break;
583
585 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_BREP );
586 break;
587
589 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_XAO );
590 break;
591
593 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_GLTF );
594 break;
595
597 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_PLY );
598 break;
599
601 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_STL );
602 break;
603
604 default:
605 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_UNKNOWN );
606 break;
607 }
608}
609
610
612{
613 if( m_pcbModel )
614 return true;
615
616 SHAPE_POLY_SET pcbOutlines; // stores the board main outlines
617
618 if( !m_board->GetBoardPolygonOutlines( pcbOutlines,
619 /* error handler */ nullptr,
620 /* allows use arcs in outlines */ true ) )
621 {
622 wxLogWarning( _( "Board outline is malformed. Run DRC for a full analysis." ) );
623 }
624
625 SHAPE_POLY_SET pcbOutlinesNoArcs = pcbOutlines;
626 pcbOutlinesNoArcs.ClearArcs();
627
628 VECTOR2D origin;
629
630 // Determine the coordinate system reference:
631 // Precedence of reference point is Drill Origin > Grid Origin > User Offset
634 else if( m_params.m_UseGridOrigin )
636 else
637 origin = m_params.m_Origin;
638
639 m_pcbModel = std::make_unique<STEP_PCB_MODEL>( m_pcbBaseName );
640
642
644 m_pcbModel->SetPadColor( m_padColor.r, m_padColor.g, m_padColor.b );
645
646 m_pcbModel->SetStackup( m_board->GetStackupOrDefault() );
647 m_pcbModel->SetEnabledLayers( m_layersToExport );
648 m_pcbModel->SetFuseShapes( m_params.m_FuseShapes );
649 m_pcbModel->SetNetFilter( m_params.m_NetFilter );
650
651 // Note: m_params.m_BoardOutlinesChainingEpsilon is used only to build the board outlines,
652 // not to set OCC chaining epsilon (much smaller)
653 //
654 // Set the min distance between 2 points for OCC to see these 2 points as merged
655 // OCC_MAX_DISTANCE_TO_MERGE_POINTS is acceptable for OCC, otherwise there are issues
656 // to handle the shapes chaining on copper layers, because the Z dist is 0.035 mm and the
657 // min dist must be much smaller (we use 0.001 mm giving good results)
658 m_pcbModel->OCCSetMergeMaxDistance( OCC_MAX_DISTANCE_TO_MERGE_POINTS );
659
661
662 // For copper layers, only pads and tracks are added, because adding everything on copper
663 // generate unreasonable file sizes and take a unreasonable calculation time.
664 for( FOOTPRINT* fp : m_board->Footprints() )
665 buildFootprint3DShapes( fp, origin, &pcbOutlinesNoArcs );
666
667 for( PCB_TRACK* track : m_board->Tracks() )
668 buildTrack3DShape( track, origin );
669
670 for( BOARD_ITEM* item : m_board->Drawings() )
671 buildGraphic3DShape( item, origin );
672
674 {
675 buildZones3DShape( origin );
676 }
677
678 for( PCB_LAYER_ID pcblayer : m_layersToExport.Seq() )
679 {
680 SHAPE_POLY_SET poly = m_poly_shapes[pcblayer];
681 poly.Simplify();
682
683 poly.SimplifyOutlines( pcbIUScale.mmToIU( 0.003 ) );
684 poly.Simplify();
685
686 SHAPE_POLY_SET holes = m_poly_holes[pcblayer];
687 holes.Simplify();
688
689 // Mask layer is negative
690 if( pcblayer == F_Mask || pcblayer == B_Mask )
691 {
692 SHAPE_POLY_SET mask = pcbOutlinesNoArcs;
693
694 mask.BooleanSubtract( poly );
695 mask.BooleanSubtract( holes );
696
697 poly = mask;
698 }
699 else
700 {
701 // Subtract holes
702 poly.BooleanSubtract( holes );
703
704 // Clip to board outline
705 poly.BooleanIntersection( pcbOutlinesNoArcs );
706 }
707
708 m_pcbModel->AddPolygonShapes( &poly, pcblayer, origin );
709 }
710
711 ReportMessage( wxT( "Create PCB solid model\n" ) );
712
713 wxString msg;
714 msg.Printf( wxT( "Board outline: find %d initial points\n" ), pcbOutlines.FullPointCount() );
715 ReportMessage( msg );
716
717 if( !m_pcbModel->CreatePCB( pcbOutlines, origin, m_params.m_ExportBoardBody ) )
718 {
719 ReportMessage( wxT( "could not create PCB solid model\n" ) );
720 return false;
721 }
722
723 return true;
724}
725
726
728{
729 // Display the export time, for statistics
730 int64_t stats_startExportTime = GetRunningMicroSecs();
731
732 // setup opencascade message log
733 Message::DefaultMessenger()->RemovePrinters( STANDARD_TYPE( Message_PrinterOStream ) );
734 Message::DefaultMessenger()->AddPrinter( new KiCadPrinter( this ) );
735
736 ReportMessage( _( "Determining PCB data\n" ) );
737
738 if( m_params.m_OutputFile.IsEmpty() )
739 {
740 wxFileName fn = m_board->GetFileName();
741 fn.SetName( fn.GetName() );
742 fn.SetExt( m_params.GetDefaultExportExtension() );
743
744 m_params.m_OutputFile = fn.GetFullName();
745 }
746
748
751
753 {
756 }
757
759 {
762 }
763
765
766 try
767 {
768 ReportMessage( wxString::Format( _( "Build %s data\n" ), m_params.GetFormatName() ) );
769
770 if( !buildBoard3DShapes() )
771 {
772 ReportMessage( _( "\n** Error building STEP board model. Export aborted. **\n" ) );
773 return false;
774 }
775
776 ReportMessage( wxString::Format( _( "Writing %s file\n" ), m_params.GetFormatName() ) );
777
778 bool success = true;
780 success = m_pcbModel->WriteSTEP( m_outputFile, m_params.m_OptimizeStep );
782 success = m_pcbModel->WriteBREP( m_outputFile );
784 success = m_pcbModel->WriteXAO( m_outputFile );
786 success = m_pcbModel->WriteGLTF( m_outputFile );
788 success = m_pcbModel->WritePLY( m_outputFile );
790 success = m_pcbModel->WriteSTL( m_outputFile );
791
792 if( !success )
793 {
794 ReportMessage( wxString::Format( _( "\n** Error writing %s file. **\n" ),
796 return false;
797 }
798 else
799 {
800 ReportMessage( wxString::Format( _( "%s file '%s' created.\n" ),
802 }
803 }
804 catch( const Standard_Failure& e )
805 {
806 ReportMessage( e.GetMessageString() );
807 ReportMessage( wxString::Format( _( "\n** Error exporting %s file. Export aborted. **\n" ),
809 return false;
810 }
811 catch( ... )
812 {
813 ReportMessage( wxString::Format( _( "\n** Error exporting %s file. Export aborted. **\n" ),
815 return false;
816 }
817
818 if( m_fail || m_error )
819 {
820 wxString msg;
821
822 if( m_fail )
823 {
824 msg = wxString::Format( _( "Unable to create %s file.\n"
825 "Check that the board has a valid outline and models." ),
827 }
828 else if( m_error || m_warn )
829 {
830 msg = wxString::Format( _( "%s file has been created, but there are warnings." ),
832 }
833
834 ReportMessage( msg );
835 }
836
837 // Display calculation time in seconds
838 double calculation_time = (double)( GetRunningMicroSecs() - stats_startExportTime) / 1e6;
839 ReportMessage( wxString::Format( _( "\nExport time %.3f s\n" ), calculation_time ) );
840
841 return true;
842}
@ ERROR_INSIDE
Definition: approximation.h:34
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
BASE_SET & set(size_t pos)
Definition: base_set.h:116
const VECTOR2I & GetGridOrigin() const
const VECTOR2I & GetAuxOrigin() const
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:78
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:235
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:297
BOARD_STACKUP GetStackupOrDefault() const
Definition: board.cpp:2388
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:2531
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:829
const ZONES & Zones() const
Definition: board.h:342
const FOOTPRINTS & Footprints() const
Definition: board.h:338
const TRACKS & Tracks() const
Definition: board.h:336
const wxString & GetFileName() const
Definition: board.h:334
PROJECT * GetProject() const
Definition: board.h:511
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:946
const DRAWINGS & Drawings() const
Definition: board.h:340
double AsRadians() const
Definition: eda_angle.h:117
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:107
wxString GetFormatName() const
wxString GetDefaultExportExtension() const
int m_platingThickness
Definition: exporter_step.h:90
void buildZones3DShape(VECTOR2D aOrigin)
LSET m_layersToExport
Definition: exporter_step.h:85
void initOutputVariant()
std::map< PCB_LAYER_ID, SHAPE_POLY_SET > m_poly_holes
Definition: exporter_step.h:83
BOARD * m_board
Definition: exporter_step.h:75
wxString m_outputFile
Definition: exporter_step.h:53
bool buildGraphic3DShape(BOARD_ITEM *aItem, VECTOR2D aOrigin)
bool buildFootprint3DShapes(FOOTPRINT *aFootprint, VECTOR2D aOrigin, SHAPE_POLY_SET *aClipPolygon)
EXPORTER_STEP_PARAMS m_params
Definition: exporter_step.h:67
EXPORTER_STEP(BOARD *aBoard, const EXPORTER_STEP_PARAMS &aParams)
wxString m_pcbBaseName
the name of the project (board short filename (no path, no ext) used to identify items in step file
Definition: exporter_step.h:80
std::unique_ptr< FILENAME_RESOLVER > m_resolver
Definition: exporter_step.h:68
bool buildBoard3DShapes()
std::unique_ptr< STEP_PCB_MODEL > m_pcbModel
Definition: exporter_step.h:76
std::map< PCB_LAYER_ID, SHAPE_POLY_SET > m_poly_shapes
Definition: exporter_step.h:82
bool buildTrack3DShape(PCB_TRACK *aTrack, VECTOR2D aOrigin)
KIGFX::COLOR4D m_copperColor
Definition: exporter_step.h:87
KIGFX::COLOR4D m_padColor
Definition: exporter_step.h:88
EDA_ANGLE GetOrientation() const
Definition: footprint.h:232
std::deque< PAD * > & Pads()
Definition: footprint.h:211
int GetAttributes() const
Definition: footprint.h:295
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: footprint.h:241
void TransformFPShapesToPolySet(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool aIncludeText=true, bool aIncludeShapes=true, bool aIncludePrivateItems=false) const
Generate shapes of graphic items (outlines) on layer aLayer as polygons and adds these polygons to aB...
Definition: footprint.cpp:3941
const LIB_ID & GetFPID() const
Definition: footprint.h:253
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:225
const wxString & GetReference() const
Definition: footprint.h:619
VECTOR2I GetPosition() const override
Definition: footprint.h:229
Hold a record identifying a library accessed by the appropriate footprint library #PLUGIN object in t...
Definition: fp_lib_table.h:42
const FP_LIB_TABLE_ROW * FindRow(const wxString &aNickName, bool aCheckIfEnabled=false)
Return an FP_LIB_TABLE_ROW if aNickName is found in this table or in any chained fall back table frag...
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
EXPORTER_STEP * m_converter
KiCadPrinter(EXPORTER_STEP *aConverter)
virtual void Send(const TCollection_ExtendedString &theString, const Message_Gravity theGravity, const Standard_Boolean theToPutEol) const override
virtual void Send(const TCollection_AsciiString &theString, const Message_Gravity theGravity, const Standard_Boolean theToPutEol) const override
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition: lib_id.h:87
const wxString GetFullURI(bool aSubstituted=false) const
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
static LSET ExternalCuMask()
Return a mask holding the Front and Bottom layers.
Definition: lset.cpp:594
static LSET InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
Definition: lset.cpp:561
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:572
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:297
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition: lset.h:63
Definition: pad.h:54
void TransformShapeToPolySet(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc) const override
Convert the item shape to a polyset.
Definition: pcb_shape.cpp:854
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...
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the track shape to a closed polygon.
Definition: pcb_track.cpp:2157
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pcb_track.cpp:1118
static FP_LIB_TABLE * PcbFootprintLibs(PROJECT *aProject)
Return the table of footprint libraries without Kiway.
Definition: project_pcb.cpp:37
Represent a set of closed polygons.
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
int FullPointCount() const
Return the number of points in the shape poly set.
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
void Unfracture()
Convert a single outline slitted ("fractured") polygon into a set ouf outlines with holes.
void SimplifyOutlines(int aMaxError=0)
Simplifies the lines in the polyset.
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
void TransformToPolygon(SHAPE_POLY_SET &aBuffer, int aError, ERROR_LOC aErrorLoc) const override
Fills a SHAPE_POLY_SET with a polygon representation of this shape.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const std::vector< POLYGON > & CPolygons() const
void TransformToPolygon(SHAPE_POLY_SET &aBuffer, int aError, ERROR_LOC aErrorLoc) const override
Fills a SHAPE_POLY_SET with a polygon representation of this shape.
Simple container to manage line stroke parameters.
Definition: stroke_params.h:94
int GetWidth() const
Handle a list of polygons defining a copper zone.
Definition: zone.h:74
#define _(s)
void ReportMessage(const wxString &aMessage)
@ FP_SMD
Definition: footprint.h:81
@ FP_DNP
Definition: footprint.h:88
@ FP_THROUGH_HOLE
Definition: footprint.h:80
static const std::string AutoSaveFilePrefix
const wxChar *const traceKiCad2Step
Flag to enable KiCad2Step debug tracing.
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:663
bool IsInnerCopperLayer(int aLayerId)
Test whether a layer is an inner (In1_Cu to In30_Cu) copper layer.
Definition: layer_ids.h:685
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ B_Mask
Definition: layer_ids.h:98
@ B_Cu
Definition: layer_ids.h:65
@ F_Mask
Definition: layer_ids.h:97
@ F_SilkS
Definition: layer_ids.h:100
@ B_SilkS
Definition: layer_ids.h:101
@ F_Cu
Definition: layer_ids.h:64
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:1071
see class PGM_BASE
int64_t GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
static constexpr double OCC_MAX_DISTANCE_TO_MERGE_POINTS
Default distance between points to treat them as separate ones (mm) 0.001 mm or less is a reasonable ...
constexpr double IUTomm(int iu) const
Definition: base_units.h:86
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
wxLogTrace helper definitions.
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition: typeinfo.h:93
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:92
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition: typeinfo.h:94
Definition of file extensions used in Kicad.