KiCad PCB EDA Suite
Loading...
Searching...
No Matches
command_pcb_export_3d.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 The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
22#include <cli/exit_codes.h>
23#include <base_units.h>
24#include <kiface_base.h>
25#include <regex>
26#include <string_utils.h>
27#include <locale_io.h>
28#include <wx/crt.h>
29
30#include <macros.h>
31
32#define ARG_DRILL_ORIGIN "--drill-origin"
33#define ARG_GRID_ORIGIN "--grid-origin"
34#define ARG_NO_UNSPECIFIED "--no-unspecified"
35#define ARG_NO_DNP "--no-dnp"
36#define ARG_SUBST_MODELS "--subst-models"
37#define ARG_FORCE "--force"
38#define ARG_MIN_DISTANCE "--min-distance"
39#define ARG_USER_ORIGIN "--user-origin"
40#define ARG_BOARD_ONLY "--board-only"
41#define ARG_CUT_VIAS_IN_BODY "--cut-vias-in-body"
42#define ARG_NO_BOARD_BODY "--no-board-body"
43#define ARG_NO_COMPONENTS "--no-components"
44#define ARG_INCLUDE_TRACKS "--include-tracks"
45#define ARG_INCLUDE_PADS "--include-pads"
46#define ARG_INCLUDE_ZONES "--include-zones"
47#define ARG_INCLUDE_INNER_COPPER "--include-inner-copper"
48#define ARG_INCLUDE_SILKSCREEN "--include-silkscreen"
49#define ARG_INCLUDE_SOLDERMASK "--include-soldermask"
50#define ARG_FUSE_SHAPES "--fuse-shapes"
51#define ARG_FILL_ALL_VIAS "--fill-all-vias"
52#define ARG_NO_OPTIMIZE_STEP "--no-optimize-step"
53#define ARG_NO_EXTRA_PAD_THICKNESS "--no-extra-pad-thickness"
54#define ARG_NET_FILTER "--net-filter"
55#define ARG_FORMAT "--format"
56#define ARG_VRML_UNITS "--units"
57#define ARG_VRML_MODELS_DIR "--models-dir"
58#define ARG_VRML_MODELS_RELATIVE "--models-relative"
59#define ARG_COMPONENT_FILTER "--component-filter"
60
61#define REGEX_QUANTITY "([\\s]*[+-]?[\\d]*[.]?[\\d]*)"
62#define REGEX_DELIMITER "(?:[\\s]*x)"
63#define REGEX_UNIT "([m]{2}|(?:in))"
64
66 const std::string& aDescription,
68 COMMAND( aName ),
69 m_format( aFormat )
70{
71 m_argParser.add_description( aDescription );
74
76 {
77 m_argParser.add_argument( ARG_FORMAT )
78 .default_value( std::string( "step" ) )
79 .help( UTF8STDSTR( _( "Output file format, options: step, stepz, brep, xao, glb "
80 "(binary glTF), ply, stl" ) ) );
81 }
82
83 m_argParser.add_argument( ARG_FORCE, "-f" )
84 .help( UTF8STDSTR( _( "Overwrite output file" ) ) )
85 .flag();
86
87 m_argParser.add_argument( ARG_NO_UNSPECIFIED )
88 .help( UTF8STDSTR( _( "Exclude 3D models for components with 'Unspecified' footprint "
89 "type" ) ) )
90 .flag();
91
92 m_argParser.add_argument( ARG_NO_DNP )
93 .help( UTF8STDSTR( _( "Exclude 3D models for components with 'Do not populate' "
94 "attribute" ) ) )
95 .flag();
96
98
101 {
102 m_argParser.add_argument( ARG_GRID_ORIGIN )
103 .help( UTF8STDSTR( _( "Use Grid Origin for output origin" ) ) )
104 .flag();
105
106 m_argParser.add_argument( ARG_DRILL_ORIGIN )
107 .help( UTF8STDSTR( _( "Use Drill Origin for output origin" ) ) )
108 .flag();
109
110 m_argParser.add_argument( "--subst-models" )
111 .help( UTF8STDSTR( _( "Substitute STEP or IGS models with the same name in place "
112 "of VRML models" ) ) )
113 .flag();
114
115 m_argParser.add_argument( ARG_BOARD_ONLY )
116 .help( UTF8STDSTR( _( "Only generate a board with no components" ) ) )
117 .flag();
118
119 m_argParser.add_argument( ARG_CUT_VIAS_IN_BODY )
120 .help( UTF8STDSTR( _( "Cut via holes in board body even if conductor layers are "
121 "not exported." ) ) )
122 .flag();
123
124 m_argParser.add_argument( ARG_NO_BOARD_BODY )
125 .help( UTF8STDSTR( _( "Exclude board body" ) ) )
126 .flag();
127
128 m_argParser.add_argument( ARG_NO_COMPONENTS )
129 .help( UTF8STDSTR( _( "Exclude 3D models for components" ) ) )
130 .flag();
131
132 m_argParser.add_argument( ARG_COMPONENT_FILTER )
133 .help( UTF8STDSTR( _( "Only include component 3D models matching this list of "
134 "reference designators (comma-separated, wildcards supported)"
135 ) ) )
136 .default_value( std::string() );
137
138 m_argParser.add_argument( ARG_INCLUDE_TRACKS )
139 .help( UTF8STDSTR( _( "Export tracks and vias" ) ) )
140 .flag();
141
142 m_argParser.add_argument( ARG_INCLUDE_PADS )
143 .help( UTF8STDSTR( _( "Export pads" ) ) )
144 .flag();
145
146 m_argParser.add_argument( ARG_INCLUDE_ZONES )
147 .help( UTF8STDSTR( _( "Export zones" ) ) )
148 .flag();
149
151 .help( UTF8STDSTR( _( "Export elements on inner copper layers" ) ) )
152 .flag();
153
154 m_argParser.add_argument( ARG_INCLUDE_SILKSCREEN )
155 .help( UTF8STDSTR( _( "Export silkscreen graphics as a set of flat faces" ) ) )
156 .flag();
157
158 m_argParser.add_argument( ARG_INCLUDE_SOLDERMASK )
159 .help( UTF8STDSTR( _( "Export soldermask layers as a set of flat faces" ) ) )
160 .flag();
161
162 m_argParser.add_argument( ARG_FUSE_SHAPES )
163 .help( UTF8STDSTR( _( "Fuse overlapping geometry together" ) ) )
164 .flag();
165
166 m_argParser.add_argument( ARG_FILL_ALL_VIAS )
167 .help( UTF8STDSTR( _( "Don't cut via holes in conductor layers." ) ) )
168 .flag();
169
171 .help( UTF8STDSTR( _( "Disable extra pad thickness (pads will have normal thickness)" ) ) )
172 .flag();
173
174 m_argParser.add_argument( ARG_MIN_DISTANCE )
175 .default_value( std::string( "0.01mm" ) )
176 .help( UTF8STDSTR( _( "Minimum distance between points to treat them as separate "
177 "ones" ) ) )
178 .metavar( "MIN_DIST" );
179
180 m_argParser.add_argument( ARG_NET_FILTER )
181 .default_value( std::string() )
182 .help( UTF8STDSTR( _( "Only include copper items belonging to nets matching this "
183 "wildcard" ) ) );
184 }
185
187 {
188 m_argParser.add_argument( ARG_NO_OPTIMIZE_STEP )
189 .help( UTF8STDSTR( _( "Do not optimize STEP file (enables writing parametric "
190 "curves)" ) ) )
191 .flag();
192 }
193
194 m_argParser.add_argument( ARG_USER_ORIGIN )
195 .default_value( std::string() )
196 .help( UTF8STDSTR( _( "User-specified output origin ex. 1x1in, 1x1inch, 25.4x25.4mm "
197 "(default unit mm)" ) ) );
198
200 {
201 m_argParser.add_argument( ARG_VRML_UNITS )
202 .default_value( std::string( "in" ) )
203 .help( UTF8STDSTR( _( "Output units; valid options: mm, m, in, tenths" ) ) );
204
205 m_argParser.add_argument( ARG_VRML_MODELS_DIR )
206 .default_value( std::string( "" ) )
207 .help( UTF8STDSTR( _( "Name of folder to create and store 3d models in, if not "
208 "specified or empty, the models will be embedded in main "
209 "exported VRML file" ) ) );
210
212 .help( UTF8STDSTR( _( "Used with --models-dir to output relative paths in the "
213 "resulting file" ) ) )
214 .flag();
215 }
216}
217
219{
220 std::unique_ptr<JOB_EXPORT_PCB_3D> step( new JOB_EXPORT_PCB_3D() );
221 EXPORTER_STEP_PARAMS& params = step->m_3dparams;
222
225 {
226 params.m_UseDrillOrigin = m_argParser.get<bool>( ARG_DRILL_ORIGIN );
227 params.m_UseGridOrigin = m_argParser.get<bool>( ARG_GRID_ORIGIN );
228 params.m_SubstModels = m_argParser.get<bool>( ARG_SUBST_MODELS );
230 params.m_ExportBoardBody = !m_argParser.get<bool>( ARG_NO_BOARD_BODY );
231 params.m_ExportComponents = !m_argParser.get<bool>( ARG_NO_COMPONENTS );
233 params.m_ExportPads = m_argParser.get<bool>( ARG_INCLUDE_PADS );
234 params.m_ExportZones = m_argParser.get<bool>( ARG_INCLUDE_ZONES );
238 params.m_FuseShapes = m_argParser.get<bool>( ARG_FUSE_SHAPES );
239 params.m_FillAllVias = m_argParser.get<bool>( ARG_FILL_ALL_VIAS );
241 params.m_BoardOnly = m_argParser.get<bool>( ARG_BOARD_ONLY );
242 params.m_NetFilter = From_UTF8( m_argParser.get<std::string>( ARG_NET_FILTER ).c_str() );
243 params.m_ComponentFilter =
244 From_UTF8( m_argParser.get<std::string>( ARG_COMPONENT_FILTER ).c_str() );
245
246 }
247
249 {
250 params.m_OptimizeStep = !m_argParser.get<bool>( ARG_NO_OPTIMIZE_STEP );
251 }
252
254 params.m_IncludeDNP = !m_argParser.get<bool>( ARG_NO_DNP );
255 params.m_Overwrite = m_argParser.get<bool>( ARG_FORCE );
256
257 if( !m_argVariantNames.empty() )
258 step->m_variant = m_argVariantNames.front();
259
260 step->SetConfiguredOutputPath( m_argOutput );
261
262 step->m_filename = m_argInput;
263 step->m_format = m_format;
264 step->SetVarOverrides( m_argDefineVars );
265
266 if( step->m_format == JOB_EXPORT_PCB_3D::FORMAT::UNKNOWN )
267 {
268 wxString format = From_UTF8( m_argParser.get<std::string>( ARG_FORMAT ).c_str() );
269
270 if( format == wxS( "step" ) )
271 step->m_format = JOB_EXPORT_PCB_3D::FORMAT::STEP;
272 else if( format == wxS( "stpz" ) )
273 step->m_format = JOB_EXPORT_PCB_3D::FORMAT::STEPZ;
274 else if( format == wxS( "brep" ) )
275 step->m_format = JOB_EXPORT_PCB_3D::FORMAT::BREP;
276 else if( format == wxS( "xao" ) )
277 step->m_format = JOB_EXPORT_PCB_3D::FORMAT::XAO;
278 else if( format == wxS( "glb" ) )
279 step->m_format = JOB_EXPORT_PCB_3D::FORMAT::GLB;
280 else if( format == wxS( "ply" ) )
281 step->m_format = JOB_EXPORT_PCB_3D::FORMAT::PLY;
282 else if( format == wxS( "stl" ) )
283 step->m_format = JOB_EXPORT_PCB_3D::FORMAT::STL;
284 else if( format == wxS( "u3d" ) )
285 step->m_format = JOB_EXPORT_PCB_3D::FORMAT::U3D;
286 else if( format == wxS( "pdf" ) )
287 step->m_format = JOB_EXPORT_PCB_3D::FORMAT::PDF;
288 else
289 {
290 wxFprintf( stderr, _( "Invalid format specified\n" ) );
292 }
293 }
294
295 if( step->m_format == JOB_EXPORT_PCB_3D::FORMAT::VRML )
296 {
297 wxString units = From_UTF8( m_argParser.get<std::string>( ARG_VRML_UNITS ).c_str() );
298
299 if( units == wxS( "in" ) )
300 step->m_vrmlUnits = JOB_EXPORT_PCB_3D::VRML_UNITS::INCH;
301 else if( units == wxS( "mm" ) )
302 step->m_vrmlUnits = JOB_EXPORT_PCB_3D::VRML_UNITS::MM;
303 else if( units == wxS( "m" ) )
304 step->m_vrmlUnits = JOB_EXPORT_PCB_3D::VRML_UNITS::METERS;
305 else if( units == wxS( "tenths" ) )
306 step->m_vrmlUnits = JOB_EXPORT_PCB_3D::VRML_UNITS::TENTHS;
307 else
308 {
309 wxFprintf( stderr, _( "Invalid units specified\n" ) );
311 }
312
313 step->m_vrmlModelDir = From_UTF8( m_argParser.get<std::string>( ARG_VRML_MODELS_DIR ).c_str() );
314
315 step->m_vrmlRelativePaths = m_argParser.get<bool>( ARG_VRML_MODELS_RELATIVE );
316 }
317
318 wxString userOrigin = From_UTF8( m_argParser.get<std::string>( ARG_USER_ORIGIN ).c_str() );
319
320 LOCALE_IO dummy; // Switch to "C" locale
321
322 if( !userOrigin.IsEmpty() )
323 {
325 std::regex_constants::icase );
326 std::smatch sm;
327 std::string str( userOrigin.ToUTF8() );
328 std::regex_search( str, sm, re_pattern );
329 step->m_3dparams.m_Origin.x = atof( sm.str( 1 ).c_str() );
330 step->m_3dparams.m_Origin.y = atof( sm.str( 2 ).c_str() );
331
332 // Default unit for m_xOrigin and m_yOrigin is mm.
333 // Convert in to board units. If the value is given in inches, it will be converted later
334 step->m_3dparams.m_Origin.x = pcbIUScale.mmToIU( step->m_3dparams.m_Origin.x );
335 step->m_3dparams.m_Origin.y = pcbIUScale.mmToIU( step->m_3dparams.m_Origin.y );
336
337 std::string tunit( sm[3] );
338
339 if( tunit.size() > 0 ) // unit specified ( default = mm, but can be in, inch or mm )
340 {
341 if( ( !sm.str( 1 ).compare( " " ) || !sm.str( 2 ).compare( " " ) )
342 || ( sm.size() != 4 ) )
343 {
344 std::cout << m_argParser;
346 }
347
348 // only in, inch and mm are valid:
349 if( !tunit.compare( "in" ) || !tunit.compare( "inch" ) )
350 {
351 step->m_3dparams.m_Origin *= 25.4;
352 }
353 else if( tunit.compare( "mm" ) )
354 {
355 std::cout << m_argParser;
357 }
358 }
359
360 step->m_hasUserOrigin = true;
361 }
362
365 {
366 wxString minDistance =
367 From_UTF8( m_argParser.get<std::string>( ARG_MIN_DISTANCE ).c_str() );
368
369 if( !minDistance.IsEmpty() )
370 {
371 std::regex re_pattern( REGEX_QUANTITY REGEX_UNIT, std::regex_constants::icase );
372 std::smatch sm;
373 std::string str( minDistance.ToUTF8() );
374 std::regex_search( str, sm, re_pattern );
375 step->m_3dparams.m_BoardOutlinesChainingEpsilon = atof( sm.str( 1 ).c_str() );
376
377 std::string tunit( sm[2] );
378
379 if( tunit.size() > 0 ) // No unit accepted ( default = mm )
380 {
381 if( !tunit.compare( "in" ) || !tunit.compare( "inch" ) )
382 {
383 step->m_3dparams.m_BoardOutlinesChainingEpsilon *= 25.4;
384 }
385 else if( tunit.compare( "mm" ) )
386 {
387 std::cout << m_argParser;
389 }
390 }
391 }
392 }
393
394 int exitCode = aKiway.ProcessJob( KIWAY::FACE_PCB, step.get() );
395
396 return exitCode;
397}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
std::map< wxString, wxString > m_argDefineVars
Value of the drawing sheet arg if configured.
Definition command.h:158
void addVariantsArg()
Set up the list of variants to output arguement.
Definition command.cpp:227
argparse::ArgumentParser m_argParser
Definition command.h:113
COMMAND(const std::string &aName)
Define a new COMMAND instance.
Definition command.cpp:30
void addDefineArg()
Set up the drawing sheet arg used by many of the export commands.
Definition command.cpp:212
void addCommonArgs(bool aInput, bool aOutput, IO_TYPE aInputType, IO_TYPE aOutputType)
Set up the most common of args used across cli.
Definition command.cpp:132
wxString m_argOutput
Value of the output arg if configured.
Definition command.h:148
wxString m_argInput
Value of the common input arg if configured.
Definition command.h:143
std::vector< wxString > m_argVariantNames
The list of variant names to output.
Definition command.h:170
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:295
int ProcessJob(KIWAY::FACE_T aFace, JOB *aJob, REPORTER *aReporter=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Definition kiway.cpp:744
@ FACE_PCB
pcbnew DSO
Definition kiway.h:303
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:41
#define UTF8STDSTR(s)
Definition command.h:27
#define ARG_FORCE
#define ARG_FORMAT
#define ARG_VRML_MODELS_DIR
#define ARG_NO_UNSPECIFIED
#define ARG_INCLUDE_SILKSCREEN
#define ARG_INCLUDE_TRACKS
#define ARG_MIN_DISTANCE
#define ARG_INCLUDE_ZONES
#define ARG_NET_FILTER
#define REGEX_QUANTITY
#define ARG_INCLUDE_INNER_COPPER
#define ARG_VRML_MODELS_RELATIVE
#define ARG_NO_OPTIMIZE_STEP
#define REGEX_UNIT
#define ARG_BOARD_ONLY
#define ARG_NO_DNP
#define ARG_CUT_VIAS_IN_BODY
#define ARG_NO_EXTRA_PAD_THICKNESS
#define ARG_INCLUDE_SOLDERMASK
#define REGEX_DELIMITER
#define ARG_FILL_ALL_VIAS
#define ARG_VRML_UNITS
#define ARG_SUBST_MODELS
#define ARG_FUSE_SHAPES
#define ARG_NO_BOARD_BODY
#define ARG_NO_COMPONENTS
#define ARG_GRID_ORIGIN
#define ARG_USER_ORIGIN
#define ARG_DRILL_ORIGIN
#define ARG_COMPONENT_FILTER
#define ARG_INCLUDE_PADS
#define _(s)
This file contains miscellaneous commonly used macros and functions.
static const int ERR_ARGS
Definition exit_codes.h:31
std::vector< FAB_LAYER_COLOR > dummy
wxString From_UTF8(const char *cstring)
PCB_EXPORT_3D_COMMAND(const std::string &aName, const std::string &aDescription, JOB_EXPORT_PCB_3D::FORMAT aFormat=JOB_EXPORT_PCB_3D::FORMAT::UNKNOWN)
int doPerform(KIWAY &aKiway) override
The internal handler that should be overloaded to implement command specific processing and work.
JOB_EXPORT_PCB_3D::FORMAT m_format