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 (C) 1992-2023 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 <kiface_base.h>
24#include <regex>
25#include <string_utils.h>
26#include <locale_io.h>
27#include <wx/crt.h>
28
29#include <macros.h>
30
31#define ARG_DRILL_ORIGIN "--drill-origin"
32#define ARG_GRID_ORIGIN "--grid-origin"
33#define ARG_NO_UNSPECIFIED "--no-unspecified"
34#define ARG_NO_DNP "--no-dnp"
35#define ARG_SUBST_MODELS "--subst-models"
36#define ARG_FORCE "--force"
37#define ARG_MIN_DISTANCE "--min-distance"
38#define ARG_USER_ORIGIN "--user-origin"
39#define ARG_BOARD_ONLY "--board-only"
40#define ARG_INCLUDE_TRACKS "--include-tracks"
41#define ARG_INCLUDE_ZONES "--include-zones"
42#define ARG_NO_OPTIMIZE_STEP "--no-optimize-step"
43#define ARG_FORMAT "--format"
44#define ARG_VRML_UNITS "--units"
45#define ARG_VRML_MODELS_DIR "--models-dir"
46#define ARG_VRML_MODELS_RELATIVE "--models-relative"
47
48#define REGEX_QUANTITY "([\\s]*[+-]?[\\d]*[.]?[\\d]*)"
49#define REGEX_DELIMITER "(?:[\\s]*x)"
50#define REGEX_UNIT "([m]{2}|(?:in))"
51
53 const std::string& aDescription,
55 COMMAND( aName ),
56 m_format( aFormat )
57{
58 m_argParser.add_description( aDescription );
59 addCommonArgs( true, true, false, false );
61
63 {
64 m_argParser.add_argument( ARG_FORMAT )
65 .default_value( std::string( "step" ) )
66 .help( UTF8STDSTR( _( "Output file format, options: step, glb (binary glTF)" ) ) );
67 }
68
69 m_argParser.add_argument( ARG_FORCE, "-f" )
70 .help( UTF8STDSTR( _( "Overwrite output file" ) ) )
71 .implicit_value( true )
72 .default_value( false );
73
75 {
76 m_argParser.add_argument( ARG_GRID_ORIGIN )
77 .help( UTF8STDSTR( _( "Use Grid Origin for output origin" ) ) )
78 .implicit_value( true )
79 .default_value( false );
80
81 m_argParser.add_argument( ARG_DRILL_ORIGIN )
82 .help( UTF8STDSTR( _( "Use Drill Origin for output origin" ) ) )
83 .implicit_value( true )
84 .default_value( false );
85
86 m_argParser.add_argument( ARG_NO_UNSPECIFIED )
87 .help( UTF8STDSTR( _(
88 "Exclude 3D models for components with 'Unspecified' footprint type" ) ) )
89 .implicit_value( true )
90 .default_value( false );
91
92 m_argParser.add_argument( ARG_NO_DNP )
93 .help( UTF8STDSTR(
94 _( "Exclude 3D models for components with 'Do not populate' attribute" ) ) )
95 .implicit_value( true )
96 .default_value( false );
97
98 m_argParser.add_argument( "--subst-models" )
99 .help( UTF8STDSTR( _( "Substitute STEP or IGS models with the same name in place "
100 "of VRML models" ) ) )
101 .implicit_value( true )
102 .default_value( false );
103
104 m_argParser.add_argument( ARG_BOARD_ONLY )
105 .help( UTF8STDSTR( _( "Only generate a board with no components" ) ) )
106 .implicit_value( true )
107 .default_value( false );
108
109 m_argParser.add_argument( ARG_INCLUDE_TRACKS )
110 .help( UTF8STDSTR( _( "Export tracks (time consuming)" ) ) )
111 .implicit_value( true )
112 .default_value( false );
113
114 m_argParser.add_argument( ARG_INCLUDE_ZONES )
115 .help( UTF8STDSTR( _( "Export zones (time consuming)" ) ) )
116 .implicit_value( true )
117 .default_value( false );
118
119 m_argParser.add_argument( ARG_MIN_DISTANCE )
120 .default_value( std::string( "0.01mm" ) )
121 .help( UTF8STDSTR(
122 _( "Minimum distance between points to treat them as separate ones" ) ) )
123 .metavar( "MIN_DIST" );
124 }
125
127 {
128 m_argParser.add_argument( ARG_NO_OPTIMIZE_STEP )
129 .help( UTF8STDSTR( _( "Do not optimize STEP file (enables writing parametric curves)" ) ) )
130 .implicit_value( true )
131 .default_value( false );
132 }
133
134 m_argParser.add_argument( ARG_USER_ORIGIN )
135 .default_value( std::string() )
136 .help( UTF8STDSTR( _( "User-specified output origin ex. 1x1in, 1x1inch, 25.4x25.4mm (default unit mm)" ) ) );
137
139 {
140 m_argParser.add_argument( ARG_VRML_UNITS )
141 .default_value( std::string( "in" ) )
142 .help( UTF8STDSTR(
143 _( "Output units; ascii or csv format only; valid options: mm, m, in, tenths" ) ) );
144
145 m_argParser.add_argument( ARG_VRML_MODELS_DIR )
146 .default_value( std::string( "" ) )
147 .help( UTF8STDSTR(
148 _( "Name of folder to create and store 3d models in, if not specified or "
149 "empty, the models will be embedded in main exported vrml file" ) ) );
150
152 .help( UTF8STDSTR( _( "Used with --models-dir to output relative paths in the resulting file" ) ) )
153 .implicit_value( true )
154 .default_value( false );
155 }
156}
157
159{
160 std::unique_ptr<JOB_EXPORT_PCB_3D> step( new JOB_EXPORT_PCB_3D( true ) );
161
163 {
164 step->m_useDrillOrigin = m_argParser.get<bool>( ARG_DRILL_ORIGIN );
165 step->m_useGridOrigin = m_argParser.get<bool>( ARG_GRID_ORIGIN );
166 step->m_includeUnspecified = !m_argParser.get<bool>( ARG_NO_UNSPECIFIED );
167 step->m_includeDNP = !m_argParser.get<bool>( ARG_NO_DNP );
168 step->m_substModels = m_argParser.get<bool>( ARG_SUBST_MODELS );
169 step->m_exportTracks = m_argParser.get<bool>( ARG_INCLUDE_TRACKS );
170 step->m_exportZones = m_argParser.get<bool>( ARG_INCLUDE_ZONES );
171 step->m_boardOnly = m_argParser.get<bool>( ARG_BOARD_ONLY );
172 }
173
174 if( m_format == JOB_EXPORT_PCB_3D::FORMAT::STEP )
175 {
176 step->m_optimizeStep = !m_argParser.get<bool>( ARG_NO_OPTIMIZE_STEP );
177 }
178
179 step->m_overwrite = m_argParser.get<bool>( ARG_FORCE );
180 step->m_filename = m_argInput;
181 step->m_outputFile = m_argOutput;
182 step->m_format = m_format;
183 step->SetVarOverrides( m_argDefineVars );
184
185 if( step->m_format == JOB_EXPORT_PCB_3D::FORMAT::UNKNOWN )
186 {
187 wxString format = From_UTF8( m_argParser.get<std::string>( ARG_FORMAT ).c_str() );
188
189 if( format == wxS( "step" ) )
190 step->m_format = JOB_EXPORT_PCB_3D::FORMAT::STEP;
191 else if( format == wxS( "glb" ) )
192 step->m_format = JOB_EXPORT_PCB_3D::FORMAT::GLB;
193 else
194 {
195 wxFprintf( stderr, _( "Invalid format specified\n" ) );
197 }
198 }
199
200 if( step->m_format == JOB_EXPORT_PCB_3D::FORMAT::VRML )
201 {
202 wxString units = From_UTF8( m_argParser.get<std::string>( ARG_VRML_UNITS ).c_str() );
203
204 if( units == wxS( "in" ) )
205 step->m_vrmlUnits = JOB_EXPORT_PCB_3D::VRML_UNITS::INCHES;
206 else if( units == wxS( "mm" ) )
208 else if( units == wxS( "m" ) )
209 step->m_vrmlUnits = JOB_EXPORT_PCB_3D::VRML_UNITS::METERS;
210 else if( units == wxS( "tenths" ) )
211 step->m_vrmlUnits = JOB_EXPORT_PCB_3D::VRML_UNITS::TENTHS;
212 else
213 {
214 wxFprintf( stderr, _( "Invalid units specified\n" ) );
216 }
217
218 step->m_vrmlModelDir = From_UTF8( m_argParser.get<std::string>( ARG_VRML_MODELS_DIR ).c_str() );
219
220 step->m_vrmlRelativePaths = m_argParser.get<bool>( ARG_VRML_MODELS_RELATIVE );
221 }
222
223 wxString userOrigin = From_UTF8( m_argParser.get<std::string>( ARG_USER_ORIGIN ).c_str() );
224
225 LOCALE_IO dummy; // Switch to "C" locale
226
227 if( !userOrigin.IsEmpty() )
228 {
230 std::regex_constants::icase );
231 std::smatch sm;
232 std::string str( userOrigin.ToUTF8() );
233 std::regex_search( str, sm, re_pattern );
234 step->m_xOrigin = atof( sm.str( 1 ).c_str() );
235 step->m_yOrigin = atof( sm.str( 2 ).c_str() );
236
237 // Default unit for m_xOrigin and m_yOrigin is mm.
238 // Convert in to board units. If the value is given in inches, it will be converted later
239 step->m_xOrigin = pcbIUScale.mmToIU( step->m_xOrigin );
240 step->m_yOrigin = pcbIUScale.mmToIU( step->m_yOrigin );
241
242 std::string tunit( sm[3] );
243
244 if( tunit.size() > 0 ) // unit specified ( default = mm, but can be in, inch or mm )
245 {
246 if( ( !sm.str( 1 ).compare( " " ) || !sm.str( 2 ).compare( " " ) )
247 || ( sm.size() != 4 ) )
248 {
249 std::cout << m_argParser;
251 }
252
253 // only in, inch and mm are valid:
254 if( !tunit.compare( "in" ) || !tunit.compare( "inch" ) )
255 {
256 step->m_xOrigin *= 25.4;
257 step->m_yOrigin *= 25.4;
258 }
259 else if( tunit.compare( "mm" ) )
260 {
261 std::cout << m_argParser;
263 }
264 }
265
266 step->m_hasUserOrigin = true;
267 }
268
270 {
271 wxString minDistance =
272 From_UTF8( m_argParser.get<std::string>( ARG_MIN_DISTANCE ).c_str() );
273
274 if( !minDistance.IsEmpty() )
275 {
276 std::regex re_pattern( REGEX_QUANTITY REGEX_UNIT, std::regex_constants::icase );
277 std::smatch sm;
278 std::string str( minDistance.ToUTF8() );
279 std::regex_search( str, sm, re_pattern );
280 step->m_BoardOutlinesChainingEpsilon = atof( sm.str( 1 ).c_str() );
281
282 std::string tunit( sm[2] );
283
284 if( tunit.size() > 0 ) // No unit accepted ( default = mm )
285 {
286 if( !tunit.compare( "in" ) || !tunit.compare( "inch" ) )
287 {
288 step->m_BoardOutlinesChainingEpsilon *= 25.4;
289 }
290 else if( tunit.compare( "mm" ) )
291 {
292 std::cout << m_argParser;
294 }
295 }
296 }
297 }
298
299 int exitCode = aKiway.ProcessJob( KIWAY::FACE_PCB, step.get() );
300
301 return exitCode;
302}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
argparse::ArgumentParser m_argParser
Definition: command.h:98
void addDefineArg()
Sets up the drawing sheet arg used by many of the export commands.
Definition: command.cpp:168
void addCommonArgs(bool aInput, bool aOutput, bool aInputIsDir, bool aOutputIsDir)
Sets up the most common of args used across cli.
Definition: command.cpp:115
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition: kiway.h:279
int ProcessJob(KIWAY::FACE_T aFace, JOB *job)
Definition: kiway.cpp:729
@ FACE_PCB
pcbnew DSO
Definition: kiway.h:287
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:49
#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_TRACKS
#define ARG_MIN_DISTANCE
#define ARG_INCLUDE_ZONES
#define REGEX_QUANTITY
#define ARG_VRML_MODELS_RELATIVE
#define ARG_NO_OPTIMIZE_STEP
#define REGEX_UNIT
#define ARG_BOARD_ONLY
#define ARG_NO_DNP
#define REGEX_DELIMITER
#define ARG_VRML_UNITS
#define ARG_SUBST_MODELS
#define ARG_GRID_ORIGIN
#define ARG_USER_ORIGIN
#define ARG_DRILL_ORIGIN
#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
constexpr int mmToIU(double mm) const
Definition: base_units.h:89