KiCad PCB EDA Suite
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 (C) 2016-2022 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 <board.h>
29#include <footprint.h>
30#include <fp_lib_table.h>
31
32#include <pgm_base.h>
33#include <base_units.h>
34#include <filename_resolver.h>
35#include <trace_helpers.h>
36
37#include <Message.hxx> // OpenCascade messenger
38#include <Message_PrinterOStream.hxx> // OpenCascade output messenger
39#include <Standard_Failure.hxx> // In open cascade
40
41#include <Standard_Version.hxx>
42
43#include <wx/crt.h>
44
45#define OCC_VERSION_MIN 0x070500
46
47#if OCC_VERSION_HEX < OCC_VERSION_MIN
48#include <Message_Messenger.hxx>
49#endif
50
51#define DEFAULT_BOARD_THICKNESS 1.6
52
53void ReportMessage( const wxString& aMessage )
54{
55 wxPrintf( aMessage );
56 fflush( stdout ); // Force immediate printing (needed on mingw)
57}
58
59class KiCadPrinter : public Message_Printer
60{
61public:
62 KiCadPrinter( EXPORTER_STEP* aConverter ) : m_converter( aConverter ) {}
63
64protected:
65#if OCC_VERSION_HEX < OCC_VERSION_MIN
66 virtual void Send( const TCollection_ExtendedString& theString,
67 const Message_Gravity theGravity,
68 const Standard_Boolean theToPutEol ) const override
69 {
70 Send( TCollection_AsciiString( theString ), theGravity, theToPutEol );
71 }
72
73 virtual void Send( const TCollection_AsciiString& theString,
74 const Message_Gravity theGravity,
75 const Standard_Boolean theToPutEol ) const override
76#else
77 virtual void send( const TCollection_AsciiString& theString,
78 const Message_Gravity theGravity ) const override
79#endif
80 {
81 if( theGravity >= Message_Warning
82 || ( wxLog::IsAllowedTraceMask( traceKiCad2Step ) && theGravity == Message_Info ) )
83 {
84 ReportMessage( theString.ToCString() );
85
86#if OCC_VERSION_HEX < OCC_VERSION_MIN
87 if( theToPutEol )
88 ReportMessage( wxT( "\n" ) );
89#else
90 ReportMessage( wxT( "\n" ) );
91#endif
92 }
93
94 if( theGravity == Message_Warning )
96
97 if( theGravity >= Message_Alarm )
99
100 if( theGravity == Message_Fail )
102 }
103
104private:
106};
107
108
110 m_params( aParams ),
111 m_error( false ),
112 m_fail( false ),
113 m_warn( false ),
114 m_hasDrillOrigin( false ),
115 m_hasGridOrigin( false ),
116 m_board( aBoard ),
117 m_pcbModel( nullptr ),
118 m_pcbName(),
119 m_minDistance( STEPEXPORT_MIN_DISTANCE ),
120 m_boardThickness( DEFAULT_BOARD_THICKNESS )
121{
122 m_solderMaskColor = COLOR4D( 0.08, 0.20, 0.14, 0.83 );
123
124 m_resolver = std::make_unique<FILENAME_RESOLVER>();
125 m_resolver->Set3DConfigDir( wxT( "" ) );
126 m_resolver->SetProgramBase( &Pgm() );
127}
128
129
131{
132}
133
134
135bool EXPORTER_STEP::composePCB( FOOTPRINT* aFootprint, VECTOR2D aOrigin )
136{
137 bool hasdata = false;
138
140 {
141 return hasdata;
142 }
143
144 // Prefetch the library for this footprint
145 // In case we need to resolve relative footprint paths
146 wxString libraryName = aFootprint->GetFPID().GetLibNickname();
147 wxString footprintBasePath = wxEmptyString;
148
149 double posX = aFootprint->GetPosition().x - aOrigin.x;
150 double posY = (aFootprint->GetPosition().y) - aOrigin.y;
151
152 if( m_board->GetProject() )
153 {
154 try
155 {
156 // FindRow() can throw an exception
157 const FP_LIB_TABLE_ROW* fpRow =
158 m_board->GetProject()->PcbFootprintLibs()->FindRow( libraryName, false );
159
160 if( fpRow )
161 footprintBasePath = fpRow->GetFullURI( true );
162 }
163 catch( ... )
164 {
165 // Do nothing if the libraryName is not found in lib table
166 }
167 }
168
169 // Dump the pad holes into the PCB
170 for( PAD* pad : aFootprint->Pads() )
171 {
172 if( m_pcbModel->AddPadHole( pad, aOrigin ) )
173 hasdata = true;
174 }
175
176 // Exit early if we don't want to include footprint models
178 {
179 return hasdata;
180 }
181
182 VECTOR2D newpos( pcbIUScale.IUTomm( posX ), pcbIUScale.IUTomm( posY ) );
183
184 for( const FP_3DMODEL& fp_model : aFootprint->Models() )
185 {
186 if( !fp_model.m_Show || fp_model.m_Filename.empty() )
187
188 continue;
189
190 std::vector<wxString> searchedPaths;
191 wxString mname = m_resolver->ResolvePath( fp_model.m_Filename, wxEmptyString );
192
193
194 if( !wxFileName::FileExists( mname ) )
195 {
196 ReportMessage( wxString::Format( wxT( "Could not add 3D model to %s.\n"
197 "File not found: %s\n" ),
198 aFootprint->GetReference(), mname ) );
199 continue;
200 }
201
202 std::string fname( mname.ToUTF8() );
203 std::string refName( aFootprint->GetReference().ToUTF8() );
204 try
205 {
206 bool bottomSide = aFootprint->GetLayer() == B_Cu;
207
208 // the rotation is stored in degrees but opencascade wants radians
209 VECTOR3D modelRot = fp_model.m_Rotation;
210 modelRot *= M_PI;
211 modelRot /= 180.0;
212
213 if( m_pcbModel->AddComponent( fname, refName, bottomSide,
214 newpos,
215 aFootprint->GetOrientation().AsRadians(),
216 fp_model.m_Offset, modelRot,
217 fp_model.m_Scale, m_params.m_substModels ) )
218 {
219 hasdata = true;
220 }
221 }
222 catch( const Standard_Failure& e )
223 {
224 ReportMessage( wxString::Format( wxT( "Could not add 3D model to %s.\n"
225 "OpenCASCADE error: %s\n" ),
226 aFootprint->GetReference(), e.GetMessageString() ) );
227 }
228
229 }
230
231 return hasdata;
232}
233
234
236{
237 if( m_pcbModel )
238 return true;
239
240 SHAPE_POLY_SET pcbOutlines; // stores the board main outlines
241
242 if( !m_board->GetBoardPolygonOutlines( pcbOutlines ) )
243 {
244 wxLogWarning( _( "Board outline is malformed. Run DRC for a full analysis." ) );
245 }
246
247 VECTOR2D origin;
248
249 // Determine the coordinate system reference:
250 // Precedence of reference point is Drill Origin > Grid Origin > User Offset
253 else if( m_params.m_useGridOrigin )
255 else
256 origin = m_params.m_origin;
257
258 m_pcbModel = std::make_unique<STEP_PCB_MODEL>( m_pcbName );
259
260 // TODO: Handle when top & bottom soldermask colours are different...
262
263 m_pcbModel->SetPCBThickness( m_boardThickness );
264 m_pcbModel->SetMinDistance(
266
268
269 for( FOOTPRINT* i : m_board->Footprints() )
270 composePCB( i, origin );
271
272 ReportMessage( wxT( "Create PCB solid model\n" ) );
273
274 if( !m_pcbModel->CreatePCB( pcbOutlines, origin ) )
275 {
276 ReportMessage( wxT( "could not create PCB solid model\n" ) );
277 return false;
278 }
279
280 return true;
281}
282
283
285{
287
289
290 if( bds.GetStackupDescriptor().GetCount() )
291 {
292 int thickness = 0;
293
294 for( BOARD_STACKUP_ITEM* item : bds.GetStackupDescriptor().GetList() )
295 {
296 switch( item->GetType() )
297 {
299 thickness += item->GetThickness();
300 break;
301
303 if( item->IsEnabled() )
304 thickness += item->GetThickness();
305
306 break;
307
308 default:
309 break;
310 }
311 }
312
313 m_boardThickness = pcbIUScale.IUTomm( thickness );
314 }
315}
316
317
319{
320 // setup opencascade message log
321 Message::DefaultMessenger()->RemovePrinters( STANDARD_TYPE( Message_PrinterOStream ) );
322 Message::DefaultMessenger()->AddPrinter( new KiCadPrinter( this ) );
323
324 ReportMessage( _( "Determining PCB data\n" ) );
326
327 try
328 {
329 ReportMessage( _( "Build STEP data\n" ) );
330
331 if( !composePCB() )
332 {
333 ReportMessage( _( "\n** Error building STEP board model. Export aborted. **\n" ) );
334 return false;
335 }
336
337 ReportMessage( _( "Writing STEP file\n" ) );
338
339 if( !m_pcbModel->WriteSTEP( m_outputFile ) )
340 {
341 ReportMessage( _( "\n** Error writing STEP file. **\n" ) );
342 return false;
343 }
344 else
345 {
346 ReportMessage( wxString::Format( _( "\nSTEP file '%s' created.\n" ), m_outputFile ) );
347 }
348 }
349 catch( const Standard_Failure& e )
350 {
351 ReportMessage( e.GetMessageString() );
352 ReportMessage( _( "\n** Error exporting STEP file. Export aborted. **\n" ) );
353 return false;
354 }
355 catch( ... )
356 {
357 ReportMessage( _( "\n** Error exporting STEP file. Export aborted. **\n" ) );
358 return false;
359 }
360
361 if( m_fail || m_error )
362 {
363 wxString msg;
364
365 if( m_fail )
366 {
367 msg = _( "Unable to create STEP file.\n"
368 "Check that the board has a valid outline and models." );
369 }
370 else if( m_error || m_warn )
371 {
372 msg = _( "STEP file has been created, but there are warnings." );
373 }
374
375 ReportMessage( msg );
376 }
377
378 return true;
379}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
@ BS_ITEM_TYPE_COPPER
Definition: board_stackup.h:43
@ BS_ITEM_TYPE_DIELECTRIC
Definition: board_stackup.h:44
Container for design settings for a BOARD object.
const VECTOR2I & GetGridOrigin()
const VECTOR2I & GetAuxOrigin()
BOARD_STACKUP & GetStackupDescriptor()
Manage one layer needed to make a physical board.
Definition: board_stackup.h:91
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
int GetCount() const
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:265
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
Definition: board.cpp:1950
FOOTPRINTS & Footprints()
Definition: board.h:307
PROJECT * GetProject() const
Definition: board.h:440
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:682
double AsRadians() const
Definition: eda_angle.h:153
KIGFX::COLOR4D m_solderMaskColor
BOARD * m_board
Definition: exporter_step.h:92
wxString m_outputFile
Definition: exporter_step.h:72
EXPORTER_STEP_PARAMS m_params
Definition: exporter_step.h:83
EXPORTER_STEP(BOARD *aBoard, const EXPORTER_STEP_PARAMS &aParams)
std::unique_ptr< FILENAME_RESOLVER > m_resolver
Definition: exporter_step.h:84
std::unique_ptr< STEP_PCB_MODEL > m_pcbModel
Definition: exporter_step.h:93
void determinePcbThickness()
double m_boardThickness
Definition: exporter_step.h:99
wxString m_pcbName
Definition: exporter_step.h:94
EDA_ANGLE GetOrientation() const
Definition: footprint.h:191
int GetAttributes() const
Definition: footprint.h:250
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: footprint.h:200
PADS & Pads()
Definition: footprint.h:170
const LIB_ID & GetFPID() const
Definition: footprint.h:212
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:184
const wxString & GetReference() const
Definition: footprint.h:519
VECTOR2I GetPosition() const override
Definition: footprint.h:188
Hold a record identifying a library accessed by the appropriate footprint library PLUGIN object in th...
Definition: fp_lib_table.h:41
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:390
double g
Green component.
Definition: color4d.h:391
double b
Blue component.
Definition: color4d.h:392
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...
Definition: pad.h:59
virtual FP_LIB_TABLE * PcbFootprintLibs(KIWAY &aKiway)
Return the table of footprint libraries.
Definition: project.cpp:324
Represent a set of closed polygons.
#define _(s)
#define DEFAULT_BOARD_THICKNESS
void ReportMessage(const wxString &aMessage)
@ FP_EXCLUDE_FROM_BOM
Definition: footprint.h:71
const wxChar *const traceKiCad2Step
Flag to enable KiCad2Step debug tracing.
@ B_Cu
Definition: layer_ids.h:95
see class PGM_BASE
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:111
static constexpr double STEPEXPORT_MIN_DISTANCE
< Default minimum distance between points to treat them as separate ones (mm)
static constexpr double STEPEXPORT_MIN_ACCEPTABLE_DISTANCE
constexpr double IUTomm(int iu) const
Definition: base_units.h:87
wxLogTrace helper definitions.