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 int maxError = m_board->GetDesignSettings().m_MaxError;
168
169 // Dump the pad holes into the PCB
170 for( PAD* pad : aFootprint->Pads() )
171 {
172 bool castellated = pad->GetProperty() == PAD_PROP::CASTELLATED;
173 std::shared_ptr<SHAPE_SEGMENT> holeShape = pad->GetEffectiveHoleShape();
174
175 SHAPE_POLY_SET holePoly;
176 holeShape->TransformToPolygon( holePoly, maxError, ERROR_INSIDE );
177
178 // This helps with fusing
179 holePoly.Deflate( m_platingThickness / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError );
180
181 for( PCB_LAYER_ID pcblayer : pad->GetLayerSet().Seq() )
182 {
183 if( pad->IsOnLayer( pcblayer ) )
184 m_poly_holes[pcblayer].Append( holePoly );
185 }
186
187 if( pad->HasHole() )
188 {
189 int platingThickness = pad->GetAttribute() == PAD_ATTRIB::PTH ? m_platingThickness : 0;
190
191 if( m_pcbModel->AddHole( *holeShape, platingThickness, F_Cu, B_Cu, false, aOrigin, true,
192 true ) )
193 {
194 hasdata = true;
195 }
196
198 //if( m_layersToExport.Contains( F_SilkS ) || m_layersToExport.Contains( B_SilkS ) )
199 //{
200 // m_poly_holes[F_SilkS].Append( holePoly );
201 // m_poly_holes[B_SilkS].Append( holePoly );
202 //}
203 }
204
205 if( !m_params.m_NetFilter.IsEmpty() && !pad->GetNetname().Matches( m_params.m_NetFilter ) )
206 continue;
207
209 {
210 if( m_pcbModel->AddPadShape( pad, aOrigin, false,
211 castellated ? aClipPolygon : nullptr) )
212 hasdata = true;
213
215 {
216 for( PCB_LAYER_ID pcblayer : pad->GetLayerSet().Seq() )
217 {
218 if( pcblayer != F_Mask && pcblayer != B_Mask )
219 continue;
220
221 SHAPE_POLY_SET poly;
222 PCB_LAYER_ID cuLayer = ( pcblayer == F_Mask ) ? F_Cu : B_Cu;
223 pad->TransformShapeToPolygon( poly, cuLayer,
224 pad->GetSolderMaskExpansion( cuLayer ), maxError,
225 ERROR_INSIDE );
226
227 m_poly_shapes[pcblayer][wxEmptyString].Append( poly );
228 }
229 }
230 }
231
232 padsMatchingNetFilter.push_back( pad );
233 }
234
235 // Build 3D shapes of the footprint graphic items:
236 for( PCB_LAYER_ID pcblayer : m_layersToExport.Seq() )
237 {
238 if( IsCopperLayer( pcblayer ) && !m_params.m_ExportTracksVias )
239 continue;
240
241 SHAPE_POLY_SET buffer;
242
243 aFootprint->TransformFPShapesToPolySet( buffer, pcblayer, 0, maxError, ERROR_INSIDE,
244 true, /* include text */
245 true, /* include shapes */
246 false /* include private items */ );
247
248 if( !IsCopperLayer( pcblayer ) )
249 {
250 m_poly_shapes[pcblayer][wxEmptyString].Append( buffer );
251 }
252 else
253 {
254 std::map<const SHAPE_POLY_SET::POLYGON*, PAD*> polyPadMap;
255
256 // Only add polygons colliding with any matching pads
257 for( const SHAPE_POLY_SET::POLYGON& poly : buffer.CPolygons() )
258 {
259 for( PAD* pad : padsMatchingNetFilter )
260 {
261 if( !pad->IsOnLayer( pcblayer ) )
262 continue;
263
264 std::shared_ptr<SHAPE_POLY_SET> padPoly = pad->GetEffectivePolygon( pcblayer );
265 SHAPE_POLY_SET gfxPoly( poly );
266
267 if( padPoly->Collide( &gfxPoly ) )
268 {
269 polyPadMap[&poly] = pad;
270 m_poly_shapes[pcblayer][pad->GetNetname()].Append( gfxPoly );
271 break;
272 }
273 }
274 }
275
276 if( m_params.m_NetFilter.empty() )
277 {
278 // Add polygons with no net
279 for( const SHAPE_POLY_SET::POLYGON& poly : buffer.CPolygons() )
280 {
281 auto it = polyPadMap.find( &poly );
282
283 if( it == polyPadMap.end() )
284 m_poly_shapes[pcblayer][wxEmptyString].Append( poly );
285 }
286 }
287 }
288 }
289
290 if( ( !(aFootprint->GetAttributes() & (FP_THROUGH_HOLE|FP_SMD)) ) && !m_params.m_IncludeUnspecified )
291 {
292 return hasdata;
293 }
294
295 if( ( aFootprint->GetAttributes() & FP_DNP ) && !m_params.m_IncludeDNP )
296 {
297 return hasdata;
298 }
299
300 // Prefetch the library for this footprint
301 // In case we need to resolve relative footprint paths
302 wxString libraryName = aFootprint->GetFPID().GetLibNickname();
303 wxString footprintBasePath = wxEmptyString;
304
305 double posX = aFootprint->GetPosition().x - aOrigin.x;
306 double posY = (aFootprint->GetPosition().y) - aOrigin.y;
307
308 if( m_board->GetProject() )
309 {
310 try
311 {
312 // FindRow() can throw an exception
313 const FP_LIB_TABLE_ROW* fpRow =
314 PROJECT_PCB::PcbFootprintLibs( m_board->GetProject() )->FindRow( libraryName, false );
315
316 if( fpRow )
317 footprintBasePath = fpRow->GetFullURI( true );
318 }
319 catch( ... )
320 {
321 // Do nothing if the libraryName is not found in lib table
322 }
323 }
324
325 // Exit early if we don't want to include footprint models
327 {
328 return hasdata;
329 }
330
331 bool componentFilter = !m_params.m_ComponentFilter.IsEmpty();
332 std::vector<wxString> componentFilterPatterns;
333
334 if( componentFilter )
335 {
336 wxStringTokenizer tokenizer( m_params.m_ComponentFilter, wxS( "," ), wxTOKEN_STRTOK );
337
338 while( tokenizer.HasMoreTokens() )
339 componentFilterPatterns.push_back( tokenizer.GetNextToken().Trim( false ) );
340
341 bool found = false;
342
343 for( const wxString& pattern : componentFilterPatterns )
344 {
345 if( aFootprint->GetReference().Matches( pattern ) )
346 {
347 found = true;
348 break;
349 }
350 }
351
352 if( !found )
353 return hasdata;
354 }
355
356 VECTOR2D newpos( pcbIUScale.IUTomm( posX ), pcbIUScale.IUTomm( posY ) );
357
358 for( const FP_3DMODEL& fp_model : aFootprint->Models() )
359 {
360 if( !fp_model.m_Show || fp_model.m_Filename.empty() )
361 continue;
362
363 std::vector<wxString> searchedPaths;
364 std::vector<const EMBEDDED_FILES*> embeddedFilesStack;
365 embeddedFilesStack.push_back( aFootprint->GetEmbeddedFiles() );
366 embeddedFilesStack.push_back( m_board->GetEmbeddedFiles() );
367
368 wxString mname = m_resolver->ResolvePath( fp_model.m_Filename, footprintBasePath, embeddedFilesStack );
369
370 if( mname.empty() || !wxFileName::FileExists( mname ) )
371 {
372 // the error path will return an empty name sometimes, at least report back the original filename
373 if( mname.empty() )
374 mname = fp_model.m_Filename;
375
376 m_reporter->Report( wxString::Format( _( "Could not add 3D model for %s.\n"
377 "File not found: %s\n" ),
378 aFootprint->GetReference(),
379 mname ),
381 continue;
382 }
383
384 std::string fname( mname.ToUTF8() );
385 std::string refName( aFootprint->GetReference().ToUTF8() );
386 try
387 {
388 bool bottomSide = aFootprint->GetLayer() == B_Cu;
389
390 // the rotation is stored in degrees but opencascade wants radians
391 VECTOR3D modelRot = fp_model.m_Rotation;
392 modelRot *= M_PI;
393 modelRot /= 180.0;
394
395 if( m_pcbModel->AddComponent( fname, refName, bottomSide,
396 newpos,
397 aFootprint->GetOrientation().AsRadians(),
398 fp_model.m_Offset, modelRot,
399 fp_model.m_Scale, m_params.m_SubstModels ) )
400 {
401 hasdata = true;
402 }
403 }
404 catch( const Standard_Failure& e )
405 {
406 m_reporter->Report( wxString::Format( _( "Could not add 3D model for %s.\n"
407 "OpenCASCADE error: %s\n" ),
408 aFootprint->GetReference(),
409 e.GetMessageString() ),
411 }
412
413 }
414
415 return hasdata;
416}
417
418
420{
421 bool skipCopper = !m_params.m_ExportTracksVias
422 || ( !m_params.m_NetFilter.IsEmpty()
423 && !aTrack->GetNetname().Matches( m_params.m_NetFilter ) );
424
425 int maxError = m_board->GetDesignSettings().m_MaxError;
426
427 if( m_params.m_ExportSoldermask && aTrack->IsOnLayer( F_Mask ) )
428 {
429 aTrack->TransformShapeToPolygon( m_poly_shapes[F_Mask][wxEmptyString], F_Mask,
430 aTrack->GetSolderMaskExpansion(), maxError, ERROR_INSIDE );
431 }
432
433 if( m_params.m_ExportSoldermask && aTrack->IsOnLayer( B_Mask ) )
434 {
435 aTrack->TransformShapeToPolygon( m_poly_shapes[B_Mask][wxEmptyString], B_Mask,
436 aTrack->GetSolderMaskExpansion(), maxError, ERROR_INSIDE );
437 }
438
439 if( aTrack->Type() == PCB_VIA_T )
440 {
441 PCB_VIA* via = static_cast<PCB_VIA*>( aTrack );
442
443 std::shared_ptr<SHAPE_SEGMENT> holeShape = via->GetEffectiveHoleShape();
444 SHAPE_POLY_SET holePoly;
445 holeShape->TransformToPolygon( holePoly, maxError, ERROR_INSIDE );
446
447 // This helps with fusing
448 holePoly.Deflate( m_platingThickness / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError );
449
450 LSET layers( via->GetLayerSet() & m_layersToExport );
451
452 PCB_LAYER_ID top_layer, bot_layer;
453 via->LayerPair( &top_layer, &bot_layer );
454
455 if( !skipCopper )
456 {
457 for( PCB_LAYER_ID pcblayer : layers.Seq() )
458 {
459 const std::shared_ptr<SHAPE>& shape = via->GetEffectiveShape( pcblayer );
460
461 SHAPE_POLY_SET poly;
462 shape->TransformToPolygon( poly, maxError, ERROR_INSIDE );
463 m_poly_shapes[pcblayer][via->GetNetname()].Append( poly );
464 m_poly_holes[pcblayer].Append( holePoly );
465 }
466
467 m_pcbModel->AddBarrel( *holeShape, top_layer, bot_layer, true, aOrigin, via->GetNetname() );
468 }
469
471 //if( m_layersToExport.Contains( F_SilkS ) || m_layersToExport.Contains( B_SilkS ) )
472 //{
473 // m_poly_holes[F_SilkS].Append( holePoly );
474 // m_poly_holes[B_SilkS].Append( holePoly );
475 //}
476
477 m_pcbModel->AddHole( *holeShape, m_platingThickness, top_layer, bot_layer, true, aOrigin,
479
480 return true;
481 }
482
483 if( skipCopper )
484 return true;
485
486 PCB_LAYER_ID pcblayer = aTrack->GetLayer();
487
488 if( !m_layersToExport.Contains( pcblayer ) )
489 return false;
490
491 aTrack->TransformShapeToPolygon( m_poly_shapes[pcblayer][aTrack->GetNetname()], pcblayer, 0,
492 maxError, ERROR_INSIDE );
493
494 return true;
495}
496
497
499{
500 for( ZONE* zone : m_board->Zones() )
501 {
502 LSET layers = zone->GetLayerSet();
503
504 if( ( layers & LSET::AllCuMask() ).count() && !m_params.m_NetFilter.IsEmpty()
505 && !zone->GetNetname().Matches( m_params.m_NetFilter ) )
506 {
507 continue;
508 }
509
510 for( PCB_LAYER_ID layer : layers.Seq() )
511 {
512 SHAPE_POLY_SET fill_shape;
513 zone->TransformSolidAreasShapesToPolygon( layer, fill_shape );
514 fill_shape.Unfracture();
515
516 fill_shape.SimplifyOutlines( ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel );
517
518 m_poly_shapes[layer][zone->GetNetname()].Append( fill_shape );
519 }
520 }
521}
522
523
525{
526 PCB_LAYER_ID pcblayer = aItem->GetLayer();
527
528 if( !m_layersToExport.Contains( pcblayer ) )
529 return false;
530
531 if( IsCopperLayer( pcblayer ) && !m_params.m_ExportTracksVias )
532 return false;
533
535 return false;
536
537 int maxError = m_board->GetDesignSettings().m_MaxError;
538
539 switch( aItem->Type() )
540 {
541 case PCB_SHAPE_T:
542 {
543 PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
544
545 if( IsCopperLayer( pcblayer ) && !m_params.m_NetFilter.IsEmpty()
546 && !graphic->GetNetname().Matches( m_params.m_NetFilter ) )
547 {
548 return true;
549 }
550
551 LINE_STYLE lineStyle = graphic->GetLineStyle();
552
553 if( lineStyle == LINE_STYLE::SOLID )
554 {
555 graphic->TransformShapeToPolySet( m_poly_shapes[pcblayer][graphic->GetNetname()],
556 pcblayer, 0, maxError, ERROR_INSIDE );
557 }
558 else
559 {
560 std::vector<SHAPE*> shapes = graphic->MakeEffectiveShapes( true );
561 const PCB_PLOT_PARAMS& plotParams = m_board->GetPlotOptions();
562 KIGFX::PCB_RENDER_SETTINGS renderSettings;
563
564 renderSettings.SetDashLengthRatio( plotParams.GetDashedLineDashRatio() );
565 renderSettings.SetGapLengthRatio( plotParams.GetDashedLineGapRatio() );
566
567 for( SHAPE* shape : shapes )
568 {
569 STROKE_PARAMS::Stroke( shape, lineStyle, graphic->GetWidth(), &renderSettings,
570 [&]( const VECTOR2I& a, const VECTOR2I& b )
571 {
572 SHAPE_SEGMENT seg( a, b, graphic->GetWidth() );
573 seg.TransformToPolygon( m_poly_shapes[pcblayer][graphic->GetNetname()],
574 maxError, ERROR_INSIDE );
575 } );
576 }
577
578 for( SHAPE* shape : shapes )
579 delete shape;
580 }
581
582 if( graphic->IsHatchedFill() )
583 m_poly_shapes[pcblayer][graphic->GetNetname()].Append( graphic->GetHatching() );
584
585 if( m_params.m_ExportSoldermask && graphic->IsOnLayer( F_Mask ) )
586 {
587 graphic->TransformShapeToPolygon( m_poly_shapes[F_Mask][wxEmptyString], F_Mask,
588 graphic->GetSolderMaskExpansion(), maxError, ERROR_INSIDE );
589 }
590
591 if( m_params.m_ExportSoldermask && graphic->IsOnLayer( B_Mask ) )
592 {
593 graphic->TransformShapeToPolygon( m_poly_shapes[B_Mask][wxEmptyString], B_Mask,
594 graphic->GetSolderMaskExpansion(), maxError, ERROR_INSIDE );
595 }
596
597 break;
598 }
599
600 case PCB_TEXT_T:
601 {
602 PCB_TEXT* text = static_cast<PCB_TEXT*>( aItem );
603
604 text->TransformTextToPolySet( m_poly_shapes[pcblayer][wxEmptyString], 0, maxError,
605 ERROR_INSIDE );
606 break;
607 }
608
609 case PCB_TEXTBOX_T:
610 {
611 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( aItem );
612
613 // border
614 if( textbox->IsBorderEnabled() )
615 {
616 textbox->PCB_SHAPE::TransformShapeToPolygon( m_poly_shapes[pcblayer][wxEmptyString],
617 pcblayer, 0, maxError, ERROR_INSIDE );
618 }
619
620 // text
621 textbox->TransformTextToPolySet( m_poly_shapes[pcblayer][wxEmptyString], 0, maxError,
622 ERROR_INSIDE );
623 break;
624 }
625
626 case PCB_TABLE_T:
627 {
628 PCB_TABLE* table = static_cast<PCB_TABLE*>( aItem );
629
630 for( PCB_TABLECELL* cell : table->GetCells() )
631 {
632 cell->TransformTextToPolySet( m_poly_shapes[pcblayer][wxEmptyString], 0, maxError,
633 ERROR_INSIDE );
634 }
635
636 table->DrawBorders(
637 [&]( const VECTOR2I& ptA, const VECTOR2I& ptB, const STROKE_PARAMS& stroke )
638 {
639 SHAPE_SEGMENT seg( ptA, ptB, stroke.GetWidth() );
640 seg.TransformToPolygon( m_poly_shapes[pcblayer][wxEmptyString], maxError,
641 ERROR_INSIDE );
642 } );
643
644 break;
645 }
646
647 default: wxFAIL_MSG( "buildGraphic3DShape: unhandled item type" );
648 }
649
650 return true;
651}
652
653
655{
656 // Specialize the STEP_PCB_MODEL generator for specific output format
657 // it can have some minor actions for the generator
658 switch( m_params.m_Format )
659 {
661 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_STEP );
662 break;
663
665 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_BREP );
666 break;
667
669 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_XAO );
670 break;
671
673 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_GLTF );
674 break;
675
677 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_PLY );
678 break;
679
681 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_STL );
682 break;
683
684 default:
685 m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_UNKNOWN );
686 break;
687 }
688}
689
690
692{
693 if( m_pcbModel )
694 return true;
695
696 SHAPE_POLY_SET pcbOutlines; // stores the board main outlines
697
698 if( !m_board->GetBoardPolygonOutlines( pcbOutlines,
699 /* error handler */ nullptr,
700 /* allows use arcs in outlines */ true ) )
701 {
702 wxLogWarning( _( "Board outline is malformed. Run DRC for a full analysis." ) );
703 }
704
705 SHAPE_POLY_SET pcbOutlinesNoArcs = pcbOutlines;
706 pcbOutlinesNoArcs.ClearArcs();
707
708 VECTOR2D origin;
709
710 // Determine the coordinate system reference:
711 // Precedence of reference point is Drill Origin > Grid Origin > User Offset
714 else if( m_params.m_UseGridOrigin )
716 else
717 origin = m_params.m_Origin;
718
719 m_pcbModel = std::make_unique<STEP_PCB_MODEL>( m_pcbBaseName, m_reporter );
720
722
724 m_pcbModel->SetPadColor( m_padColor.r, m_padColor.g, m_padColor.b );
725
726 m_pcbModel->SetStackup( m_board->GetStackupOrDefault() );
727 m_pcbModel->SetEnabledLayers( m_layersToExport );
728 m_pcbModel->SetFuseShapes( m_params.m_FuseShapes );
729 m_pcbModel->SetNetFilter( m_params.m_NetFilter );
730
731 // Note: m_params.m_BoardOutlinesChainingEpsilon is used only to build the board outlines,
732 // not to set OCC chaining epsilon (much smaller)
733 //
734 // Set the min distance between 2 points for OCC to see these 2 points as merged
735 // OCC_MAX_DISTANCE_TO_MERGE_POINTS is acceptable for OCC, otherwise there are issues
736 // to handle the shapes chaining on copper layers, because the Z dist is 0.035 mm and the
737 // min dist must be much smaller (we use 0.001 mm giving good results)
738 m_pcbModel->OCCSetMergeMaxDistance( OCC_MAX_DISTANCE_TO_MERGE_POINTS );
739
741
742 // For copper layers, only pads and tracks are added, because adding everything on copper
743 // generate unreasonable file sizes and take a unreasonable calculation time.
744 for( FOOTPRINT* fp : m_board->Footprints() )
745 buildFootprint3DShapes( fp, origin, &pcbOutlinesNoArcs );
746
747 for( PCB_TRACK* track : m_board->Tracks() )
748 buildTrack3DShape( track, origin );
749
750 for( BOARD_ITEM* item : m_board->Drawings() )
751 buildGraphic3DShape( item, origin );
752
754 {
755 buildZones3DShape( origin );
756 }
757
758 for( PCB_LAYER_ID pcblayer : m_layersToExport.Seq() )
759 {
760 SHAPE_POLY_SET holes = m_poly_holes[pcblayer];
761 holes.Simplify();
762
763 if( pcblayer == F_Mask || pcblayer == B_Mask )
764 {
765 // Mask layer is negative
766 SHAPE_POLY_SET mask = pcbOutlinesNoArcs;
767
768 for( auto& [netname, poly] : m_poly_shapes[pcblayer] )
769 {
770 poly.Simplify();
771
772 poly.SimplifyOutlines( pcbIUScale.mmToIU( 0.003 ) );
773 poly.Simplify();
774
775 mask.BooleanSubtract( poly );
776 }
777
778 mask.BooleanSubtract( holes );
779
780 m_pcbModel->AddPolygonShapes( &mask, pcblayer, origin, wxEmptyString );
781 }
782 else
783 {
784 for( auto& [netname, poly] : m_poly_shapes[pcblayer] )
785 {
786 poly.Simplify();
787
788 poly.SimplifyOutlines( pcbIUScale.mmToIU( 0.003 ) );
789 poly.Simplify();
790
791 // Subtract holes
792 poly.BooleanSubtract( holes );
793
794 // Clip to board outline
795 poly.BooleanIntersection( pcbOutlinesNoArcs );
796
797 m_pcbModel->AddPolygonShapes( &poly, pcblayer, origin, netname );
798 }
799 }
800 }
801
802 m_reporter->Report( wxT( "Create PCB solid model.\n" ), RPT_SEVERITY_DEBUG );
803
804 m_reporter->Report( wxString::Format( wxT( "Board outline: found %d initial points.\n" ),
805 pcbOutlines.FullPointCount() ),
807
808 if( !m_pcbModel->CreatePCB( pcbOutlines, origin, m_params.m_ExportBoardBody ) )
809 {
810 m_reporter->Report( _( "Could not create PCB solid model.\n" ), RPT_SEVERITY_ERROR );
811 return false;
812 }
813
814 return true;
815}
816
817
819{
820 // Display the export time, for statistics
821 int64_t stats_startExportTime = GetRunningMicroSecs();
822
823 // setup opencascade message log
824 Message::DefaultMessenger()->RemovePrinters( STANDARD_TYPE( Message_PrinterOStream ) );
825 Message::DefaultMessenger()->AddPrinter( new KICAD_PRINTER( m_reporter ) );
826
827 m_reporter->Report( wxT( "Determining PCB data.\n" ), RPT_SEVERITY_DEBUG );
828
829 if( m_params.m_OutputFile.IsEmpty() )
830 {
831 wxFileName fn = m_board->GetFileName();
832 fn.SetName( fn.GetName() );
833 fn.SetExt( m_params.GetDefaultExportExtension() );
834
835 m_params.m_OutputFile = fn.GetFullName();
836 }
837
839
842
844 {
847 }
848
850 {
853 }
854
856
857 try
858 {
859 m_reporter->Report( wxString::Format( wxT( "Build %s data.\n" ), m_params.GetFormatName() ),
861
862 if( !buildBoard3DShapes() )
863 {
864 m_reporter->Report( _( "\n"
865 "** Error building STEP board model. Export aborted. **\n" ),
867 return false;
868 }
869
870 m_reporter->Report( wxString::Format( wxT( "Writing %s file.\n" ), m_params.GetFormatName() ),
872
873 bool success = true;
875 success = m_pcbModel->WriteSTEP( m_outputFile, m_params.m_OptimizeStep );
877 success = m_pcbModel->WriteBREP( m_outputFile );
879 success = m_pcbModel->WriteXAO( m_outputFile );
881 success = m_pcbModel->WriteGLTF( m_outputFile );
883 success = m_pcbModel->WritePLY( m_outputFile );
885 success = m_pcbModel->WriteSTL( m_outputFile );
886
887 if( !success )
888 {
889 m_reporter->Report( wxString::Format( _( "\n"
890 "** Error writing %s file. **\n" ),
893 return false;
894 }
895 else
896 {
897 m_reporter->Report( wxString::Format( wxT( "%s file '%s' created.\n" ),
899 m_outputFile ),
901 }
902 }
903 catch( const Standard_Failure& e )
904 {
905 m_reporter->Report( e.GetMessageString(), RPT_SEVERITY_ERROR );
906 m_reporter->Report( wxString::Format( _( "\n"
907 "** Error exporting %s file. Export aborted. **\n" ),
910 return false;
911 }
912 catch( ... )
913 {
914 m_reporter->Report( wxString::Format( _( "\n"
915 "** Error exporting %s file. Export aborted. **\n" ),
918 return false;
919 }
920
921 // Display calculation time in seconds
922 double calculation_time = (double)( GetRunningMicroSecs() - stats_startExportTime) / 1e6;
923 m_reporter->Report( wxString::Format( _( "\n"
924 "Export time %.3f s\n" ),
925 calculation_time ),
927
929}
@ 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
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:317
BOARD_STACKUP GetStackupOrDefault() const
Definition: board.cpp:2378
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:2524
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition: board.cpp:2577
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:743
PROJECT * GetProject() const
Definition: board.h:536
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:946
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:829
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:109
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:2376
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:3944
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:625
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition: footprint.h:969
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 LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:583
static const LSET & ExternalCuMask()
Return a mask holding the Front and Bottom layers.
Definition: lset.cpp:620
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:297
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:561
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:841
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:832
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:1133
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:2251
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pcb_track.cpp:1160
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:664
bool IsInnerCopperLayer(int aLayerId)
Test whether a layer is an inner (In1_Cu to In30_Cu) copper layer.
Definition: layer_ids.h:686
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:893
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.