KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 <[email protected]>
5 * Copyright (C) 2016 Cirilo Bernardo <[email protected]>
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 <pcb_painter.h>
37#include <pad.h>
38#include <zone.h>
39#include <fp_lib_table.h>
40#include "step_pcb_model.h"
41
42#include <pgm_base.h>
43#include <reporter.h>
44#include <base_units.h>
45#include <filename_resolver.h>
46#include <trace_helpers.h>
47#include <project_pcb.h>
49
50#include <Message.hxx> // OpenCascade messenger
51#include <Message_PrinterOStream.hxx> // OpenCascade output messenger
52#include <Standard_Failure.hxx> // In open cascade
53
54#include <Standard_Version.hxx>
55
56#include <wx/crt.h>
57#include <wx/log.h>
58#include <core/profile.h> // To use GetRunningMicroSecs or another profiling utility
59
60#define OCC_VERSION_MIN 0x070500
61
62#if OCC_VERSION_HEX < OCC_VERSION_MIN
63#include <Message_Messenger.hxx>
64#endif
65
66
67class KICAD_PRINTER : public Message_Printer
68{
69public:
70 KICAD_PRINTER( REPORTER* aReporter ) :
71 m_reporter( aReporter )
72 {}
73
74protected:
75#if OCC_VERSION_HEX < OCC_VERSION_MIN
76 virtual void Send( const TCollection_ExtendedString& theString,
77 const Message_Gravity theGravity,
78 const Standard_Boolean theToPutEol ) const override
79 {
80 Send( TCollection_AsciiString( theString ), theGravity, theToPutEol );
81 }
82
83 virtual void Send( const TCollection_AsciiString& theString,
84 const Message_Gravity theGravity,
85 const Standard_Boolean theToPutEol ) const override
86#else
87 virtual void send( const TCollection_AsciiString& theString,
88 const Message_Gravity theGravity ) const override
89#endif
90 {
91 wxString msg( theString.ToCString() );
92
93#if OCC_VERSION_HEX < OCC_VERSION_MIN
94 if( theToPutEol )
95 msg += wxT( "\n" );
96#else
97 msg += wxT( "\n" );
98#endif
99
100 m_reporter->Report( msg, getSeverity( theGravity ) );
101 }
102
103private:
104 SEVERITY getSeverity( const Message_Gravity theGravity ) const
105 {
106 switch( theGravity )
107 {
108 case Message_Trace: return RPT_SEVERITY_DEBUG;
109 case Message_Info: return RPT_SEVERITY_DEBUG;
110 case Message_Warning: return RPT_SEVERITY_WARNING;
111 case Message_Alarm: return RPT_SEVERITY_ERROR;
112 case Message_Fail: return RPT_SEVERITY_ERROR;
113
114 // There are no other values, but gcc doesn't appear to be able to work that out.
115 default: return RPT_SEVERITY_UNDEFINED;
116 }
117 }
118
119private:
121};
122
123
125 REPORTER* aReporter ) :
126 m_params( aParams ),
127 m_reporter( aReporter ),
128 m_board( aBoard ),
129 m_pcbModel( nullptr )
130{
131 m_copperColor = COLOR4D( 0.7, 0.61, 0.0, 1.0 );
132
134 m_padColor = COLOR4D( 0.50, 0.50, 0.50, 1.0 );
135 else
137
138 // TODO: make configurable
140
141 // Init m_pcbBaseName to the board short filename (no path, no ext)
142 // m_pcbName is used later to identify items in step file
143 wxFileName fn( aBoard->GetFileName() );
144 m_pcbBaseName = fn.GetName();
145
146 // Remove the autosave prefix
148
149 m_resolver = std::make_unique<FILENAME_RESOLVER>();
150 m_resolver->Set3DConfigDir( wxT( "" ) );
151 // needed to add the project to the search stack
152 m_resolver->SetProject( aBoard->GetProject() );
153 m_resolver->SetProgramBase( &Pgm() );
154}
155
156
158{
159}
160
161
163 SHAPE_POLY_SET* aClipPolygon )
164{
165 bool hasdata = false;
166 std::vector<PAD*> padsMatchingNetFilter;
167
168 // Dump the pad holes into the PCB
169 for( PAD* pad : aFootprint->Pads() )
170 {
171 bool castellated = pad->GetProperty() == PAD_PROP::CASTELLATED;
172 std::shared_ptr<SHAPE_SEGMENT> holeShape = pad->GetEffectiveHoleShape();
173
174 SHAPE_POLY_SET holePoly;
175 holeShape->TransformToPolygon( holePoly, pad->GetMaxError(), ERROR_INSIDE );
176
177 // This helps with fusing
178 holePoly.Deflate( m_platingThickness / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, pad->GetMaxError() );
179
180 for( PCB_LAYER_ID pcblayer : pad->GetLayerSet() )
181 {
182 if( pad->IsOnLayer( pcblayer ) )
183 m_poly_holes[pcblayer].Append( holePoly );
184 }
185
186 if( pad->HasHole() )
187 {
188 int platingThickness = pad->GetAttribute() == PAD_ATTRIB::PTH ? m_platingThickness : 0;
189
190 if( m_pcbModel->AddHole( *holeShape, platingThickness, F_Cu, B_Cu, false, aOrigin, true, true ) )
191 hasdata = true;
192
194 //if( m_layersToExport.Contains( F_SilkS ) || m_layersToExport.Contains( B_SilkS ) )
195 //{
196 // m_poly_holes[F_SilkS].Append( holePoly );
197 // m_poly_holes[B_SilkS].Append( holePoly );
198 //}
199 }
200
201 if( !m_params.m_NetFilter.IsEmpty() && !pad->GetNetname().Matches( m_params.m_NetFilter ) )
202 continue;
203
205 {
206 if( m_pcbModel->AddPadShape( pad, aOrigin, false, castellated ? aClipPolygon : nullptr) )
207 hasdata = true;
208
210 {
211 for( PCB_LAYER_ID pcblayer : pad->GetLayerSet() )
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, pad->GetSolderMaskExpansion( cuLayer ),
219 pad->GetMaxError(), ERROR_INSIDE );
220
221 m_poly_shapes[pcblayer][wxEmptyString].Append( poly );
222 }
223 }
224 }
225
226 padsMatchingNetFilter.push_back( pad );
227 }
228
229 // Build 3D shapes of the footprint graphic items:
230 for( PCB_LAYER_ID pcblayer : m_layersToExport )
231 {
232 if( IsCopperLayer( pcblayer ) && !m_params.m_ExportTracksVias )
233 continue;
234
235 SHAPE_POLY_SET buffer;
236
237 aFootprint->TransformFPShapesToPolySet( buffer, pcblayer, 0, aFootprint->GetMaxError(), ERROR_INSIDE,
238 true, /* include text */
239 true, /* include shapes */
240 false /* include private items */ );
241
242 if( !IsCopperLayer( pcblayer ) )
243 {
244 m_poly_shapes[pcblayer][wxEmptyString].Append( buffer );
245 }
246 else
247 {
248 std::map<const SHAPE_POLY_SET::POLYGON*, PAD*> polyPadMap;
249
250 // Only add polygons colliding with any matching pads
251 for( const SHAPE_POLY_SET::POLYGON& poly : buffer.CPolygons() )
252 {
253 for( PAD* pad : padsMatchingNetFilter )
254 {
255 if( !pad->IsOnLayer( pcblayer ) )
256 continue;
257
258 std::shared_ptr<SHAPE_POLY_SET> padPoly = pad->GetEffectivePolygon( pcblayer );
259 SHAPE_POLY_SET gfxPoly( poly );
260
261 if( padPoly->Collide( &gfxPoly ) )
262 {
263 polyPadMap[&poly] = pad;
264 m_poly_shapes[pcblayer][pad->GetNetname()].Append( gfxPoly );
265 break;
266 }
267 }
268 }
269
270 if( m_params.m_NetFilter.empty() )
271 {
272 // Add polygons with no net
273 for( const SHAPE_POLY_SET::POLYGON& poly : buffer.CPolygons() )
274 {
275 auto it = polyPadMap.find( &poly );
276
277 if( it == polyPadMap.end() )
278 m_poly_shapes[pcblayer][wxEmptyString].Append( poly );
279 }
280 }
281 }
282 }
283
284 if( ( !(aFootprint->GetAttributes() & (FP_THROUGH_HOLE|FP_SMD)) ) && !m_params.m_IncludeUnspecified )
285 {
286 return hasdata;
287 }
288
289 if( ( aFootprint->GetAttributes() & FP_DNP ) && !m_params.m_IncludeDNP )
290 {
291 return hasdata;
292 }
293
294 // Prefetch the library for this footprint
295 // In case we need to resolve relative footprint paths
296 wxString libraryName = aFootprint->GetFPID().GetLibNickname();
297 wxString footprintBasePath = wxEmptyString;
298
299 double posX = aFootprint->GetPosition().x - aOrigin.x;
300 double posY = (aFootprint->GetPosition().y) - aOrigin.y;
301
302 if( m_board->GetProject() )
303 {
304 try
305 {
306 // FindRow() can throw an exception
307 const FP_LIB_TABLE_ROW* fpRow =
308 PROJECT_PCB::PcbFootprintLibs( m_board->GetProject() )->FindRow( libraryName, false );
309
310 if( fpRow )
311 footprintBasePath = fpRow->GetFullURI( true );
312 }
313 catch( ... )
314 {
315 // Do nothing if the libraryName is not found in lib table
316 }
317 }
318
319 // Exit early if we don't want to include footprint models
321 {
322 return hasdata;
323 }
324
325 bool componentFilter = !m_params.m_ComponentFilter.IsEmpty();
326 std::vector<wxString> componentFilterPatterns;
327
328 if( componentFilter )
329 {
330 wxStringTokenizer tokenizer( m_params.m_ComponentFilter, wxS( "," ), wxTOKEN_STRTOK );
331
332 while( tokenizer.HasMoreTokens() )
333 componentFilterPatterns.push_back( tokenizer.GetNextToken().Trim( false ) );
334
335 bool found = false;
336
337 for( const wxString& pattern : componentFilterPatterns )
338 {
339 if( aFootprint->GetReference().Matches( pattern ) )
340 {
341 found = true;
342 break;
343 }
344 }
345
346 if( !found )
347 return hasdata;
348 }
349
350 VECTOR2D newpos( pcbIUScale.IUTomm( posX ), pcbIUScale.IUTomm( posY ) );
351
352 for( const FP_3DMODEL& fp_model : aFootprint->Models() )
353 {
354 if( !fp_model.m_Show || fp_model.m_Filename.empty() )
355 continue;
356
357 std::vector<wxString> searchedPaths;
358 std::vector<const EMBEDDED_FILES*> embeddedFilesStack;
359 embeddedFilesStack.push_back( aFootprint->GetEmbeddedFiles() );
360 embeddedFilesStack.push_back( m_board->GetEmbeddedFiles() );
361
362 wxString mname = m_resolver->ResolvePath( fp_model.m_Filename, footprintBasePath, embeddedFilesStack );
363
364 if( mname.empty() || !wxFileName::FileExists( mname ) )
365 {
366 // the error path will return an empty name sometimes, at least report back the original filename
367 if( mname.empty() )
368 mname = fp_model.m_Filename;
369
370 m_reporter->Report( wxString::Format( _( "Could not add 3D model for %s.\n"
371 "File not found: %s\n" ),
372 aFootprint->GetReference(),
373 mname ),
375 continue;
376 }
377
378 std::string fname( mname.ToUTF8() );
379 std::string refName( aFootprint->GetReference().ToUTF8() );
380 try
381 {
382 bool bottomSide = aFootprint->GetLayer() == B_Cu;
383
384 // the rotation is stored in degrees but opencascade wants radians
385 VECTOR3D modelRot = fp_model.m_Rotation;
386 modelRot *= M_PI;
387 modelRot /= 180.0;
388
389 if( m_pcbModel->AddComponent( fname, refName, bottomSide,
390 newpos,
391 aFootprint->GetOrientation().AsRadians(),
392 fp_model.m_Offset, modelRot,
393 fp_model.m_Scale, m_params.m_SubstModels ) )
394 {
395 hasdata = true;
396 }
397 }
398 catch( const Standard_Failure& e )
399 {
400 m_reporter->Report( wxString::Format( _( "Could not add 3D model for %s.\n"
401 "OpenCASCADE error: %s\n" ),
402 aFootprint->GetReference(),
403 e.GetMessageString() ),
405 }
406
407 }
408
409 return hasdata;
410}
411
412
414{
415 bool skipCopper = !m_params.m_ExportTracksVias
416 || ( !m_params.m_NetFilter.IsEmpty()
417 && !aTrack->GetNetname().Matches( m_params.m_NetFilter ) );
418
419 if( m_params.m_ExportSoldermask && aTrack->IsOnLayer( F_Mask ) )
420 {
421 aTrack->TransformShapeToPolygon( m_poly_shapes[F_Mask][wxEmptyString], F_Mask,
422 aTrack->GetSolderMaskExpansion(), aTrack->GetMaxError(),
423 ERROR_INSIDE );
424 }
425
426 if( m_params.m_ExportSoldermask && aTrack->IsOnLayer( B_Mask ) )
427 {
428 aTrack->TransformShapeToPolygon( m_poly_shapes[B_Mask][wxEmptyString], B_Mask,
429 aTrack->GetSolderMaskExpansion(), aTrack->GetMaxError(),
430 ERROR_INSIDE );
431 }
432
433 if( aTrack->Type() == PCB_VIA_T )
434 {
435 PCB_VIA* via = static_cast<PCB_VIA*>( aTrack );
436
437 std::shared_ptr<SHAPE_SEGMENT> holeShape = via->GetEffectiveHoleShape();
438 SHAPE_POLY_SET holePoly;
439 holeShape->TransformToPolygon( holePoly, via->GetMaxError(), ERROR_INSIDE );
440
441 // This helps with fusing
442 holePoly.Deflate( m_platingThickness / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, via->GetMaxError() );
443
444 LSET layers( via->GetLayerSet() & m_layersToExport );
445
446 PCB_LAYER_ID top_layer, bot_layer;
447 via->LayerPair( &top_layer, &bot_layer );
448
449 if( !skipCopper )
450 {
451 for( PCB_LAYER_ID pcblayer : layers )
452 {
453 const std::shared_ptr<SHAPE>& shape = via->GetEffectiveShape( pcblayer );
454
455 SHAPE_POLY_SET poly;
456 shape->TransformToPolygon( poly, via->GetMaxError(), ERROR_INSIDE );
457 m_poly_shapes[pcblayer][via->GetNetname()].Append( poly );
458 m_poly_holes[pcblayer].Append( holePoly );
459 }
460
461 m_pcbModel->AddBarrel( *holeShape, top_layer, bot_layer, true, aOrigin, via->GetNetname() );
462 }
463
465 //if( m_layersToExport.Contains( F_SilkS ) || m_layersToExport.Contains( B_SilkS ) )
466 //{
467 // m_poly_holes[F_SilkS].Append( holePoly );
468 // m_poly_holes[B_SilkS].Append( holePoly );
469 //}
470
471 m_pcbModel->AddHole( *holeShape, m_platingThickness, top_layer, bot_layer, true, aOrigin,
473
474 return true;
475 }
476
477 if( skipCopper )
478 return true;
479
480 PCB_LAYER_ID pcblayer = aTrack->GetLayer();
481
482 if( !m_layersToExport.Contains( pcblayer ) )
483 return false;
484
485 aTrack->TransformShapeToPolygon( m_poly_shapes[pcblayer][aTrack->GetNetname()], pcblayer, 0,
486 aTrack->GetMaxError(), ERROR_INSIDE );
487
488 return true;
489}
490
491
493{
494 for( ZONE* zone : m_board->Zones() )
495 {
496 LSET layers = zone->GetLayerSet();
497
498 if( ( layers & LSET::AllCuMask() ).count() && !m_params.m_NetFilter.IsEmpty()
499 && !zone->GetNetname().Matches( m_params.m_NetFilter ) )
500 {
501 continue;
502 }
503
504 for( PCB_LAYER_ID layer : layers )
505 {
506 SHAPE_POLY_SET fill_shape;
507 zone->TransformSolidAreasShapesToPolygon( layer, fill_shape );
508 fill_shape.Unfracture();
509
510 fill_shape.SimplifyOutlines( ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel );
511
512 m_poly_shapes[layer][zone->GetNetname()].Append( fill_shape );
513 }
514 }
515}
516
517
519{
520 PCB_LAYER_ID pcblayer = aItem->GetLayer();
521
522 if( !m_layersToExport.Contains( pcblayer ) )
523 return false;
524
525 if( IsCopperLayer( pcblayer ) && !m_params.m_ExportTracksVias )
526 return false;
527
529 return false;
530
531 switch( aItem->Type() )
532 {
533 case PCB_SHAPE_T:
534 {
535 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
536
537 if( IsCopperLayer( pcblayer ) && !m_params.m_NetFilter.IsEmpty()
538 && !graphic->GetNetname().Matches( m_params.m_NetFilter ) )
539 {
540 return true;
541 }
542
543 LINE_STYLE lineStyle = graphic->GetLineStyle();
544
545 if( lineStyle == LINE_STYLE::SOLID )
546 {
547 graphic->TransformShapeToPolySet( m_poly_shapes[pcblayer][graphic->GetNetname()],
548 pcblayer, 0, graphic->GetMaxError(), ERROR_INSIDE );
549 }
550 else
551 {
552 std::vector<SHAPE*> shapes = graphic->MakeEffectiveShapes( true );
553 const PCB_PLOT_PARAMS& plotParams = m_board->GetPlotOptions();
554 KIGFX::PCB_RENDER_SETTINGS renderSettings;
555
556 renderSettings.SetDashLengthRatio( plotParams.GetDashedLineDashRatio() );
557 renderSettings.SetGapLengthRatio( plotParams.GetDashedLineGapRatio() );
558
559 for( SHAPE* shape : shapes )
560 {
561 STROKE_PARAMS::Stroke( shape, lineStyle, graphic->GetWidth(), &renderSettings,
562 [&]( const VECTOR2I& a, const VECTOR2I& b )
563 {
564 SHAPE_SEGMENT seg( a, b, graphic->GetWidth() );
565 seg.TransformToPolygon( m_poly_shapes[pcblayer][graphic->GetNetname()],
566 graphic->GetMaxError(), ERROR_INSIDE );
567 } );
568 }
569
570 for( SHAPE* shape : shapes )
571 delete shape;
572 }
573
574 if( graphic->IsHatchedFill() )
575 m_poly_shapes[pcblayer][graphic->GetNetname()].Append( graphic->GetHatching() );
576
577 if( m_params.m_ExportSoldermask && graphic->IsOnLayer( F_Mask ) )
578 {
579 graphic->TransformShapeToPolygon( m_poly_shapes[F_Mask][wxEmptyString], F_Mask,
580 graphic->GetSolderMaskExpansion(), graphic->GetMaxError(),
581 ERROR_INSIDE );
582 }
583
584 if( m_params.m_ExportSoldermask && graphic->IsOnLayer( B_Mask ) )
585 {
586 graphic->TransformShapeToPolygon( m_poly_shapes[B_Mask][wxEmptyString], B_Mask,
587 graphic->GetSolderMaskExpansion(), graphic->GetMaxError(),
588 ERROR_INSIDE );
589 }
590
591 break;
592 }
593
594 case PCB_TEXT_T:
595 {
596 PCB_TEXT* text = static_cast<PCB_TEXT*>( aItem );
597
598 text->TransformTextToPolySet( m_poly_shapes[pcblayer][wxEmptyString], 0, text->GetMaxError(),
599 ERROR_INSIDE );
600 break;
601 }
602
603 case PCB_TEXTBOX_T:
604 {
605 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( aItem );
606
607 // border
608 if( textbox->IsBorderEnabled() )
609 {
610 textbox->PCB_SHAPE::TransformShapeToPolygon( m_poly_shapes[pcblayer][wxEmptyString],
611 pcblayer, 0, textbox->GetMaxError(), ERROR_INSIDE );
612 }
613
614 // text
615 textbox->TransformTextToPolySet( m_poly_shapes[pcblayer][wxEmptyString], 0, textbox->GetMaxError(),
616 ERROR_INSIDE );
617 break;
618 }
619
620 case PCB_TABLE_T:
621 {
622 PCB_TABLE* table = static_cast<PCB_TABLE*>( aItem );
623
624 for( PCB_TABLECELL* cell : table->GetCells() )
625 {
626 cell->TransformTextToPolySet( m_poly_shapes[pcblayer][wxEmptyString], 0, cell->GetMaxError(),
627 ERROR_INSIDE );
628 }
629
630 table->DrawBorders(
631 [&]( const VECTOR2I& ptA, const VECTOR2I& ptB, const STROKE_PARAMS& stroke )
632 {
633 SHAPE_SEGMENT seg( ptA, ptB, stroke.GetWidth() );
634 seg.TransformToPolygon( m_poly_shapes[pcblayer][wxEmptyString], table->GetMaxError(),
635 ERROR_INSIDE );
636 } );
637
638 break;
639 }
640
641 default: wxFAIL_MSG( "buildGraphic3DShape: unhandled item type" );
642 }
643
644 return true;
645}
646
647
649{
650 // Specialize the STEP_PCB_MODEL generator for specific output format
651 // it can have some minor actions for the generator
652 switch( m_params.m_Format )
653 {
655 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_STEP );
656 break;
657
659 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_STEPZ );
660 break;
661
663 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_BREP );
664 break;
665
667 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_XAO );
668 break;
669
671 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_GLTF );
672 break;
673
675 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_PLY );
676 break;
677
679 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_STL );
680 break;
681
682 default:
683 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_UNKNOWN );
684 break;
685 }
686}
687
688
690{
691 if( m_pcbModel )
692 return true;
693
694 SHAPE_POLY_SET pcbOutlines; // stores the board main outlines
695
696 if( !m_board->GetBoardPolygonOutlines( pcbOutlines,
697 /* error handler */ nullptr,
698 /* allows use arcs in outlines */ true ) )
699 {
700 wxLogWarning( _( "Board outline is malformed. Run DRC for a full analysis." ) );
701 }
702
703 SHAPE_POLY_SET pcbOutlinesNoArcs = pcbOutlines;
704 pcbOutlinesNoArcs.ClearArcs();
705
706 VECTOR2D origin;
707
708 // Determine the coordinate system reference:
709 // Precedence of reference point is Drill Origin > Grid Origin > User Offset
712 else if( m_params.m_UseGridOrigin )
714 else
715 origin = m_params.m_Origin;
716
717 m_pcbModel = std::make_unique<STEP_PCB_MODEL>( m_pcbBaseName, m_reporter );
718
720
722 m_pcbModel->SetPadColor( m_padColor.r, m_padColor.g, m_padColor.b );
723
724 m_pcbModel->SetStackup( m_board->GetStackupOrDefault() );
725 m_pcbModel->SetEnabledLayers( m_layersToExport );
726 m_pcbModel->SetFuseShapes( m_params.m_FuseShapes );
727 m_pcbModel->SetNetFilter( m_params.m_NetFilter );
728
729 // Note: m_params.m_BoardOutlinesChainingEpsilon is used only to build the board outlines,
730 // not to set OCC chaining epsilon (much smaller)
731 //
732 // Set the min distance between 2 points for OCC to see these 2 points as merged
733 // OCC_MAX_DISTANCE_TO_MERGE_POINTS is acceptable for OCC, otherwise there are issues
734 // to handle the shapes chaining on copper layers, because the Z dist is 0.035 mm and the
735 // min dist must be much smaller (we use 0.001 mm giving good results)
736 m_pcbModel->OCCSetMergeMaxDistance( OCC_MAX_DISTANCE_TO_MERGE_POINTS );
737
738 // For copper layers, only pads and tracks are added, because adding everything on copper
739 // generate unreasonable file sizes and take a unreasonable calculation time.
740 for( FOOTPRINT* fp : m_board->Footprints() )
741 buildFootprint3DShapes( fp, origin, &pcbOutlinesNoArcs );
742
743 for( PCB_TRACK* track : m_board->Tracks() )
744 buildTrack3DShape( track, origin );
745
746 for( BOARD_ITEM* item : m_board->Drawings() )
747 buildGraphic3DShape( item, origin );
748
750 buildZones3DShape( origin );
751
752 for( PCB_LAYER_ID pcblayer : m_layersToExport.Seq() )
753 {
754 SHAPE_POLY_SET holes = m_poly_holes[pcblayer];
755 holes.Simplify();
756
757 if( pcblayer == F_Mask || pcblayer == B_Mask )
758 {
759 // Mask layer is negative
760 SHAPE_POLY_SET mask = pcbOutlinesNoArcs;
761
762 for( auto& [netname, poly] : m_poly_shapes[pcblayer] )
763 {
764 poly.Simplify();
765
766 poly.SimplifyOutlines( pcbIUScale.mmToIU( 0.003 ) );
767 poly.Simplify();
768
769 mask.BooleanSubtract( poly );
770 }
771
772 mask.BooleanSubtract( holes );
773
774 m_pcbModel->AddPolygonShapes( &mask, pcblayer, origin, wxEmptyString );
775 }
776 else
777 {
778 for( auto& [netname, poly] : m_poly_shapes[pcblayer] )
779 {
780 poly.Simplify();
781
782 poly.SimplifyOutlines( pcbIUScale.mmToIU( 0.003 ) );
783 poly.Simplify();
784
785 // Subtract holes
786 poly.BooleanSubtract( holes );
787
788 // Clip to board outline
789 poly.BooleanIntersection( pcbOutlinesNoArcs );
790
791 m_pcbModel->AddPolygonShapes( &poly, pcblayer, origin, netname );
792 }
793 }
794 }
795
796 m_reporter->Report( wxT( "Create PCB solid model.\n" ), RPT_SEVERITY_DEBUG );
797
798 m_reporter->Report( wxString::Format( wxT( "Board outline: found %d initial points.\n" ),
799 pcbOutlines.FullPointCount() ),
801
802 if( !m_pcbModel->CreatePCB( pcbOutlines, origin, m_params.m_ExportBoardBody ) )
803 {
804 m_reporter->Report( _( "Could not create PCB solid model.\n" ), RPT_SEVERITY_ERROR );
805 return false;
806 }
807
808 return true;
809}
810
811
813{
814 // Display the export time, for statistics
815 int64_t stats_startExportTime = GetRunningMicroSecs();
816
817 // setup opencascade message log
818 Message::DefaultMessenger()->RemovePrinters( STANDARD_TYPE( Message_PrinterOStream ) );
819 Message::DefaultMessenger()->AddPrinter( new KICAD_PRINTER( m_reporter ) );
820
821 m_reporter->Report( wxT( "Determining PCB data.\n" ), RPT_SEVERITY_DEBUG );
822
823 if( m_params.m_OutputFile.IsEmpty() )
824 {
825 wxFileName fn = m_board->GetFileName();
826 fn.SetName( fn.GetName() );
827 fn.SetExt( m_params.GetDefaultExportExtension() );
828
829 m_params.m_OutputFile = fn.GetFullName();
830 }
831
833
836
838 {
841 }
842
844 {
847 }
848
850
851 try
852 {
853 m_reporter->Report( wxString::Format( wxT( "Build %s data.\n" ), m_params.GetFormatName() ),
855
856 if( !buildBoard3DShapes() )
857 {
858 m_reporter->Report( _( "\n"
859 "** Error building STEP board model. Export aborted. **\n" ),
861 return false;
862 }
863
864 m_reporter->Report( wxString::Format( wxT( "Writing %s file.\n" ), m_params.GetFormatName() ),
866
867 bool success = true;
869 success = m_pcbModel->WriteSTEP( m_outputFile, m_params.m_OptimizeStep, false );
871 success = m_pcbModel->WriteSTEP( m_outputFile, m_params.m_OptimizeStep, true );
873 success = m_pcbModel->WriteBREP( m_outputFile );
875 success = m_pcbModel->WriteXAO( m_outputFile );
877 success = m_pcbModel->WriteGLTF( m_outputFile );
879 success = m_pcbModel->WritePLY( m_outputFile );
881 success = m_pcbModel->WriteSTL( m_outputFile );
882
883 if( !success )
884 {
885 m_reporter->Report( wxString::Format( _( "\n"
886 "** Error writing %s file. **\n" ),
889 return false;
890 }
891 else
892 {
893 m_reporter->Report( wxString::Format( wxT( "%s file '%s' created.\n" ),
895 m_outputFile ),
897 }
898 }
899 catch( const Standard_Failure& e )
900 {
901 m_reporter->Report( e.GetMessageString(), RPT_SEVERITY_ERROR );
902 m_reporter->Report( wxString::Format( _( "\n"
903 "** Error exporting %s file. Export aborted. **\n" ),
906 return false;
907 }
908 catch( ... )
909 {
910 m_reporter->Report( wxString::Format( _( "\n"
911 "** Error exporting %s file. Export aborted. **\n" ),
914 return false;
915 }
916
917 // Display calculation time in seconds
918 double calculation_time = (double)( GetRunningMicroSecs() - stats_startExportTime) / 1e6;
919 m_reporter->Report( wxString::Format( _( "\n"
920 "Export time %.3f s\n" ),
921 calculation_time ),
923
925}
@ ERROR_INSIDE
Definition: approximation.h:34
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:112
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:79
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:232
int GetMaxError() const
Definition: board_item.cpp:138
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:317
BOARD_STACKUP GetStackupOrDefault() const
Definition: board.cpp:2459
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:2604
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition: board.cpp:2657
const ZONES & Zones() const
Definition: board.h:362
const FOOTPRINTS & Footprints() const
Definition: board.h:358
const TRACKS & Tracks() const
Definition: board.h:356
const wxString & GetFileName() const
Definition: board.h:354
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition: board.h:745
PROJECT * GetProject() const
Definition: board.h:538
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:1024
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:907
const DRAWINGS & Drawings() const
Definition: board.h:360
double AsRadians() const
Definition: eda_angle.h:120
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:110
const SHAPE_POLY_SET & GetHatching() const
Definition: eda_shape.h:148
virtual std::vector< SHAPE * > MakeEffectiveShapes(bool aEdgeOnly=false) const
Make a set of SHAPE objects representing the EDA_SHAPE.
Definition: eda_shape.h:379
bool IsHatchedFill() const
Definition: eda_shape.h:124
LINE_STYLE GetLineStyle() const
Definition: eda_shape.cpp:2388
wxString GetFormatName() const
wxString GetDefaultExportExtension() const
int m_platingThickness
Definition: exporter_step.h:86
void buildZones3DShape(VECTOR2D aOrigin)
LSET m_layersToExport
Definition: exporter_step.h:81
REPORTER * m_reporter
Definition: exporter_step.h:68
void initOutputVariant()
std::map< PCB_LAYER_ID, SHAPE_POLY_SET > m_poly_holes
Definition: exporter_step.h:79
BOARD * m_board
Definition: exporter_step.h:70
wxString m_outputFile
Definition: exporter_step.h:54
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:64
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:75
std::unique_ptr< FILENAME_RESOLVER > m_resolver
Definition: exporter_step.h:65
bool buildBoard3DShapes()
std::unique_ptr< STEP_PCB_MODEL > m_pcbModel
Definition: exporter_step.h:71
std::map< PCB_LAYER_ID, std::map< wxString, SHAPE_POLY_SET > > m_poly_shapes
Definition: exporter_step.h:78
bool buildTrack3DShape(PCB_TRACK *aTrack, VECTOR2D aOrigin)
KIGFX::COLOR4D m_copperColor
Definition: exporter_step.h:83
EXPORTER_STEP(BOARD *aBoard, const EXPORTER_STEP_PARAMS &aParams, REPORTER *aReporter)
KIGFX::COLOR4D m_padColor
Definition: exporter_step.h:84
EDA_ANGLE GetOrientation() const
Definition: footprint.h:230
std::deque< PAD * > & Pads()
Definition: footprint.h:209
int GetAttributes() const
Definition: footprint.h:293
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: footprint.h:239
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:4008
const LIB_ID & GetFPID() const
Definition: footprint.h:251
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:223
const wxString & GetReference() const
Definition: footprint.h:627
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition: footprint.h:971
VECTOR2I GetPosition() const override
Definition: footprint.h:227
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...
virtual void Send(const TCollection_ExtendedString &theString, const Message_Gravity theGravity, const Standard_Boolean theToPutEol) const override
REPORTER * m_reporter
SEVERITY getSeverity(const Message_Gravity theGravity) const
KICAD_PRINTER(REPORTER *aReporter)
virtual void Send(const TCollection_AsciiString &theString, const Message_Gravity theGravity, const Standard_Boolean theToPutEol) const override
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
PCB specific render settings.
Definition: pcb_painter.h:80
void SetGapLengthRatio(double aRatio)
void SetDashLengthRatio(double aRatio)
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 const LSET & ExternalCuMask()
Return a mask holding the Front and Bottom layers.
Definition: lset.cpp:617
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:296
static LSET AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition: lset.cpp:591
static const LSET & InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
Definition: lset.cpp:560
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition: lset.h:63
Definition: pad.h:54
Parameters and options when plotting/printing a board.
double GetDashedLineGapRatio() const
double GetDashedLineDashRatio() const
int GetWidth() const override
Definition: pcb_shape.cpp:385
int GetSolderMaskExpansion() const
Definition: pcb_shape.cpp:185
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:838
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the shape to a closed polygon.
Definition: pcb_shape.cpp:829
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pcb_shape.cpp:212
bool IsBorderEnabled() const
Disables the border, this is done by changing the stroke internally.
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...
int GetSolderMaskExpansion() const
Definition: pcb_track.cpp:1129
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:2269
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pcb_track.cpp:1156
static FP_LIB_TABLE * PcbFootprintLibs(PROJECT *aProject)
Return the table of footprint libraries without Kiway.
Definition: project_pcb.cpp:37
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition: reporter.h:102
virtual bool HasMessageOfSeverity(int aSeverityMask) const
Returns true if the reporter has one or more messages matching the specified severity mask.
Definition: reporter.h:140
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 Deflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError)
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.
An abstract shape on 2D plane.
Definition: shape.h:126
Simple container to manage line stroke parameters.
Definition: stroke_params.h:94
int GetWidth() const
static void Stroke(const SHAPE *aShape, LINE_STYLE aLineStyle, int aWidth, const KIGFX::RENDER_SETTINGS *aRenderSettings, const std::function< void(const VECTOR2I &a, const VECTOR2I &b)> &aStroker)
Handle a list of polygons defining a copper zone.
Definition: zone.h:74
#define _(s)
@ FP_SMD
Definition: footprint.h:81
@ FP_DNP
Definition: footprint.h:86
@ FP_THROUGH_HOLE
Definition: footprint.h:80
static const std::string AutoSaveFilePrefix
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:665
bool IsInnerCopperLayer(int aLayerId)
Test whether a layer is an inner (In1_Cu to In30_Cu) copper layer.
Definition: layer_ids.h:687
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:902
see class PGM_BASE
int64_t GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
SEVERITY
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_UNDEFINED
@ RPT_SEVERITY_DEBUG
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
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 ...
LINE_STYLE
Dashed line types.
Definition: stroke_params.h:46
constexpr double IUTomm(int iu) const
Definition: base_units.h:90
constexpr int mmToIU(double mm) const
Definition: base_units.h:92
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.