KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcbnew_jobs_handler.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
21#include <wx/dir.h>
22#include "pcbnew_jobs_handler.h"
23#include <board_commit.h>
25#include <drc/drc_item.h>
26#include <drc/drc_report.h>
30#include <jobs/job_fp_upgrade.h>
45#include <jobs/job_pcb_render.h>
46#include <jobs/job_pcb_drc.h>
48#include <lset.h>
49#include <cli/exit_codes.h>
55#include <tool/tool_manager.h>
56#include <tools/drc_tool.h>
57#include <filename_resolver.h>
62#include <kiface_base.h>
63#include <macros.h>
64#include <pad.h>
65#include <pcb_marker.h>
69#include <kiface_ids.h>
72#include <pcbnew_settings.h>
73#include <pcbplot.h>
74#include <pcb_plotter.h>
75#include <pgm_base.h>
78#include <project_pcb.h>
80#include <reporter.h>
81#include <progress_reporter.h>
83#include <export_vrml.h>
84#include <wx/wfstream.h>
85#include <wx/zipstrm.h>
92#include <dialogs/dialog_plot.h>
96#include <paths.h>
98
100#include <locale_io.h>
101#include <confirm.h>
102
103
104#ifdef _WIN32
105#ifdef TRANSPARENT
106#undef TRANSPARENT
107#endif
108#endif
109
110
112 JOB_DISPATCHER( aKiway ),
113 m_cliBoard( nullptr ),
114 m_toolManager( nullptr )
115{
116 Register( "3d", std::bind( &PCBNEW_JOBS_HANDLER::JobExportStep, this, std::placeholders::_1 ),
117 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
118 {
119 JOB_EXPORT_PCB_3D* svgJob = dynamic_cast<JOB_EXPORT_PCB_3D*>( job );
120
121 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
122 false ) );
123
124 wxCHECK( svgJob && editFrame, false );
125
126 DIALOG_EXPORT_STEP dlg( editFrame, aParent, "", svgJob );
127 return dlg.ShowModal() == wxID_OK;
128 } );
129 Register( "render",
130 std::bind( &PCBNEW_JOBS_HANDLER::JobExportRender, this, std::placeholders::_1 ),
131 []( JOB* job, wxWindow* aParent ) -> bool
132 {
133 JOB_PCB_RENDER* renderJob = dynamic_cast<JOB_PCB_RENDER*>( job );
134
135 wxCHECK( renderJob, false );
136
137 DIALOG_RENDER_JOB dlg( aParent, renderJob );
138 return dlg.ShowModal() == wxID_OK;
139 } );
140 Register( "upgrade", std::bind( &PCBNEW_JOBS_HANDLER::JobUpgrade, this, std::placeholders::_1 ),
141 []( JOB* job, wxWindow* aParent ) -> bool
142 {
143 return true;
144 } );
145 Register( "svg", std::bind( &PCBNEW_JOBS_HANDLER::JobExportSvg, this, std::placeholders::_1 ),
146 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
147 {
148 JOB_EXPORT_PCB_SVG* svgJob = dynamic_cast<JOB_EXPORT_PCB_SVG*>( job );
149
150 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
151 false ) );
152
153 wxCHECK( svgJob && editFrame, false );
154
155 DIALOG_PLOT dlg( editFrame, aParent, svgJob );
156 return dlg.ShowModal() == wxID_OK;
157 } );
158 Register( "gencad",
159 std::bind( &PCBNEW_JOBS_HANDLER::JobExportGencad, this, std::placeholders::_1 ),
160 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
161 {
162 JOB_EXPORT_PCB_GENCAD* gencadJob = dynamic_cast<JOB_EXPORT_PCB_GENCAD*>( job );
163
164 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
165 false ) );
166
167 wxCHECK( gencadJob && editFrame, false );
168
169 DIALOG_GENCAD_EXPORT_OPTIONS dlg( editFrame, gencadJob->GetSettingsDialogTitle(), gencadJob );
170 return dlg.ShowModal() == wxID_OK;
171 } );
172 Register( "dxf", std::bind( &PCBNEW_JOBS_HANDLER::JobExportDxf, this, std::placeholders::_1 ),
173 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
174 {
175 JOB_EXPORT_PCB_DXF* dxfJob = dynamic_cast<JOB_EXPORT_PCB_DXF*>( job );
176
177 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
178 false ) );
179
180 wxCHECK( dxfJob && editFrame, false );
181
182 DIALOG_PLOT dlg( editFrame, aParent, dxfJob );
183 return dlg.ShowModal() == wxID_OK;
184 } );
185 Register( "pdf", std::bind( &PCBNEW_JOBS_HANDLER::JobExportPdf, this, std::placeholders::_1 ),
186 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
187 {
188 JOB_EXPORT_PCB_PDF* pdfJob = dynamic_cast<JOB_EXPORT_PCB_PDF*>( job );
189
190 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
191 false ) );
192
193 wxCHECK( pdfJob && editFrame, false );
194
195 DIALOG_PLOT dlg( editFrame, aParent, pdfJob );
196 return dlg.ShowModal() == wxID_OK;
197 } );
198 Register( "ps", std::bind( &PCBNEW_JOBS_HANDLER::JobExportPs, this, std::placeholders::_1 ),
199 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
200 {
201 JOB_EXPORT_PCB_PS* psJob = dynamic_cast<JOB_EXPORT_PCB_PS*>( job );
202
203 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
204 false ) );
205
206 wxCHECK( psJob && editFrame, false );
207
208 DIALOG_PLOT dlg( editFrame, aParent, psJob );
209 return dlg.ShowModal() == wxID_OK;
210 } );
211 Register( "gerber",
212 std::bind( &PCBNEW_JOBS_HANDLER::JobExportGerber, this, std::placeholders::_1 ),
213 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
214 {
215 JOB_EXPORT_PCB_GERBER* gJob = dynamic_cast<JOB_EXPORT_PCB_GERBER*>( job );
216
217 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
218 false ) );
219
220 wxCHECK( gJob && editFrame, false );
221
222 DIALOG_PLOT dlg( editFrame, aParent, gJob );
223 return dlg.ShowModal() == wxID_OK;
224 } );
225 Register( "gerbers",
226 std::bind( &PCBNEW_JOBS_HANDLER::JobExportGerbers, this, std::placeholders::_1 ),
227 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
228 {
229 JOB_EXPORT_PCB_GERBERS* gJob = dynamic_cast<JOB_EXPORT_PCB_GERBERS*>( job );
230
231 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
232 false ) );
233
234 wxCHECK( gJob && editFrame, false );
235
236 DIALOG_PLOT dlg( editFrame, aParent, gJob );
237 return dlg.ShowModal() == wxID_OK;
238 } );
239 Register( "hpgl",
240 [&]( JOB* aJob )
241 {
242 m_reporter->Report( _( "Plotting to HPGL is no longer supported as of KiCad 10.0.\n" ),
245 },
246 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
247 {
248 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
249 false ) );
250
251 wxCHECK( editFrame, false );
252
253 DisplayErrorMessage( editFrame,
254 _( "Plotting to HPGL is no longer supported as of KiCad 10.0." ) );
255 return false;
256 } );
257 Register( "drill",
258 std::bind( &PCBNEW_JOBS_HANDLER::JobExportDrill, this, std::placeholders::_1 ),
259 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
260 {
261 JOB_EXPORT_PCB_DRILL* drillJob = dynamic_cast<JOB_EXPORT_PCB_DRILL*>( job );
262
263 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
264 false ) );
265
266 wxCHECK( drillJob && editFrame, false );
267
268 DIALOG_GENDRILL dlg( editFrame, drillJob, aParent );
269 return dlg.ShowModal() == wxID_OK;
270 } );
271 Register( "pos", std::bind( &PCBNEW_JOBS_HANDLER::JobExportPos, this, std::placeholders::_1 ),
272 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
273 {
274 JOB_EXPORT_PCB_POS* posJob = dynamic_cast<JOB_EXPORT_PCB_POS*>( job );
275
276 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
277 false ) );
278
279 wxCHECK( posJob && editFrame, false );
280
281 DIALOG_GEN_FOOTPRINT_POSITION dlg( posJob, editFrame, aParent );
282 return dlg.ShowModal() == wxID_OK;
283 } );
284 Register( "fpupgrade",
285 std::bind( &PCBNEW_JOBS_HANDLER::JobExportFpUpgrade, this, std::placeholders::_1 ),
286 []( JOB* job, wxWindow* aParent ) -> bool
287 {
288 return true;
289 } );
290 Register( "fpsvg",
291 std::bind( &PCBNEW_JOBS_HANDLER::JobExportFpSvg, this, std::placeholders::_1 ),
292 []( JOB* job, wxWindow* aParent ) -> bool
293 {
294 return true;
295 } );
296 Register( "drc", std::bind( &PCBNEW_JOBS_HANDLER::JobExportDrc, this, std::placeholders::_1 ),
297 []( JOB* job, wxWindow* aParent ) -> bool
298 {
299 JOB_PCB_DRC* drcJob = dynamic_cast<JOB_PCB_DRC*>( job );
300
301 wxCHECK( drcJob, false );
302
303 DIALOG_DRC_JOB_CONFIG dlg( aParent, drcJob );
304 return dlg.ShowModal() == wxID_OK;
305 } );
306 Register( "ipc2581",
307 std::bind( &PCBNEW_JOBS_HANDLER::JobExportIpc2581, this, std::placeholders::_1 ),
308 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
309 {
310 JOB_EXPORT_PCB_IPC2581* ipcJob = dynamic_cast<JOB_EXPORT_PCB_IPC2581*>( job );
311
312 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
313 false ) );
314
315 wxCHECK( ipcJob && editFrame, false );
316
317 DIALOG_EXPORT_2581 dlg( ipcJob, editFrame, aParent );
318 return dlg.ShowModal() == wxID_OK;
319 } );
320 Register( "ipcd356",
321 std::bind( &PCBNEW_JOBS_HANDLER::JobExportIpcD356, this, std::placeholders::_1 ),
322 []( JOB* job, wxWindow* aParent ) -> bool
323 {
324 return true;
325 } );
326 Register( "odb",
327 std::bind( &PCBNEW_JOBS_HANDLER::JobExportOdb, this, std::placeholders::_1 ),
328 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
329 {
330 JOB_EXPORT_PCB_ODB* odbJob = dynamic_cast<JOB_EXPORT_PCB_ODB*>( job );
331
332 PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( aKiway->Player( FRAME_PCB_EDITOR,
333 false ) );
334
335 wxCHECK( odbJob && editFrame, false );
336
337 DIALOG_EXPORT_ODBPP dlg( odbJob, editFrame, aParent );
338 return dlg.ShowModal() == wxID_OK;
339 } );
340}
341
342
346
347
349{
350 TOOL_MANAGER* toolManager = nullptr;
351 if( Pgm().IsGUI() )
352 {
353 // we assume the PCB we are working on here is the one in the frame
354 // so use the frame's tool manager
355 PCB_EDIT_FRAME* editFrame = (PCB_EDIT_FRAME*) m_kiway->Player( FRAME_PCB_EDITOR, false );
356 if( editFrame )
357 toolManager = editFrame->GetToolManager();
358 }
359 else
360 {
361 if( m_toolManager == nullptr )
362 {
363 m_toolManager = std::make_unique<TOOL_MANAGER>();
364 }
365
366 toolManager = m_toolManager.get();
367
368 toolManager->SetEnvironment( aBrd, nullptr, nullptr, Kiface().KifaceSettings(), nullptr );
369 }
370 return toolManager;
371}
372
373
374BOARD* PCBNEW_JOBS_HANDLER::getBoard( const wxString& aPath )
375{
376 BOARD* brd = nullptr;
377
378 if( !Pgm().IsGUI() && Pgm().GetSettingsManager().IsProjectOpen() )
379 {
380 wxString pcbPath = aPath;
381
382 if( pcbPath.IsEmpty() )
383 {
384 wxFileName path = Pgm().GetSettingsManager().Prj().GetProjectFullName();
386 path.MakeAbsolute();
387 pcbPath = path.GetFullPath();
388 }
389
390 if( !m_cliBoard )
391 m_cliBoard = LoadBoard( pcbPath, true );
392
393 brd = m_cliBoard;
394 }
395 else if( Pgm().IsGUI() && Pgm().GetSettingsManager().IsProjectOpen() )
396 {
397 PCB_EDIT_FRAME* editFrame = (PCB_EDIT_FRAME*) m_kiway->Player( FRAME_PCB_EDITOR, false );
398
399 if( editFrame )
400 brd = editFrame->GetBoard();
401 }
402 else
403 {
404 brd = LoadBoard( aPath, true );
405 }
406
407 if( !brd )
408 m_reporter->Report( _( "Failed to load board\n" ), RPT_SEVERITY_ERROR );
409
410 return brd;
411}
412
413
414LSEQ PCBNEW_JOBS_HANDLER::convertLayerArg( wxString& aLayerString, BOARD* aBoard ) const
415{
416 std::map<wxString, LSET> layerUserMasks;
417 std::map<wxString, LSET> layerMasks;
418 std::map<wxString, LSET> layerGuiMasks;
419
420 // Build list of layer names and their layer mask:
421 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
422 {
423 // Add user layer name
424 if( aBoard )
425 layerUserMasks[ aBoard->GetLayerName( layer ) ] = LSET( { layer } );
426
427 // Add layer name used in pcb files
428 layerMasks[ LSET::Name( layer ) ] = LSET( { layer } );
429 // Add layer name using GUI canonical layer name
430 layerGuiMasks[ LayerName( layer ) ] = LSET( { layer } );
431 }
432
433 // Add list of grouped layer names used in pcb files
434 layerMasks[ wxT( "*" ) ] = LSET::AllLayersMask();
435 layerMasks[ wxT( "*.Cu" ) ] = LSET::AllCuMask();
436 layerMasks[ wxT( "*In.Cu" ) ] = LSET::InternalCuMask();
437 layerMasks[ wxT( "F&B.Cu" ) ] = LSET( { F_Cu, B_Cu } );
438 layerMasks[ wxT( "*.Adhes" ) ] = LSET( { B_Adhes, F_Adhes } );
439 layerMasks[ wxT( "*.Paste" ) ] = LSET( { B_Paste, F_Paste } );
440 layerMasks[ wxT( "*.Mask" ) ] = LSET( { B_Mask, F_Mask } );
441 layerMasks[ wxT( "*.SilkS" ) ] = LSET( { B_SilkS, F_SilkS } );
442 layerMasks[ wxT( "*.Fab" ) ] = LSET( { B_Fab, F_Fab } );
443 layerMasks[ wxT( "*.CrtYd" ) ] = LSET( { B_CrtYd, F_CrtYd } );
444
445 // Add list of grouped layer names using GUI canonical layer names
446 layerGuiMasks[ wxT( "*.Adhesive" ) ] = LSET( { B_Adhes, F_Adhes } );
447 layerGuiMasks[ wxT( "*.Silkscreen" ) ] = LSET( { B_SilkS, F_SilkS } );
448 layerGuiMasks[ wxT( "*.Courtyard" ) ] = LSET( { B_CrtYd, F_CrtYd } );
449
450 LSEQ layerMask;
451
452 auto pushLayers =
453 [&]( const LSET& layerSet )
454 {
455 for( PCB_LAYER_ID layer : layerSet.Seq() )
456 layerMask.push_back( layer );
457 };
458
459 if( !aLayerString.IsEmpty() )
460 {
461 wxStringTokenizer layerTokens( aLayerString, "," );
462
463 while( layerTokens.HasMoreTokens() )
464 {
465 std::string token = TO_UTF8( layerTokens.GetNextToken().Trim( true ).Trim( false ) );
466
467 if( layerUserMasks.contains( token ) )
468 pushLayers( layerUserMasks.at( token ) );
469 else if( layerMasks.count( token ) )
470 pushLayers( layerMasks.at( token ) );
471 else if( layerGuiMasks.count( token ) )
472 pushLayers( layerGuiMasks.at( token ) );
473 else
474 m_reporter->Report( wxString::Format( _( "Invalid layer name '%s'\n" ), token ) );
475 }
476 }
477
478 return layerMask;
479}
480
481
483{
484 JOB_EXPORT_PCB_3D* aStepJob = dynamic_cast<JOB_EXPORT_PCB_3D*>( aJob );
485
486 if( aStepJob == nullptr )
488
489 BOARD* brd = getBoard( aStepJob->m_filename );
490
491 if( !brd )
493
494 aJob->SetTitleBlock( brd->GetTitleBlock() );
495 brd->GetProject()->ApplyTextVars( aJob->GetVarOverrides() );
497
498 if( aStepJob->GetConfiguredOutputPath().IsEmpty() )
499 {
500 wxFileName fn = brd->GetFileName();
501 fn.SetName( fn.GetName() );
502
503 switch( aStepJob->m_format )
504 {
514 default:
515 m_reporter->Report( _( "Unknown export format" ), RPT_SEVERITY_ERROR );
516 return CLI::EXIT_CODES::ERR_UNKNOWN; // shouldnt have gotten here
517 }
518
519 aStepJob->SetWorkingOutputPath( fn.GetFullName() );
520 }
521
522 wxString outPath = aStepJob->GetFullOutputPath( brd->GetProject() );
523
524 if( !PATHS::EnsurePathExists( outPath, true ) )
525 {
526 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
528 }
529
531 {
532
533 double scale = 0.0;
534 switch ( aStepJob->m_vrmlUnits )
535 {
536 case JOB_EXPORT_PCB_3D::VRML_UNITS::MM: scale = 1.0; break;
537 case JOB_EXPORT_PCB_3D::VRML_UNITS::METERS: scale = 0.001; break;
538 case JOB_EXPORT_PCB_3D::VRML_UNITS::TENTHS: scale = 10.0 / 25.4; break;
539 case JOB_EXPORT_PCB_3D::VRML_UNITS::INCH: scale = 1.0 / 25.4; break;
540 }
541
542 EXPORTER_VRML vrmlExporter( brd );
543 wxString messages;
544
545 double originX = pcbIUScale.IUTomm( aStepJob->m_3dparams.m_Origin.x );
546 double originY = pcbIUScale.IUTomm( aStepJob->m_3dparams.m_Origin.y );
547
548 if( !aStepJob->m_hasUserOrigin )
549 {
550 BOX2I bbox = brd->ComputeBoundingBox( true );
551 originX = pcbIUScale.IUTomm( bbox.GetCenter().x );
552 originY = pcbIUScale.IUTomm( bbox.GetCenter().y );
553 }
554
555 bool success = vrmlExporter.ExportVRML_File( brd->GetProject(),
556 &messages,
557 outPath,
558 scale,
560 aStepJob->m_3dparams.m_IncludeDNP,
561 !aStepJob->m_vrmlModelDir.IsEmpty(),
562 aStepJob->m_vrmlRelativePaths,
563 aStepJob->m_vrmlModelDir,
564 originX,
565 originY );
566
567 if ( success )
568 {
569 m_reporter->Report( wxString::Format( _( "Successfully exported VRML to %s" ),
570 outPath ),
572 }
573 else
574 {
575 m_reporter->Report( _( "Error exporting VRML" ), RPT_SEVERITY_ERROR );
577 }
578 }
579 else
580 {
581 EXPORTER_STEP_PARAMS params = aStepJob->m_3dparams;
582
583 switch( aStepJob->m_format )
584 {
594 default:
595 m_reporter->Report( _( "Unknown export format" ), RPT_SEVERITY_ERROR );
596 return CLI::EXIT_CODES::ERR_UNKNOWN; // shouldnt have gotten here
597 }
598
599 EXPORTER_STEP stepExporter( brd, params, m_reporter );
600 stepExporter.m_outputFile = aStepJob->GetFullOutputPath( brd->GetProject() );
601
602 if( !stepExporter.Export() )
604 }
605
606 return CLI::EXIT_CODES::OK;
607}
608
609
611{
612 JOB_PCB_RENDER* aRenderJob = dynamic_cast<JOB_PCB_RENDER*>( aJob );
613
614 if( aRenderJob == nullptr )
616
617 // Reject width and height being invalid
618 // Final bit of sanity because this can blow things up
619 if( aRenderJob->m_width <= 0 || aRenderJob->m_height <= 0 )
620 {
621 m_reporter->Report( _( "Invalid image dimensions" ), RPT_SEVERITY_ERROR );
623 }
624
625 BOARD* brd = getBoard( aRenderJob->m_filename );
626
627 if( !brd )
629
630 aJob->SetTitleBlock( brd->GetTitleBlock() );
631 brd->GetProject()->ApplyTextVars( aJob->GetVarOverrides() );
633
634 if( aRenderJob->GetConfiguredOutputPath().IsEmpty() )
635 {
636 wxFileName fn = brd->GetFileName();
637
638 switch( aRenderJob->m_format )
639 {
642 default:
643 m_reporter->Report( _( "Unknown export format" ), RPT_SEVERITY_ERROR );
644 return CLI::EXIT_CODES::ERR_UNKNOWN; // shouldnt have gotten here
645 }
646
647 // set the name to board name + "side", its lazy but its hard to generate anything truely unique
648 // incase someone is doing this in a jobset with multiple jobs, they should be setting the output themselves
649 // or we do a hash based on all the options
650 fn.SetName( wxString::Format( "%s-%d", fn.GetName(), static_cast<int>( aRenderJob->m_side ) ) );
651
652 aRenderJob->SetWorkingOutputPath( fn.GetFullName() );
653 }
654
655 wxString outPath = aRenderJob->GetFullOutputPath( brd->GetProject() );
656
657 if( !PATHS::EnsurePathExists( outPath, true ) )
658 {
659 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
661 }
662
663 BOARD_ADAPTER boardAdapter;
664
665 boardAdapter.SetBoard( brd );
666 boardAdapter.m_IsBoardView = false;
667
669
671 {
672 cfg.m_Render = userCfg->m_Render;
673 cfg.m_Camera = userCfg->m_Camera;
674 cfg.m_LayerPresets = userCfg->m_LayerPresets;
675 }
676
677 if( aRenderJob->m_appearancePreset.empty() )
678 {
679 // Force display 3D models
681 cfg.m_Render.show_footprints_dnp = true;
685 }
686
687 if( aRenderJob->m_quality == JOB_PCB_RENDER::QUALITY::BASIC )
688 {
689 // Silkscreen is pixelated without antialiasing
691
692 cfg.m_Render.raytrace_backfloor = aRenderJob->m_floor;
693 cfg.m_Render.raytrace_post_processing = aRenderJob->m_floor;
694
696 cfg.m_Render.raytrace_reflections = false;
697 cfg.m_Render.raytrace_shadows = aRenderJob->m_floor;
698
699 // Better colors
701
702 // Tracks below soldermask are not visible without refractions
705 }
706 else if( aRenderJob->m_quality == JOB_PCB_RENDER::QUALITY::HIGH )
707 {
709 cfg.m_Render.raytrace_backfloor = true;
713 cfg.m_Render.raytrace_shadows = true;
716 }
717 else if( aRenderJob->m_quality == JOB_PCB_RENDER::QUALITY::JOB_SETTINGS )
718 {
720 cfg.m_Render.raytrace_backfloor = aRenderJob->m_floor;
723 }
724
726 aRenderJob->m_lightTopIntensity.y,
727 aRenderJob->m_lightTopIntensity.z, 1.0 );
728
730 aRenderJob->m_lightBottomIntensity.y,
731 aRenderJob->m_lightBottomIntensity.z, 1.0 );
732
734 aRenderJob->m_lightCameraIntensity.y,
735 aRenderJob->m_lightCameraIntensity.z, 1.0 );
736
737 COLOR4D lightColor( aRenderJob->m_lightSideIntensity.x,
738 aRenderJob->m_lightSideIntensity.y,
739 aRenderJob->m_lightSideIntensity.z, 1.0 );
740
742 lightColor, lightColor, lightColor, lightColor,
743 lightColor, lightColor, lightColor, lightColor,
744 };
745
746 int sideElevation = aRenderJob->m_lightSideElevation;
747
749 sideElevation, sideElevation, sideElevation, sideElevation,
750 -sideElevation, -sideElevation, -sideElevation, -sideElevation,
751 };
752
754 45, 135, 225, 315, 45, 135, 225, 315,
755 };
756
757 cfg.m_CurrentPreset = aRenderJob->m_appearancePreset;
759 boardAdapter.m_Cfg = &cfg;
760
763 && aRenderJob->m_format == JOB_PCB_RENDER::FORMAT::PNG ) )
764 {
765 boardAdapter.m_ColorOverrides[LAYER_3D_BACKGROUND_TOP] = COLOR4D( 1.0, 1.0, 1.0, 0.0 );
766 boardAdapter.m_ColorOverrides[LAYER_3D_BACKGROUND_BOTTOM] = COLOR4D( 1.0, 1.0, 1.0, 0.0 );
767 }
768
770
771 static std::map<JOB_PCB_RENDER::SIDE, VIEW3D_TYPE> s_viewCmdMap = {
778 };
779
782
783 wxSize windowSize( aRenderJob->m_width, aRenderJob->m_height );
784 TRACK_BALL camera( 2 * RANGE_SCALE_3D );
785
786 camera.SetProjection( projection );
787 camera.SetCurWindowSize( windowSize );
788
789 RENDER_3D_RAYTRACE_RAM raytrace( boardAdapter, camera );
790 raytrace.SetCurWindowSize( windowSize );
791
792 for( bool first = true; raytrace.Redraw( false, m_reporter, m_reporter ); first = false )
793 {
794 if( first )
795 {
796 const float cmTo3D = boardAdapter.BiuTo3dUnits() * pcbIUScale.mmToIU( 10.0 );
797
798 // First redraw resets lookat point to the board center, so set up the camera here
799 camera.ViewCommand_T1( s_viewCmdMap[aRenderJob->m_side] );
800
801 camera.SetLookAtPos_T1( camera.GetLookAtPos_T1() + SFVEC3F( aRenderJob->m_pivot.x,
802 aRenderJob->m_pivot.y,
803 aRenderJob->m_pivot.z ) * cmTo3D );
804
805 camera.Pan_T1( SFVEC3F( aRenderJob->m_pan.x, aRenderJob->m_pan.y, aRenderJob->m_pan.z ) );
806
807 camera.Zoom_T1( aRenderJob->m_zoom );
808
809 camera.RotateX_T1( DEG2RAD( aRenderJob->m_rotation.x ) );
810 camera.RotateY_T1( DEG2RAD( aRenderJob->m_rotation.y ) );
811 camera.RotateZ_T1( DEG2RAD( aRenderJob->m_rotation.z ) );
812
813 camera.Interpolate( 1.0f );
814 camera.SetT0_and_T1_current_T();
815 camera.ParametersChanged();
816 }
817 }
818
819 uint8_t* rgbaBuffer = raytrace.GetBuffer();
820 wxSize realSize = raytrace.GetRealBufferSize();
821 bool success = !!rgbaBuffer;
822
823 if( rgbaBuffer )
824 {
825 const unsigned int wxh = realSize.x * realSize.y;
826
827 unsigned char* rgbBuffer = (unsigned char*) malloc( wxh * 3 );
828 unsigned char* alphaBuffer = (unsigned char*) malloc( wxh );
829
830 unsigned char* rgbaPtr = rgbaBuffer;
831 unsigned char* rgbPtr = rgbBuffer;
832 unsigned char* alphaPtr = alphaBuffer;
833
834 for( int y = 0; y < realSize.y; y++ )
835 {
836 for( int x = 0; x < realSize.x; x++ )
837 {
838 rgbPtr[0] = rgbaPtr[0];
839 rgbPtr[1] = rgbaPtr[1];
840 rgbPtr[2] = rgbaPtr[2];
841 alphaPtr[0] = rgbaPtr[3];
842
843 rgbaPtr += 4;
844 rgbPtr += 3;
845 alphaPtr += 1;
846 }
847 }
848
849 wxImage image( realSize );
850 image.SetData( rgbBuffer );
851 image.SetAlpha( alphaBuffer );
852 image = image.Mirror( false );
853
854 image.SetOption( wxIMAGE_OPTION_QUALITY, 90 );
855 image.SaveFile( outPath, aRenderJob->m_format == JOB_PCB_RENDER::FORMAT::PNG ? wxBITMAP_TYPE_PNG
856 : wxBITMAP_TYPE_JPEG );
857 }
858
859 if( success )
860 {
861 m_reporter->Report( _( "Successfully created 3D render image" ) + wxS( "\n" ), RPT_SEVERITY_INFO );
862 return CLI::EXIT_CODES::OK;
863 }
864 else
865 {
866 m_reporter->Report( _( "Error creating 3D render image" ) + wxS( "\n" ), RPT_SEVERITY_ERROR );
868 }
869}
870
871
873{
874 JOB_EXPORT_PCB_SVG* aSvgJob = dynamic_cast<JOB_EXPORT_PCB_SVG*>( aJob );
875
876 if( aSvgJob == nullptr )
878
879 BOARD* brd = getBoard( aSvgJob->m_filename );
880 TOOL_MANAGER* toolManager = getToolManager( brd );
881
882 if( !brd )
884
885 aJob->SetTitleBlock( brd->GetTitleBlock() );
886
888 {
889 if( aSvgJob->GetConfiguredOutputPath().IsEmpty() )
890 {
891 wxFileName fn = brd->GetFileName();
892 fn.SetName( fn.GetName() );
894
895 aSvgJob->SetWorkingOutputPath( fn.GetFullName() );
896 }
897 }
898
899 wxString outPath = aSvgJob->GetFullOutputPath( brd->GetProject() );
900
902 {
903 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
905 }
906
908 brd->GetProject()->ApplyTextVars( aJob->GetVarOverrides() );
910
911 if( aSvgJob->m_checkZonesBeforePlot )
912 {
913 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
914 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
915
916 toolManager->GetTool<ZONE_FILLER_TOOL>()->CheckAllZones( nullptr );
917 }
918
919 if( aSvgJob->m_argLayers )
920 aSvgJob->m_plotLayerSequence = convertLayerArg( aSvgJob->m_argLayers.value(), brd );
921
922 if( aSvgJob->m_argCommonLayers )
923 aSvgJob->m_plotOnAllLayersSequence = convertLayerArg( aSvgJob->m_argCommonLayers.value(), brd );
924
925 if( aSvgJob->m_plotLayerSequence.size() < 1 )
926 {
927 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
929 }
930
931 PCB_PLOT_PARAMS plotOpts;
932 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aSvgJob, *m_reporter );
933
934 PCB_PLOTTER plotter( brd, m_reporter, plotOpts );
935
936 std::optional<wxString> layerName;
937 std::optional<wxString> sheetName;
938 std::optional<wxString> sheetPath;
939
941 {
942 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
943 layerName = aSvgJob->GetVarOverrides().at( wxT( "LAYER" ) );
944
945 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
946 sheetName = aSvgJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
947
948 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
949 sheetPath = aSvgJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
950 }
951
952 if( !plotter.Plot( outPath, aSvgJob->m_plotLayerSequence, aSvgJob->m_plotOnAllLayersSequence,
954 layerName, sheetName, sheetPath ) )
955 {
957 }
958
959 return CLI::EXIT_CODES::OK;
960}
961
962
964{
965 JOB_EXPORT_PCB_DXF* aDxfJob = dynamic_cast<JOB_EXPORT_PCB_DXF*>( aJob );
966
967 if( aDxfJob == nullptr )
969
970 BOARD* brd = getBoard( aDxfJob->m_filename );
971
972 if( !brd )
974
975 TOOL_MANAGER* toolManager = getToolManager( brd );
976
977 aJob->SetTitleBlock( brd->GetTitleBlock() );
979 brd->GetProject()->ApplyTextVars( aJob->GetVarOverrides() );
981
982 if( aDxfJob->m_checkZonesBeforePlot )
983 {
984 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
985 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
986
987 toolManager->GetTool<ZONE_FILLER_TOOL>()->CheckAllZones( nullptr );
988 }
989
990 if( aDxfJob->m_argLayers )
991 aDxfJob->m_plotLayerSequence = convertLayerArg( aDxfJob->m_argLayers.value(), brd );
992
993 if( aDxfJob->m_argCommonLayers )
994 aDxfJob->m_plotOnAllLayersSequence = convertLayerArg( aDxfJob->m_argCommonLayers.value(), brd );
995
996 if( aDxfJob->m_plotLayerSequence.size() < 1 )
997 {
998 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1000 }
1001
1003 {
1004 if( aDxfJob->GetConfiguredOutputPath().IsEmpty() )
1005 {
1006 wxFileName fn = brd->GetFileName();
1007 fn.SetName( fn.GetName() );
1009
1010 aDxfJob->SetWorkingOutputPath( fn.GetFullName() );
1011 }
1012 }
1013
1014 wxString outPath = aDxfJob->GetFullOutputPath( brd->GetProject() );
1015
1017 {
1018 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1020 }
1021
1022 PCB_PLOT_PARAMS plotOpts;
1023 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aDxfJob, *m_reporter);
1024
1025 PCB_PLOTTER plotter( brd, m_reporter, plotOpts );
1026
1027 std::optional<wxString> layerName;
1028 std::optional<wxString> sheetName;
1029 std::optional<wxString> sheetPath;
1030
1032 {
1033 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1034 layerName = aDxfJob->GetVarOverrides().at( wxT( "LAYER" ) );
1035
1036 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1037 sheetName = aDxfJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1038
1039 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1040 sheetPath = aDxfJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1041 }
1042
1043 if( !plotter.Plot( outPath, aDxfJob->m_plotLayerSequence, aDxfJob->m_plotOnAllLayersSequence,
1045 layerName, sheetName, sheetPath ) )
1046 {
1048 }
1049
1050 return CLI::EXIT_CODES::OK;
1051}
1052
1053
1055{
1056 bool plotAllLayersOneFile = false;
1057 JOB_EXPORT_PCB_PDF* pdfJob = dynamic_cast<JOB_EXPORT_PCB_PDF*>( aJob );
1058
1059 if( pdfJob == nullptr )
1061
1062 BOARD* brd = getBoard( pdfJob->m_filename );
1063
1064 if( !brd )
1066
1067 TOOL_MANAGER* toolManager = getToolManager( brd );
1068
1069 pdfJob->SetTitleBlock( brd->GetTitleBlock() );
1071 brd->GetProject()->ApplyTextVars( pdfJob->GetVarOverrides() );
1072 brd->SynchronizeProperties();
1073
1074 if( pdfJob->m_checkZonesBeforePlot )
1075 {
1076 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1077 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1078
1079 toolManager->GetTool<ZONE_FILLER_TOOL>()->CheckAllZones( nullptr );
1080 }
1081
1082 if( pdfJob->m_argLayers )
1083 pdfJob->m_plotLayerSequence = convertLayerArg( pdfJob->m_argLayers.value(), brd );
1084
1085 if( pdfJob->m_argCommonLayers )
1086 pdfJob->m_plotOnAllLayersSequence = convertLayerArg( pdfJob->m_argCommonLayers.value(), brd );
1087
1089 plotAllLayersOneFile = true;
1090
1091 if( pdfJob->m_plotLayerSequence.size() < 1 )
1092 {
1093 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1095 }
1096
1097 if( plotAllLayersOneFile && pdfJob->GetConfiguredOutputPath().IsEmpty() )
1098 {
1099 wxFileName fn = brd->GetFileName();
1100 fn.SetName( fn.GetName() );
1102
1103 pdfJob->SetWorkingOutputPath( fn.GetFullName() );
1104 }
1105
1106 PCB_PLOT_PARAMS plotOpts;
1107 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, pdfJob, *m_reporter );
1108
1109 // ensure this is set for this one gen mode
1110 if( plotAllLayersOneFile )
1111 plotOpts.m_PDFSingle = true;
1112
1113 PCB_PLOTTER pcbPlotter( brd, m_reporter, plotOpts );
1114
1115 wxString outPath = pdfJob->GetFullOutputPath( brd->GetProject() );
1116
1117 if( !PATHS::EnsurePathExists( outPath, plotAllLayersOneFile ) )
1118 {
1119 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1121 }
1122
1123 std::optional<wxString> layerName;
1124 std::optional<wxString> sheetName;
1125 std::optional<wxString> sheetPath;
1126
1127 if( plotAllLayersOneFile )
1128 {
1129 if( pdfJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1130 layerName = pdfJob->GetVarOverrides().at( wxT( "LAYER" ) );
1131
1132 if( pdfJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1133 sheetName = pdfJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1134
1135 if( pdfJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1136 sheetPath = pdfJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1137 }
1138
1139 if( !pcbPlotter.Plot( outPath, pdfJob->m_plotLayerSequence,
1140 pdfJob->m_plotOnAllLayersSequence, false, plotAllLayersOneFile,
1141 layerName, sheetName, sheetPath ) )
1142 {
1144 }
1145
1146 return CLI::EXIT_CODES::OK;
1147}
1148
1149
1151{
1152 JOB_EXPORT_PCB_PS* psJob = dynamic_cast<JOB_EXPORT_PCB_PS*>( aJob );
1153
1154 if( psJob == nullptr )
1156
1157 BOARD* brd = getBoard( psJob->m_filename );
1158
1159 if( !brd )
1161
1162 TOOL_MANAGER* toolManager = getToolManager( brd );
1163
1164 psJob->SetTitleBlock( brd->GetTitleBlock() );
1166 brd->GetProject()->ApplyTextVars( psJob->GetVarOverrides() );
1167 brd->SynchronizeProperties();
1168
1169 if( psJob->m_checkZonesBeforePlot )
1170 {
1171 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1172 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1173
1174 toolManager->GetTool<ZONE_FILLER_TOOL>()->CheckAllZones( nullptr );
1175 }
1176
1177 if( psJob->m_argLayers )
1178 psJob->m_plotLayerSequence = convertLayerArg( psJob->m_argLayers.value(), brd );
1179
1180 if( psJob->m_argCommonLayers )
1181 psJob->m_plotOnAllLayersSequence = convertLayerArg( psJob->m_argCommonLayers.value(), brd );
1182
1183 if( psJob->m_plotLayerSequence.size() < 1 )
1184 {
1185 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1187 }
1188
1189 bool isSingle = psJob->m_genMode == JOB_EXPORT_PCB_PS::GEN_MODE::SINGLE;
1190
1191 if( isSingle )
1192 {
1193 if( psJob->GetConfiguredOutputPath().IsEmpty() )
1194 {
1195 wxFileName fn = brd->GetFileName();
1196 fn.SetName( fn.GetName() );
1198
1199 psJob->SetWorkingOutputPath( fn.GetFullName() );
1200 }
1201 }
1202
1203 wxString outPath = psJob->GetFullOutputPath( brd->GetProject() );
1204
1205 if( !PATHS::EnsurePathExists( outPath, isSingle ) )
1206 {
1207 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1209 }
1210
1211 PCB_PLOT_PARAMS plotOpts;
1212 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, psJob, *m_reporter );
1213
1214 PCB_PLOTTER pcbPlotter( brd, m_reporter, plotOpts );
1215
1216 std::optional<wxString> layerName;
1217 std::optional<wxString> sheetName;
1218 std::optional<wxString> sheetPath;
1219
1220 if( isSingle )
1221 {
1222 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1223 layerName = psJob->GetVarOverrides().at( wxT( "LAYER" ) );
1224
1225 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1226 sheetName = psJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1227
1228 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1229 sheetPath = psJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1230 }
1231
1232 if( !pcbPlotter.Plot( outPath, psJob->m_plotLayerSequence, psJob->m_plotOnAllLayersSequence, false, isSingle,
1233 layerName, sheetName, sheetPath ) )
1234 {
1236 }
1237
1238 return CLI::EXIT_CODES::OK;
1239}
1240
1241
1243{
1244 int exitCode = CLI::EXIT_CODES::OK;
1245 JOB_EXPORT_PCB_GERBERS* aGerberJob = dynamic_cast<JOB_EXPORT_PCB_GERBERS*>( aJob );
1246
1247 if( aGerberJob == nullptr )
1249
1250 BOARD* brd = getBoard( aGerberJob->m_filename );
1251
1252 if( !brd )
1254
1255 TOOL_MANAGER* toolManager = getToolManager( brd );
1256
1257 wxString outPath = aGerberJob->GetFullOutputPath( brd->GetProject() );
1258
1259 if( !PATHS::EnsurePathExists( outPath, false ) )
1260 {
1261 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1263 }
1264
1265 aJob->SetTitleBlock( brd->GetTitleBlock() );
1266 loadOverrideDrawingSheet( brd, aGerberJob->m_drawingSheet );
1267 brd->GetProject()->ApplyTextVars( aJob->GetVarOverrides() );
1268 brd->SynchronizeProperties();
1269
1270 if( aGerberJob->m_checkZonesBeforePlot )
1271 {
1272 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1273 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1274
1275 toolManager->GetTool<ZONE_FILLER_TOOL>()->CheckAllZones( nullptr );
1276 }
1277
1278 bool hasLayerListSpecified = false; // will be true if the user layer list is not empty
1279
1280 if( aGerberJob->m_argLayers )
1281 {
1282 if( !aGerberJob->m_argLayers.value().empty() )
1283 {
1284 aGerberJob->m_plotLayerSequence = convertLayerArg( aGerberJob->m_argLayers.value(), brd );
1285 hasLayerListSpecified = true;
1286 }
1287 else
1288 {
1290 }
1291 }
1292
1293 if( aGerberJob->m_argCommonLayers )
1294 aGerberJob->m_plotOnAllLayersSequence = convertLayerArg( aGerberJob->m_argCommonLayers.value(), brd );
1295
1296 PCB_PLOT_PARAMS boardPlotOptions = brd->GetPlotOptions();
1297 GERBER_JOBFILE_WRITER jobfile_writer( brd );
1298
1299 wxString fileExt;
1300
1301 if( aGerberJob->m_useBoardPlotParams )
1302 {
1303 // The board plot options are saved with all copper layers enabled, even those that don't
1304 // exist in the current stackup. This is done so the layers are automatically enabled in the plot
1305 // dialog when the user enables them. We need to filter out these not-enabled layers here so
1306 // we don't plot 32 layers when we only have 4, etc.
1307 LSET plotLayers = ( boardPlotOptions.GetLayerSelection() & LSET::AllNonCuMask() )
1308 | ( brd->GetEnabledLayers() & LSET::AllCuMask() );
1309 aGerberJob->m_plotLayerSequence = plotLayers.SeqStackupForPlotting();
1310 aGerberJob->m_plotOnAllLayersSequence = boardPlotOptions.GetPlotOnAllLayersSequence();
1311 }
1312 else
1313 {
1314 // default to the board enabled layers, but only if the user has not specifed a layer list
1315 // ( m_plotLayerSequence can be empty with a broken user layer list)
1316 if( aGerberJob->m_plotLayerSequence.empty() && !hasLayerListSpecified )
1318 }
1319
1320 // Ensure layers to plot are restricted to enabled layers of the board to plot
1321 LSET layersToPlot = LSET( { aGerberJob->m_plotLayerSequence } ) & brd->GetEnabledLayers();
1322
1323 for( PCB_LAYER_ID layer : layersToPlot.UIOrder() )
1324 {
1325 LSEQ plotSequence;
1326
1327 // Base layer always gets plotted first.
1328 plotSequence.push_back( layer );
1329
1330 // Now all the "include on all" layers
1331 for( PCB_LAYER_ID layer_all : aGerberJob->m_plotOnAllLayersSequence )
1332 {
1333 // Don't plot the same layer more than once;
1334 if( find( plotSequence.begin(), plotSequence.end(), layer_all ) != plotSequence.end() )
1335 continue;
1336
1337 plotSequence.push_back( layer_all );
1338 }
1339
1340 // Pick the basename from the board file
1341 wxFileName fn( brd->GetFileName() );
1342 wxString layerName = brd->GetLayerName( layer );
1343 wxString sheetName;
1344 wxString sheetPath;
1345 PCB_PLOT_PARAMS plotOpts;
1346
1347 if( aGerberJob->m_useBoardPlotParams )
1348 plotOpts = boardPlotOptions;
1349 else
1350 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aGerberJob, *m_reporter );
1351
1352 if( plotOpts.GetUseGerberProtelExtensions() )
1353 fileExt = GetGerberProtelExtension( layer );
1354 else
1356
1357 BuildPlotFileName( &fn, outPath, layerName, fileExt );
1358 wxString fullname = fn.GetFullName();
1359
1360 if( m_progressReporter )
1361 {
1362 m_progressReporter->AdvancePhase( wxString::Format( _( "Exporting %s" ), fullname ) );
1363 m_progressReporter->KeepRefreshing();
1364 }
1365
1366 jobfile_writer.AddGbrFile( layer, fullname );
1367
1368 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1369 layerName = aJob->GetVarOverrides().at( wxT( "LAYER" ) );
1370
1371 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1372 sheetName = aJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1373
1374 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1375 sheetPath = aJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1376
1377 // We are feeding it one layer at the start here to silence a logic check
1378 GERBER_PLOTTER* plotter;
1379 plotter = (GERBER_PLOTTER*) StartPlotBoard( brd, &plotOpts, layer, layerName,
1380 fn.GetFullPath(), sheetName, sheetPath );
1381
1382 if( plotter )
1383 {
1384 m_reporter->Report( wxString::Format( _( "Plotted to '%s'.\n" ), fn.GetFullPath() ),
1386
1387 PlotBoardLayers( brd, plotter, plotSequence, plotOpts );
1388 plotter->EndPlot();
1389 }
1390 else
1391 {
1392 m_reporter->Report( wxString::Format( _( "Failed to plot to '%s'.\n" ), fn.GetFullPath() ),
1395 }
1396
1397 delete plotter;
1398 }
1399
1400 if( aGerberJob->m_createJobsFile )
1401 {
1402 wxFileName fn( brd->GetFileName() );
1403
1404 // Build gerber job file from basename
1405 BuildPlotFileName( &fn, outPath, wxT( "job" ), FILEEXT::GerberJobFileExtension );
1406 jobfile_writer.CreateJobFile( fn.GetFullPath() );
1407 }
1408
1409 return exitCode;
1410}
1411
1412
1414{
1415 JOB_EXPORT_PCB_GENCAD* aGencadJob = dynamic_cast<JOB_EXPORT_PCB_GENCAD*>( aJob );
1416
1417 if( aGencadJob == nullptr )
1419
1420 BOARD* brd = LoadBoard( aGencadJob->m_filename, true ); // Ensure m_board is of type BOARD*
1421
1422 if( brd == nullptr )
1424
1425 GENCAD_EXPORTER exporter( brd );
1426
1427 VECTOR2I GencadOffset;
1428 VECTOR2I auxOrigin = brd->GetDesignSettings().GetAuxOrigin();
1429 GencadOffset.x = aGencadJob->m_useDrillOrigin ? auxOrigin.x : 0;
1430 GencadOffset.y = aGencadJob->m_useDrillOrigin ? auxOrigin.y : 0;
1431
1432 exporter.FlipBottomPads( aGencadJob->m_flipBottomPads );
1433 exporter.UsePinNamesUnique( aGencadJob->m_useUniquePins );
1434 exporter.UseIndividualShapes( aGencadJob->m_useIndividualShapes );
1435 exporter.SetPlotOffet( GencadOffset );
1436 exporter.StoreOriginCoordsInFile( aGencadJob->m_storeOriginCoords );
1437
1438 if( aGencadJob->GetConfiguredOutputPath().IsEmpty() )
1439 {
1440 wxFileName fn = brd->GetFileName();
1441 fn.SetName( fn.GetName() );
1442 fn.SetExt( FILEEXT::GencadFileExtension );
1443
1444 aGencadJob->SetWorkingOutputPath( fn.GetFullName() );
1445 }
1446
1447 wxString outPath = aGencadJob->GetFullOutputPath( brd->GetProject() );
1448
1449 if( !PATHS::EnsurePathExists( outPath, true ) )
1450 {
1451 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1453 }
1454
1455 if( !exporter.WriteFile( outPath ) )
1456 {
1457 m_reporter->Report( wxString::Format( _( "Failed to create file '%s'.\n" ), outPath ),
1459
1461 }
1462
1463 m_reporter->Report( _( "Successfully created genCAD file\n" ), RPT_SEVERITY_INFO );
1464
1465 return CLI::EXIT_CODES::OK;
1466}
1467
1468
1470{
1471 int exitCode = CLI::EXIT_CODES::OK;
1472 JOB_EXPORT_PCB_GERBER* aGerberJob = dynamic_cast<JOB_EXPORT_PCB_GERBER*>( aJob );
1473
1474 if( aGerberJob == nullptr )
1476
1477 BOARD* brd = getBoard( aGerberJob->m_filename );
1478
1479 if( !brd )
1481
1482 TOOL_MANAGER* toolManager = getToolManager( brd );
1483
1484 aJob->SetTitleBlock( brd->GetTitleBlock() );
1485 brd->GetProject()->ApplyTextVars( aJob->GetVarOverrides() );
1486 brd->SynchronizeProperties();
1487
1488 if( aGerberJob->m_checkZonesBeforePlot )
1489 {
1490 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
1491 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
1492
1493 toolManager->GetTool<ZONE_FILLER_TOOL>()->CheckAllZones( nullptr );
1494 }
1495
1496 if( aGerberJob->m_argLayers )
1497 aGerberJob->m_plotLayerSequence = convertLayerArg( aGerberJob->m_argLayers.value(), brd );
1498
1499 if( aGerberJob->m_argCommonLayers )
1500 aGerberJob->m_plotOnAllLayersSequence = convertLayerArg( aGerberJob->m_argCommonLayers.value(), brd );
1501
1502 if( aGerberJob->m_plotLayerSequence.size() < 1 )
1503 {
1504 m_reporter->Report( _( "At least one layer must be specified\n" ), RPT_SEVERITY_ERROR );
1506 }
1507
1508 if( aGerberJob->GetConfiguredOutputPath().IsEmpty() )
1509 {
1510 wxFileName fn = brd->GetFileName();
1511 fn.SetName( fn.GetName() );
1513
1514 aGerberJob->SetWorkingOutputPath( fn.GetFullName() );
1515 }
1516
1517 PCB_PLOT_PARAMS plotOpts;
1518 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aGerberJob, *m_reporter );
1519 plotOpts.SetLayerSelection( aGerberJob->m_plotLayerSequence );
1521
1523 wxString layerName;
1524 wxString sheetName;
1525 wxString sheetPath;
1526 wxString outPath = aGerberJob->GetFullOutputPath( brd->GetProject() );
1527
1528 // The first layer will be treated as the layer name for the gerber header,
1529 // the other layers will be treated equivalent to the "Plot on All Layers" option
1530 // in the GUI
1531 if( aGerberJob->m_plotLayerSequence.size() >= 1 )
1532 {
1533 layer = aGerberJob->m_plotLayerSequence.front();
1534 layerName = brd->GetLayerName( layer );
1535 }
1536
1537 if( aJob->GetVarOverrides().contains( wxT( "LAYER" ) ) )
1538 layerName = aJob->GetVarOverrides().at( wxT( "LAYER" ) );
1539
1540 if( aJob->GetVarOverrides().contains( wxT( "SHEETNAME" ) ) )
1541 sheetName = aJob->GetVarOverrides().at( wxT( "SHEETNAME" ) );
1542
1543 if( aJob->GetVarOverrides().contains( wxT( "SHEETPATH" ) ) )
1544 sheetPath = aJob->GetVarOverrides().at( wxT( "SHEETPATH" ) );
1545
1546 // We are feeding it one layer at the start here to silence a logic check
1547 PLOTTER* plotter = StartPlotBoard( brd, &plotOpts, layer, layerName, outPath, sheetName,
1548 sheetPath );
1549
1550 if( plotter )
1551 {
1552 PlotBoardLayers( brd, plotter, aGerberJob->m_plotLayerSequence, plotOpts );
1553 plotter->EndPlot();
1554 }
1555 else
1556 {
1557 m_reporter->Report( wxString::Format( _( "Failed to plot to '%s'.\n" ), outPath ),
1560 }
1561
1562 delete plotter;
1563
1564 return exitCode;
1565}
1566
1569
1570
1572{
1573 JOB_EXPORT_PCB_DRILL* aDrillJob = dynamic_cast<JOB_EXPORT_PCB_DRILL*>( aJob );
1574
1575 if( aDrillJob == nullptr )
1577
1578 BOARD* brd = getBoard( aDrillJob->m_filename );
1579
1580 if( !brd )
1582
1583 aJob->SetTitleBlock( brd->GetTitleBlock() );
1584
1585 wxString outPath = aDrillJob->GetFullOutputPath( brd->GetProject() );
1586
1587 if( !PATHS::EnsurePathExists( outPath ) )
1588 {
1589 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1591 }
1592
1593 std::unique_ptr<GENDRILL_WRITER_BASE> drillWriter;
1594
1596 drillWriter = std::make_unique<EXCELLON_WRITER>( brd );
1597 else
1598 drillWriter = std::make_unique<GERBER_WRITER>( brd );
1599
1600 VECTOR2I offset;
1601
1603 offset = VECTOR2I( 0, 0 );
1604 else
1605 offset = brd->GetDesignSettings().GetAuxOrigin();
1606
1607 PLOT_FORMAT mapFormat = PLOT_FORMAT::PDF;
1608
1609 switch( aDrillJob->m_mapFormat )
1610 {
1615 default:
1617 }
1618
1620 {
1622 switch( aDrillJob->m_zeroFormat )
1623 {
1626 break;
1629 break;
1632 break;
1634 default:
1636 break;
1637 }
1638
1639 DRILL_PRECISION precision;
1640
1642 precision = precisionListForInches;
1643 else
1644 precision = precisionListForMetric;
1645
1646 EXCELLON_WRITER* excellonWriter = dynamic_cast<EXCELLON_WRITER*>( drillWriter.get() );
1647
1648 if( excellonWriter == nullptr )
1650
1651 excellonWriter->SetFormat( aDrillJob->m_drillUnits == JOB_EXPORT_PCB_DRILL::DRILL_UNITS::MM,
1652 zeroFmt, precision.m_Lhs, precision.m_Rhs );
1653 excellonWriter->SetOptions( aDrillJob->m_excellonMirrorY,
1654 aDrillJob->m_excellonMinimalHeader,
1655 offset, aDrillJob->m_excellonCombinePTHNPTH );
1656 excellonWriter->SetRouteModeForOvalHoles( aDrillJob->m_excellonOvalDrillRoute );
1657 excellonWriter->SetMapFileFormat( mapFormat );
1658
1659 if( !excellonWriter->CreateDrillandMapFilesSet( outPath, true, aDrillJob->m_generateMap,
1660 m_reporter ) )
1661 {
1663 }
1664 }
1666 {
1667 GERBER_WRITER* gerberWriter = dynamic_cast<GERBER_WRITER*>( drillWriter.get() );
1668
1669 if( gerberWriter == nullptr )
1671
1672 // Set gerber precision: only 5 or 6 digits for mantissa are allowed
1673 // (SetFormat() accept 5 or 6, and any other value set the precision to 5)
1674 // the integer part precision is always 4, and units always mm
1675 gerberWriter->SetFormat( aDrillJob->m_gerberPrecision );
1676 gerberWriter->SetOptions( offset );
1677 gerberWriter->SetMapFileFormat( mapFormat );
1678
1679 if( !gerberWriter->CreateDrillandMapFilesSet( outPath, true, aDrillJob->m_generateMap,
1680 aDrillJob->m_generateTenting, m_reporter ) )
1681 {
1683 }
1684 }
1685
1686 return CLI::EXIT_CODES::OK;
1687}
1688
1689
1691{
1692 JOB_EXPORT_PCB_POS* aPosJob = dynamic_cast<JOB_EXPORT_PCB_POS*>( aJob );
1693
1694 if( aPosJob == nullptr )
1696
1697 BOARD* brd = getBoard( aPosJob->m_filename );
1698
1699 if( !brd )
1701
1702 aJob->SetTitleBlock( brd->GetTitleBlock() );
1703
1704 if( aPosJob->GetConfiguredOutputPath().IsEmpty() )
1705 {
1706 wxFileName fn = brd->GetFileName();
1707 fn.SetName( fn.GetName() );
1708
1711 else if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::CSV )
1712 fn.SetExt( FILEEXT::CsvFileExtension );
1713 else if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::GERBER )
1714 fn.SetExt( FILEEXT::GerberFileExtension );
1715
1716 aPosJob->SetWorkingOutputPath( fn.GetFullName() );
1717 }
1718
1719 wxString outPath = aPosJob->GetFullOutputPath( brd->GetProject() );
1720
1721 if( !PATHS::EnsurePathExists( outPath, true ) )
1722 {
1723 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1725 }
1726
1729 {
1730 wxFileName fn( outPath );
1731 wxString baseName = fn.GetName();
1732
1733 auto exportPlaceFile =
1734 [&]( bool frontSide, bool backSide, const wxString& curr_outPath ) -> bool
1735 {
1736 FILE* file = wxFopen( curr_outPath, wxS( "wt" ) );
1737 wxCHECK( file, false );
1738
1739 PLACE_FILE_EXPORTER exporter( brd,
1741 aPosJob->m_smdOnly,
1743 aPosJob->m_excludeDNP,
1744 frontSide,
1745 backSide,
1748 aPosJob->m_negateBottomX );
1749
1750 std::string data = exporter.GenPositionData();
1751 fputs( data.c_str(), file );
1752 fclose( file );
1753
1754 return true;
1755 };
1756
1757 if( aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH && !aPosJob->m_singleFile )
1758 {
1759 fn.SetName( PLACE_FILE_EXPORTER::DecorateFilename( baseName, true, false ) );
1760
1761 if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::CSV && !aPosJob->m_nakedFilename )
1762 fn.SetName( fn.GetName() + wxT( "-" ) + FILEEXT::FootprintPlaceFileExtension );
1763
1764 if( exportPlaceFile( true, false, fn.GetFullPath() ) )
1765 {
1766 m_reporter->Report( wxString::Format( _( "Wrote front position data to '%s'.\n" ),
1767 fn.GetFullPath() ),
1769
1770 aPosJob->AddOutput( fn.GetFullPath() );
1771 }
1772 else
1773 {
1775 }
1776
1777 fn.SetName( PLACE_FILE_EXPORTER::DecorateFilename( baseName, false, true ) );
1778
1779 if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::CSV && !aPosJob->m_nakedFilename )
1780 fn.SetName( fn.GetName() + wxT( "-" ) + FILEEXT::FootprintPlaceFileExtension );
1781
1782 if( exportPlaceFile( false, true, fn.GetFullPath() ) )
1783 {
1784 m_reporter->Report( wxString::Format( _( "Wrote back position data to '%s'.\n" ),
1785 fn.GetFullPath() ),
1787
1788 aPosJob->AddOutput( fn.GetFullPath() );
1789 }
1790 else
1791 {
1793 }
1794 }
1795 else
1796 {
1797 bool front = aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::FRONT
1799
1800 bool back = aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BACK
1802
1803 if( !aPosJob->m_nakedFilename )
1804 {
1805 fn.SetName( PLACE_FILE_EXPORTER::DecorateFilename( fn.GetName(), front, back ) );
1806
1808 fn.SetName( fn.GetName() + wxT( "-" ) + FILEEXT::FootprintPlaceFileExtension );
1809 }
1810
1811 if( exportPlaceFile( front, back, fn.GetFullPath() ) )
1812 {
1813 m_reporter->Report( wxString::Format( _( "Wrote position data to '%s'.\n" ),
1814 fn.GetFullPath() ),
1816
1817 aPosJob->AddOutput( fn.GetFullPath() );
1818 }
1819 else
1820 {
1822 }
1823 }
1824 }
1825 else if( aPosJob->m_format == JOB_EXPORT_PCB_POS::FORMAT::GERBER )
1826 {
1827 PLACEFILE_GERBER_WRITER exporter( brd );
1828 PCB_LAYER_ID gbrLayer = F_Cu;
1829 wxString outPath_base = outPath;
1830
1832 || aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH )
1833 {
1834 if( aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH || !aPosJob->m_nakedFilename )
1835 outPath = exporter.GetPlaceFileName( outPath, gbrLayer );
1836
1837 if( exporter.CreatePlaceFile( outPath, gbrLayer, aPosJob->m_gerberBoardEdge, aPosJob->m_excludeDNP ) >= 0 )
1838 {
1839 m_reporter->Report( wxString::Format( _( "Wrote front position data to '%s'.\n" ), outPath ),
1841
1842 aPosJob->AddOutput( outPath );
1843 }
1844 else
1845 {
1847 }
1848 }
1849
1851 || aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH )
1852 {
1853 gbrLayer = B_Cu;
1854
1855 outPath = outPath_base;
1856
1857 if( aPosJob->m_side == JOB_EXPORT_PCB_POS::SIDE::BOTH || !aPosJob->m_nakedFilename )
1858 outPath = exporter.GetPlaceFileName( outPath, gbrLayer );
1859
1860 if( exporter.CreatePlaceFile( outPath, gbrLayer, aPosJob->m_gerberBoardEdge, aPosJob->m_excludeDNP ) >= 0 )
1861 {
1862 m_reporter->Report( wxString::Format( _( "Wrote back position data to '%s'.\n" ), outPath ),
1864
1865 aPosJob->AddOutput( outPath );
1866 }
1867 else
1868 {
1870 }
1871 }
1872 }
1873
1874 return CLI::EXIT_CODES::OK;
1875}
1876
1877
1879{
1880 JOB_FP_UPGRADE* upgradeJob = dynamic_cast<JOB_FP_UPGRADE*>( aJob );
1881
1882 if( upgradeJob == nullptr )
1884
1886
1887 if( !upgradeJob->m_outputLibraryPath.IsEmpty() )
1888 {
1889 if( wxFile::Exists( upgradeJob->m_outputLibraryPath )
1890 || wxDir::Exists( upgradeJob->m_outputLibraryPath) )
1891 {
1892 m_reporter->Report( _( "Output path must not conflict with existing path\n" ),
1895 }
1896 }
1897 else if( fileType != PCB_IO_MGR::KICAD_SEXP )
1898 {
1899 m_reporter->Report( _( "Output path must be specified to convert legacy and non-KiCad libraries\n" ),
1901
1903 }
1904
1906 {
1907 if( !wxDir::Exists( upgradeJob->m_libraryPath ) )
1908 {
1909 m_reporter->Report( _( "Footprint library path does not exist or is not accessible\n" ),
1912 }
1913
1915 FP_CACHE fpLib( &pcb_io, upgradeJob->m_libraryPath );
1916
1917 try
1918 {
1919 fpLib.Load();
1920 }
1921 catch( ... )
1922 {
1923 m_reporter->Report( _( "Unable to load library\n" ), RPT_SEVERITY_ERROR );
1925 }
1926
1927 if( m_progressReporter )
1928 m_progressReporter->KeepRefreshing();
1929
1930 bool shouldSave = upgradeJob->m_force;
1931
1932 for( const auto& footprint : fpLib.GetFootprints() )
1933 {
1934 if( footprint.second->GetFootprint()->GetFileFormatVersionAtLoad() < SEXPR_BOARD_FILE_VERSION )
1935 shouldSave = true;
1936 }
1937
1938 if( shouldSave )
1939 {
1940 try
1941 {
1942 if( !upgradeJob->m_outputLibraryPath.IsEmpty() )
1943 fpLib.SetPath( upgradeJob->m_outputLibraryPath );
1944
1945 fpLib.Save();
1946 }
1947 catch( ... )
1948 {
1949 m_reporter->Report( _( "Unable to save library\n" ), RPT_SEVERITY_ERROR );
1951 }
1952 }
1953 else
1954 {
1955 m_reporter->Report( _( "Footprint library was not updated\n" ), RPT_SEVERITY_ERROR );
1956 }
1957 }
1958 else
1959 {
1960 if( !PCB_IO_MGR::ConvertLibrary( nullptr, upgradeJob->m_libraryPath,
1961 upgradeJob->m_outputLibraryPath, nullptr /* REPORTER */ ) )
1962 {
1963 m_reporter->Report( ( "Unable to convert library\n" ), RPT_SEVERITY_ERROR );
1965 }
1966 }
1967
1968 return CLI::EXIT_CODES::OK;
1969}
1970
1971
1973{
1974 JOB_FP_EXPORT_SVG* svgJob = dynamic_cast<JOB_FP_EXPORT_SVG*>( aJob );
1975
1976 if( svgJob == nullptr )
1978
1980 FP_CACHE fpLib( &pcb_io, svgJob->m_libraryPath );
1981
1982 if( svgJob->m_argLayers )
1983 {
1984 if( !svgJob->m_argLayers.value().empty() )
1985 svgJob->m_plotLayerSequence = convertLayerArg( svgJob->m_argLayers.value(), nullptr );
1986 else
1988 }
1989
1990 try
1991 {
1992 fpLib.Load();
1993 }
1994 catch( ... )
1995 {
1996 m_reporter->Report( _( "Unable to load library\n" ), RPT_SEVERITY_ERROR );
1998 }
1999
2000 wxString outPath = svgJob->GetFullOutputPath( nullptr );
2001
2002 if( !PATHS::EnsurePathExists( outPath, true ) )
2003 {
2004 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2006 }
2007
2008 int exitCode = CLI::EXIT_CODES::OK;
2009 bool singleFpPlotted = false;
2010
2011 for( const auto& [fpName, fpCacheEntry] : fpLib.GetFootprints() )
2012 {
2013 if( m_progressReporter )
2014 {
2015 m_progressReporter->AdvancePhase( wxString::Format( _( "Exporting %s" ), fpName ) );
2016 m_progressReporter->KeepRefreshing();
2017 }
2018
2019 if( !svgJob->m_footprint.IsEmpty() )
2020 {
2021 // skip until we find the right footprint
2022 if( fpName != svgJob->m_footprint )
2023 continue;
2024 else
2025 singleFpPlotted = true;
2026 }
2027
2028 exitCode = doFpExportSvg( svgJob, fpCacheEntry->GetFootprint().get() );
2029
2030 if( exitCode != CLI::EXIT_CODES::OK )
2031 break;
2032 }
2033
2034 if( !svgJob->m_footprint.IsEmpty() && !singleFpPlotted )
2035 {
2036 m_reporter->Report( _( "The given footprint could not be found to export." ) + wxS( "\n" ),
2038 }
2039
2040 return CLI::EXIT_CODES::OK;
2041}
2042
2043
2045{
2046 // the hack for now is we create fake boards containing the footprint and plot the board
2047 // until we refactor better plot api later
2048 std::unique_ptr<BOARD> brd;
2049 brd.reset( CreateEmptyBoard() );
2050 brd->GetProject()->ApplyTextVars( aSvgJob->GetVarOverrides() );
2051 brd->SynchronizeProperties();
2052
2053 FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( aFootprint->Clone() );
2054
2055 if( fp == nullptr )
2057
2058 fp->SetLink( niluuid );
2059 fp->SetFlags( IS_NEW );
2060 fp->SetParent( brd.get() );
2061
2062 for( PAD* pad : fp->Pads() )
2063 {
2064 pad->SetLocalRatsnestVisible( false );
2065 pad->SetNetCode( 0 );
2066 }
2067
2068 fp->SetOrientation( ANGLE_0 );
2069 fp->SetPosition( VECTOR2I( 0, 0 ) );
2070
2071 brd->Add( fp, ADD_MODE::INSERT, true );
2072
2073 wxFileName outputFile;
2074 outputFile.SetPath( aSvgJob->GetFullOutputPath(nullptr) );
2075 outputFile.SetName( aFootprint->GetFPID().GetLibItemName().wx_str() );
2076 outputFile.SetExt( FILEEXT::SVGFileExtension );
2077
2078 m_reporter->Report( wxString::Format( _( "Plotting footprint '%s' to '%s'\n" ),
2079 aFootprint->GetFPID().GetLibItemName().wx_str(),
2080 outputFile.GetFullPath() ),
2082
2083 PCB_PLOT_PARAMS plotOpts;
2084 PCB_PLOTTER::PlotJobToPlotOpts( plotOpts, aSvgJob, *m_reporter );
2085
2086 // always fixed for the svg plot
2087 plotOpts.SetPlotFrameRef( false );
2088 plotOpts.SetSvgFitPageToBoard( true );
2089 plotOpts.SetMirror( false );
2090 plotOpts.SetSkipPlotNPTH_Pads( false );
2091
2092 if( plotOpts.GetSketchPadsOnFabLayers() )
2093 {
2094 plotOpts.SetPlotPadNumbers( true );
2095 }
2096
2097 PCB_PLOTTER plotter( brd.get(), m_reporter, plotOpts );
2098
2099 if( !plotter.Plot( outputFile.GetFullPath(),
2100 aSvgJob->m_plotLayerSequence,
2102 false,
2103 true,
2104 wxEmptyString, wxEmptyString,
2105 wxEmptyString ) )
2106 {
2107 m_reporter->Report( _( "Error creating svg file" ) + wxS( "\n" ), RPT_SEVERITY_ERROR );
2109 }
2110
2111 return CLI::EXIT_CODES::OK;
2112}
2113
2114
2116{
2117 JOB_PCB_DRC* drcJob = dynamic_cast<JOB_PCB_DRC*>( aJob );
2118
2119 if( drcJob == nullptr )
2121
2122 BOARD* brd = getBoard( drcJob->m_filename );
2123
2124 if( !brd )
2126
2127 aJob->SetTitleBlock( brd->GetTitleBlock() );
2128 brd->GetProject()->ApplyTextVars( aJob->GetVarOverrides() );
2129 brd->SynchronizeProperties();
2130
2131 if( drcJob->GetConfiguredOutputPath().IsEmpty() )
2132 {
2133 wxFileName fn = brd->GetFileName();
2134 fn.SetName( fn.GetName() + wxS( "-drc" ) );
2135
2137 fn.SetExt( FILEEXT::JsonFileExtension );
2138 else
2139 fn.SetExt( FILEEXT::ReportFileExtension );
2140
2141 drcJob->SetWorkingOutputPath( fn.GetFullName() );
2142 }
2143
2144 wxString outPath = drcJob->GetFullOutputPath( brd->GetProject() );
2145
2146 if( !PATHS::EnsurePathExists( outPath, true ) )
2147 {
2148 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2150 }
2151
2152 EDA_UNITS units;
2153
2154 switch( drcJob->m_units )
2155 {
2156 case JOB_PCB_DRC::UNITS::INCH: units = EDA_UNITS::INCH; break;
2157 case JOB_PCB_DRC::UNITS::MILS: units = EDA_UNITS::MILS; break;
2158 case JOB_PCB_DRC::UNITS::MM: units = EDA_UNITS::MM; break;
2159 default: units = EDA_UNITS::MM; break;
2160 }
2161
2162 std::shared_ptr<DRC_ENGINE> drcEngine = brd->GetDesignSettings().m_DRCEngine;
2163 std::unique_ptr<NETLIST> netlist = std::make_unique<NETLIST>();
2164
2165 drcEngine->SetDrawingSheet( getDrawingSheetProxyView( brd ) );
2166
2167 // BOARD_COMMIT uses TOOL_MANAGER to grab the board internally so we must give it one
2168 TOOL_MANAGER* toolManager = getToolManager( brd );
2169
2170 BOARD_COMMIT commit( toolManager );
2171 bool checkParity = drcJob->m_parity;
2172 std::string netlist_str;
2173
2174 if( checkParity )
2175 {
2176 wxString annotateMsg = _( "Schematic parity tests require a fully annotated schematic." );
2177 netlist_str = annotateMsg;
2178
2179 // The KIFACE_NETLIST_SCHEMATIC function has some broken-ness that the schematic
2180 // frame's version does not, but it is the only one that works in CLI, so we use it
2181 // if we don't have the sch frame open.
2182 // TODO: clean this up, see https://gitlab.com/kicad/code/kicad/-/issues/19929
2183 if( m_kiway->Player( FRAME_SCH, false ) )
2184 {
2185 m_kiway->ExpressMail( FRAME_SCH, MAIL_SCH_GET_NETLIST, netlist_str );
2186 }
2187 else
2188 {
2189 wxFileName schematicPath( drcJob->m_filename );
2190 schematicPath.SetExt( FILEEXT::KiCadSchematicFileExtension );
2191
2192 if( !schematicPath.Exists() )
2193 schematicPath.SetExt( FILEEXT::LegacySchematicFileExtension );
2194
2195 if( !schematicPath.Exists() )
2196 {
2197 m_reporter->Report( _( "Failed to fetch schematic netlist for parity tests.\n" ),
2199 checkParity = false;
2200 }
2201 else
2202 {
2203 typedef bool ( *NETLIST_FN_PTR )( const wxString&, std::string& );
2204 KIFACE* eeschema = m_kiway->KiFACE( KIWAY::FACE_SCH );
2205 NETLIST_FN_PTR netlister =
2206 (NETLIST_FN_PTR) eeschema->IfaceOrAddress( KIFACE_NETLIST_SCHEMATIC );
2207 ( *netlister )( schematicPath.GetFullPath(), netlist_str );
2208 }
2209 }
2210
2211 if( netlist_str == annotateMsg )
2212 {
2213 m_reporter->Report( wxString( netlist_str ) + wxT( "\n" ), RPT_SEVERITY_ERROR );
2214 checkParity = false;
2215 }
2216 }
2217
2218 if( checkParity )
2219 {
2220 try
2221 {
2222 STRING_LINE_READER* lineReader = new STRING_LINE_READER( netlist_str,
2223 _( "Eeschema netlist" ) );
2224 KICAD_NETLIST_READER netlistReader( lineReader, netlist.get() );
2225
2226 netlistReader.LoadNetlist();
2227 }
2228 catch( const IO_ERROR& )
2229 {
2230 m_reporter->Report( _( "Failed to fetch schematic netlist for parity tests.\n" ),
2232 checkParity = false;
2233 }
2234
2235 drcEngine->SetSchematicNetlist( netlist.get() );
2236 }
2237
2238 if( drcJob->m_refillZones )
2239 {
2240 if( !toolManager->FindTool( ZONE_FILLER_TOOL_NAME ) )
2241 toolManager->RegisterTool( new ZONE_FILLER_TOOL );
2242
2243 toolManager->GetTool<ZONE_FILLER_TOOL>()->FillAllZones( nullptr, m_progressReporter, true );
2244 }
2245
2246 drcEngine->SetProgressReporter( m_progressReporter );
2247 drcEngine->SetViolationHandler(
2248 [&]( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos, int aLayer,
2249 const std::vector<PCB_SHAPE>& aShapes )
2250 {
2251 PCB_MARKER* marker = new PCB_MARKER( aItem, aPos, aLayer );
2252 marker->SetShapes( aShapes );
2253 commit.Add( marker );
2254 } );
2255
2256 brd->RecordDRCExclusions();
2257 brd->DeleteMARKERs( true, true );
2258 drcEngine->RunTests( units, drcJob->m_reportAllTrackErrors, checkParity );
2259 drcEngine->ClearViolationHandler();
2260
2261 commit.Push( _( "DRC" ), SKIP_UNDO | SKIP_SET_DIRTY );
2262
2263 // Update the exclusion status on any excluded markers that still exist.
2264 brd->ResolveDRCExclusions( false );
2265
2266 std::shared_ptr<DRC_ITEMS_PROVIDER> markersProvider = std::make_shared<DRC_ITEMS_PROVIDER>(
2268
2269 std::shared_ptr<DRC_ITEMS_PROVIDER> ratsnestProvider =
2270 std::make_shared<DRC_ITEMS_PROVIDER>( brd, MARKER_BASE::MARKER_RATSNEST );
2271
2272 std::shared_ptr<DRC_ITEMS_PROVIDER> fpWarningsProvider =
2273 std::make_shared<DRC_ITEMS_PROVIDER>( brd, MARKER_BASE::MARKER_PARITY );
2274
2275 markersProvider->SetSeverities( drcJob->m_severity );
2276 ratsnestProvider->SetSeverities( drcJob->m_severity );
2277 fpWarningsProvider->SetSeverities( drcJob->m_severity );
2278
2279 m_reporter->Report( wxString::Format( _( "Found %d violations\n" ),
2280 markersProvider->GetCount() ),
2282 m_reporter->Report( wxString::Format( _( "Found %d unconnected items\n" ),
2283 ratsnestProvider->GetCount() ),
2285
2286 if( checkParity )
2287 {
2288 m_reporter->Report( wxString::Format( _( "Found %d schematic parity issues\n" ),
2289 fpWarningsProvider->GetCount() ),
2291 }
2292
2293 DRC_REPORT reportWriter( brd, units, markersProvider, ratsnestProvider, fpWarningsProvider );
2294
2295 bool wroteReport = false;
2296
2298 wroteReport = reportWriter.WriteJsonReport( outPath );
2299 else
2300 wroteReport = reportWriter.WriteTextReport( outPath );
2301
2302 if( !wroteReport )
2303 {
2304 m_reporter->Report( wxString::Format( _( "Unable to save DRC report to %s\n" ), outPath ),
2307 }
2308
2309 m_reporter->Report( wxString::Format( _( "Saved DRC Report to %s\n" ), outPath ),
2311
2312 if( drcJob->m_refillZones && drcJob->m_saveBoard )
2313 {
2314 if( SaveBoard( drcJob->m_filename, brd, true ) )
2315 {
2316 m_reporter->Report( _( "Saved board\n" ), RPT_SEVERITY_ACTION );
2317 }
2318 else
2319 {
2320 m_reporter->Report( _( "Failed to save board.\n" ), RPT_SEVERITY_ERROR );
2321
2323 }
2324 }
2325
2326 if( drcJob->m_exitCodeViolations )
2327 {
2328 if( markersProvider->GetCount() > 0 || ratsnestProvider->GetCount() > 0
2329 || fpWarningsProvider->GetCount() > 0 )
2330 {
2332 }
2333 }
2334
2336}
2337
2338
2340{
2341 JOB_EXPORT_PCB_IPC2581* job = dynamic_cast<JOB_EXPORT_PCB_IPC2581*>( aJob );
2342
2343 if( job == nullptr )
2345
2346 BOARD* brd = getBoard( job->m_filename );
2347
2348 if( !brd )
2350
2351 aJob->SetTitleBlock( brd->GetTitleBlock() );
2352
2353 if( job->GetConfiguredOutputPath().IsEmpty() )
2354 {
2355 wxFileName fn = brd->GetFileName();
2356 fn.SetName( fn.GetName() );
2357 fn.SetExt( FILEEXT::Ipc2581FileExtension );
2358
2359 job->SetWorkingOutputPath( fn.GetName() );
2360 }
2361
2362 wxString outPath = job->GetFullOutputPath( brd->GetProject() );
2363
2364 if( !PATHS::EnsurePathExists( outPath, true ) )
2365 {
2366 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2368 }
2369
2370 std::map<std::string, UTF8> props;
2371 props["units"] = job->m_units == JOB_EXPORT_PCB_IPC2581::IPC2581_UNITS::MM ? "mm" : "inch";
2372 props["sigfig"] = wxString::Format( "%d", job->m_precision );
2373 props["version"] = job->m_version == JOB_EXPORT_PCB_IPC2581::IPC2581_VERSION::C ? "C" : "B";
2374 props["OEMRef"] = job->m_colInternalId;
2375 props["mpn"] = job->m_colMfgPn;
2376 props["mfg"] = job->m_colMfg;
2377 props["dist"] = job->m_colDist;
2378 props["distpn"] = job->m_colDistPn;
2379
2380 wxString tempFile = wxFileName::CreateTempFileName( wxS( "pcbnew_ipc" ) );
2381 try
2382 {
2384 pi->SetProgressReporter( m_progressReporter );
2385 pi->SaveBoard( tempFile, brd, &props );
2386 }
2387 catch( const IO_ERROR& ioe )
2388 {
2389 m_reporter->Report( wxString::Format( _( "Error generating IPC-2581 file '%s'.\n%s" ),
2390 job->m_filename,
2391 ioe.What() ),
2393
2394 wxRemoveFile( tempFile );
2395
2397 }
2398
2399 if( job->m_compress )
2400 {
2401 wxFileName tempfn = outPath;
2402 tempfn.SetExt( FILEEXT::Ipc2581FileExtension );
2403 wxFileName zipfn = tempFile;
2404 zipfn.SetExt( "zip" );
2405
2406 {
2407 wxFFileOutputStream fnout( zipfn.GetFullPath() );
2408 wxZipOutputStream zip( fnout );
2409 wxFFileInputStream fnin( tempFile );
2410
2411 zip.PutNextEntry( tempfn.GetFullName() );
2412 fnin.Read( zip );
2413 }
2414
2415 wxRemoveFile( tempFile );
2416 tempFile = zipfn.GetFullPath();
2417 }
2418
2419 // If save succeeded, replace the original with what we just wrote
2420 if( !wxRenameFile( tempFile, outPath ) )
2421 {
2422 m_reporter->Report( wxString::Format( _( "Error generating IPC-2581 file '%s'.\n"
2423 "Failed to rename temporary file '%s." ),
2424 outPath,
2425 tempFile ),
2427 }
2428
2430}
2431
2432
2434{
2435 JOB_EXPORT_PCB_IPCD356* job = dynamic_cast<JOB_EXPORT_PCB_IPCD356*>( aJob );
2436
2437 if( job == nullptr )
2439
2440 BOARD* brd = getBoard( job->m_filename );
2441
2442 if( !brd )
2444
2445 aJob->SetTitleBlock( brd->GetTitleBlock() );
2446
2447 if( job->GetConfiguredOutputPath().IsEmpty() )
2448 {
2449 wxFileName fn = brd->GetFileName();
2450 fn.SetName( fn.GetName() );
2451 fn.SetExt( FILEEXT::IpcD356FileExtension );
2452
2453 job->SetWorkingOutputPath( fn.GetFullName() );
2454 }
2455
2456 wxString outPath = job->GetFullOutputPath( brd->GetProject() );
2457
2458 if( !PATHS::EnsurePathExists( outPath, true ) )
2459 {
2460 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
2462 }
2463
2464 IPC356D_WRITER exporter( brd );
2465
2466 bool success = exporter.Write( outPath );
2467
2468 if( success )
2469 {
2470 m_reporter->Report( _( "Successfully created IPC-D-356 file\n" ), RPT_SEVERITY_INFO );
2472 }
2473 else
2474 {
2475 m_reporter->Report( _( "Failed to create IPC-D-356 file\n" ), RPT_SEVERITY_ERROR );
2477 }
2478}
2479
2480
2482{
2483 JOB_EXPORT_PCB_ODB* job = dynamic_cast<JOB_EXPORT_PCB_ODB*>( aJob );
2484
2485 if( job == nullptr )
2487
2488 BOARD* brd = getBoard( job->m_filename );
2489
2490 if( !brd )
2492
2493 aJob->SetTitleBlock( brd->GetTitleBlock() );
2494
2495 wxString path = job->GetConfiguredOutputPath();
2496
2497 if( job->GetConfiguredOutputPath().IsEmpty() )
2498 {
2500 {
2501 // just basic folder name
2502 job->SetWorkingOutputPath( "odb" );
2503 }
2504 else
2505 {
2506 wxFileName fn( brd->GetFileName() );
2507 fn.SetName( fn.GetName() + wxS( "-odb" ) );
2508
2509 switch( job->m_compressionMode )
2510 {
2512 fn.SetExt( FILEEXT::ArchiveFileExtension );
2513 break;
2515 fn.SetExt( "tgz" );
2516 break;
2517 default:
2518 break;
2519 };
2520
2521 job->SetWorkingOutputPath( fn.GetFullName() );
2522 }
2523 }
2524
2526
2528}
2529
2531{
2532 JOB_PCB_UPGRADE* job = dynamic_cast<JOB_PCB_UPGRADE*>( aJob );
2533
2534 if( job == nullptr )
2536
2537 bool shouldSave = job->m_force;
2538
2539 try
2540 {
2542 BOARD* brd = getBoard( job->m_filename );
2544 shouldSave = true;
2545
2546 if( shouldSave )
2547 {
2548 pi->SaveBoard( brd->GetFileName(), brd );
2549 m_reporter->Report( _( "Successfully saved board file using the latest format\n" ), RPT_SEVERITY_INFO );
2550 }
2551 else
2552 {
2553 m_reporter->Report( _( "Board file was not updated\n" ), RPT_SEVERITY_ERROR );
2554 }
2555 }
2556 catch( const IO_ERROR& ioe )
2557 {
2558 wxString msg =
2559 wxString::Format( _( "Error saving board file '%s'.\n%s" ), job->m_filename, ioe.What().GetData() );
2560 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2562 }
2563
2565}
2566
2568{
2570 &aBrd->GetPageSettings(),
2571 aBrd->GetProject(),
2572 &aBrd->GetTitleBlock(),
2573 &aBrd->GetProperties() );
2574
2575 drawingSheet->SetSheetName( std::string() );
2576 drawingSheet->SetSheetPath( std::string() );
2577 drawingSheet->SetIsFirstPage( true );
2578
2579 drawingSheet->SetFileName( TO_UTF8( aBrd->GetFileName() ) );
2580
2581 return drawingSheet;
2582}
2583
2584
2585void PCBNEW_JOBS_HANDLER::loadOverrideDrawingSheet( BOARD* aBrd, const wxString& aSheetPath )
2586{
2587 // dont bother attempting to load a empty path, if there was one
2588 if( aSheetPath.IsEmpty() )
2589 return;
2590
2591 auto loadSheet =
2592 [&]( const wxString& path ) -> bool
2593 {
2596 resolver.SetProject( aBrd->GetProject() );
2597 resolver.SetProgramBase( &Pgm() );
2598
2599 wxString filename = resolver.ResolvePath( BASE_SCREEN::m_DrawingSheetFileName,
2600 aBrd->GetProject()->GetProjectPath(),
2601 { aBrd->GetEmbeddedFiles() } );
2602 wxString msg;
2603
2604 if( !DS_DATA_MODEL::GetTheInstance().LoadDrawingSheet( filename, &msg ) )
2605 {
2606 m_reporter->Report( wxString::Format( _( "Error loading drawing sheet '%s'." ),
2607 path )
2608 + wxS( "\n" ) + msg + wxS( "\n" ),
2610 return false;
2611 }
2612
2613 return true;
2614 };
2615
2616 if( loadSheet( aSheetPath ) )
2617 return;
2618
2619 // failed loading custom path, revert back to default
2620 loadSheet( aBrd->GetProject()->GetProjectFile().m_BoardDrawingSheetFile );
2621}
@ VIEW3D_BOTTOM
Definition 3d_enums.h:81
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
#define RANGE_SCALE_3D
This defines the range that all coord will have to be rendered.
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
PROJECTION_TYPE
Definition camera.h:40
static wxString m_DrawingSheetFileName
the name of the drawing sheet file, or empty to use the default drawing sheet
Definition base_screen.h:85
Helper class to handle information needed to display 3D board.
double BiuTo3dUnits() const noexcept
Board integer units To 3D units.
void SetBoard(BOARD *aBoard) noexcept
Set current board to be rendered.
EDA_3D_VIEWER_SETTINGS * m_Cfg
std::map< int, COLOR4D > m_ColorOverrides
allows to override color scheme colors
void Set3dCacheManager(S3D_CACHE *aCacheMgr) noexcept
Update the cache manager pointer.
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
const VECTOR2I & GetAuxOrigin() const
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:317
const PAGE_INFO & GetPageSettings() const
Definition board.h:753
void RecordDRCExclusions()
Scan existing markers and record data from any that are Excluded.
Definition board.cpp:331
TITLE_BLOCK & GetTitleBlock()
Definition board.h:759
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition board.cpp:2016
const std::map< wxString, wxString > & GetProperties() const
Definition board.h:395
const wxString & GetFileName() const
Definition board.h:354
std::vector< PCB_MARKER * > ResolveDRCExclusions(bool aCreateMarkers)
Rebuild DRC markers from the serialized data in BOARD_DESIGN_SETTINGS.
Definition board.cpp:388
int GetFileFormatVersionAtLoad() const
Definition board.h:426
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition board.h:756
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:691
PROJECT * GetProject() const
Definition board.h:549
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1040
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:923
void SynchronizeProperties()
Copy the current project's text variables into the boards property cache.
Definition board.cpp:2400
void DeleteMARKERs()
Delete all MARKERS from the board.
Definition board.cpp:1686
constexpr const Vec GetCenter() const
Definition box2.h:230
void SetProjection(PROJECTION_TYPE aProjection)
Definition camera.h:206
void RotateY_T1(float aAngleInRadians)
Definition camera.cpp:686
bool Zoom_T1(float aFactor)
Definition camera.cpp:629
bool SetCurWindowSize(const wxSize &aSize)
Update the windows size of the camera.
Definition camera.cpp:571
bool ViewCommand_T1(VIEW3D_TYPE aRequestedView)
Definition camera.cpp:110
void RotateX_T1(float aAngleInRadians)
Definition camera.cpp:680
void SetLookAtPos_T1(const SFVEC3F &aLookAtPos)
Definition camera.h:162
const SFVEC3F & GetLookAtPos_T1() const
Definition camera.h:167
void RotateZ_T1(float aAngleInRadians)
Definition camera.cpp:692
bool ParametersChanged()
Definition camera.cpp:730
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:78
static void GenerateODBPPFiles(const JOB_EXPORT_PCB_ODB &aJob, BOARD *aBoard, PCB_EDIT_FRAME *aParentFrame=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr, REPORTER *aErrorReporter=nullptr)
The dialog to create footprint position files and choose options (one or 2 files, units and force all...
A dialog to set the plot options and create plot files in various formats.
Definition dialog_plot.h:41
int ShowModal() override
bool WriteJsonReport(const wxString &aFullFileName)
bool WriteTextReport(const wxString &aFullFileName)
Helper to handle drill precision format in excellon files.
static DS_DATA_MODEL & GetTheInstance()
Return the instance of DS_DATA_MODEL used in the application.
void SetSheetPath(const std::string &aSheetPath)
Set the sheet path displayed in the title block.
void SetSheetName(const std::string &aSheetName)
Set the sheet name displayed in the title block.
void SetIsFirstPage(bool aIsFirstPage)
Change if this is first page.
void SetFileName(const std::string &aFileName)
Set the file name displayed in the title block.
std::vector< LAYER_PRESET_3D > m_LayerPresets
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:142
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.h:113
Create Excellon drill, drill map, and drill report files.
void SetFormat(bool aMetric, ZEROS_FMT aZerosFmt=DECIMAL_FORMAT, int aLeftDigits=0, int aRightDigits=0)
Initialize internal parameters to match the given format.
bool CreateDrillandMapFilesSet(const wxString &aPlotDirectory, bool aGenDrill, bool aGenMap, REPORTER *aReporter=nullptr)
Create the full set of Excellon drill file for the board.
void SetOptions(bool aMirror, bool aMinimalHeader, const VECTOR2I &aOffset, bool aMerge_PTH_NPTH)
Initialize internal parameters to match drill options.
void SetRouteModeForOvalHoles(bool aUseRouteModeForOvalHoles)
wxString m_outputFile
Wrapper to expose an API for writing VRML files, without exposing all the many structures used in the...
Definition export_vrml.h:33
bool ExportVRML_File(PROJECT *aProject, wxString *aMessages, const wxString &aFullFileName, double aMMtoWRMLunit, bool aIncludeUnspecified, bool aIncludeDNP, bool aExport3DFiles, bool aUseRelativePaths, const wxString &a3D_Subdir, double aXRef, double aYRef)
Exports the board and its footprint shapes 3D (vrml files only) as a vrml file.
Provide an extensible class to resolve 3D model paths.
void SetPosition(const VECTOR2I &aPos) override
void SetLink(const KIID &aLink)
Definition footprint.h:887
void SetOrientation(const EDA_ANGLE &aNewAngle)
EDA_ITEM * Clone() const override
Invoke a function on all children.
std::deque< PAD * > & Pads()
Definition footprint.h:224
const LIB_ID & GetFPID() const
Definition footprint.h:269
void SetPath(const wxString &aPath)
void Save(FOOTPRINT *aFootprintFilter=nullptr)
Save the footprint cache or a single footprint from it to disk.
boost::ptr_map< wxString, FP_CACHE_ENTRY > & GetFootprints()
Export board to GenCAD file format.
void UseIndividualShapes(bool aUnique)
Make pad shapes unique.
void UsePinNamesUnique(bool aUnique)
Make pin names unique.
void StoreOriginCoordsInFile(bool aStore)
Store origin coordinate in GenCAD file.
void FlipBottomPads(bool aFlip)
Flip pad shapes on the bottom side.
void SetPlotOffet(VECTOR2I aOffset)
Set the coordinates offset when exporting items.
bool WriteFile(const wxString &aFullFileName)
Export a GenCAD file.
void SetMapFileFormat(PLOT_FORMAT aMapFmt)
Initialize the format for the drill map file.
GERBER_JOBFILE_WRITER is a class used to create Gerber job file a Gerber job file stores info to make...
bool CreateJobFile(const wxString &aFullFilename)
Creates a Gerber job file.
void AddGbrFile(PCB_LAYER_ID aLayer, wxString &aFilename)
add a gerber file name and type in job file list
virtual bool EndPlot() override
Used to create Gerber drill files.
bool CreateDrillandMapFilesSet(const wxString &aPlotDirectory, bool aGenDrill, bool aGenMap, bool aGenTenting, REPORTER *aReporter=nullptr)
Create the full set of Excellon drill file for the board filenames are computed from the board name,...
void SetOptions(const VECTOR2I &aOffset)
Initialize internal parameters to match drill options.
void SetFormat(int aRightDigits=6)
Initialize internal parameters to match the given format.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
Wrapper to expose an API for writing IPC-D356 files.
Definition export_d356.h:54
bool Write(const wxString &aFilename)
Generates and writes the netlist to a given path.
void Register(const std::string &aJobTypeName, std::function< int(JOB *job)> aHandler, std::function< bool(JOB *job, wxWindow *aParent)> aConfigHandler)
JOB_DISPATCHER(KIWAY *aKiway)
PROGRESS_REPORTER * m_progressReporter
REPORTER * m_reporter
JOB_EXPORT_PCB_3D::FORMAT m_format
EXPORTER_STEP_PARAMS m_3dparams
Despite the name; also used for other formats.
wxString GetSettingsDialogTitle() const override
ODB_COMPRESSION m_compressionMode
@ ALL_LAYERS_ONE_FILE
DEPRECATED MODE.
GEN_MODE m_pdfGenMode
The background color specified in a hex string.
LSEQ m_plotOnAllLayersSequence
Used by SVG & PDF.
std::optional< wxString > m_argLayers
std::optional< wxString > m_argCommonLayers
LSEQ m_plotLayerSequence
Layers to include on all individual layer prints.
wxString m_libraryPath
wxString m_outputLibraryPath
bool m_saveBoard
Definition job_pcb_drc.h:36
bool m_reportAllTrackErrors
Definition job_pcb_drc.h:32
bool m_refillZones
Definition job_pcb_drc.h:35
VECTOR3D m_lightBottomIntensity
VECTOR3D m_lightTopIntensity
VECTOR3D m_lightCameraIntensity
VECTOR3D m_rotation
wxString m_filename
bool m_useBoardStackupColors
VECTOR3D m_lightSideIntensity
std::string m_appearancePreset
bool m_exitCodeViolations
Definition job_rc.h:52
int m_severity
Definition job_rc.h:49
UNITS m_units
Definition job_rc.h:48
OUTPUT_FORMAT m_format
Definition job_rc.h:50
wxString m_filename
Definition job_rc.h:47
An simple container class that lets us dispatch output jobs to kifaces.
Definition job.h:183
void AddOutput(wxString aOutputPath)
Definition job.h:215
wxString GetFullOutputPath(PROJECT *aProject) const
Returns the full output path for the job, taking into account the configured output path,...
Definition job.cpp:100
wxString GetConfiguredOutputPath() const
Returns the configured output path for the job.
Definition job.h:232
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition job.h:203
void SetWorkingOutputPath(const wxString &aPath)
Sets a transient output path for the job, it takes priority over the configured output path when GetF...
Definition job.h:238
const std::map< wxString, wxString > & GetVarOverrides() const
Definition job.h:196
Read the new s-expression based KiCad netlist format.
virtual void LoadNetlist() override
Load the contents of the netlist file into aNetlist.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:104
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:286
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition kiway.cpp:395
@ FACE_SCH
eeschema DSO
Definition kiway.h:293
const UTF8 & GetLibItemName() const
Definition lib_id.h:102
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
LSEQ UIOrder() const
Return the copper, technical and user layers in the order shown in layer widget.
Definition lset.cpp:726
LSEQ SeqStackupForPlotting() const
Return the sequence that is typical for a bottom-to-top stack-up.
Definition lset.cpp:387
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition lset.cpp:610
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:582
static const LSET & AllLayersMask()
Definition lset.cpp:624
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
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:188
@ MARKER_DRAWING_SHEET
Definition marker_base.h:56
Definition pad.h:54
static bool EnsurePathExists(const wxString &aPath, bool aPathToFile=false)
Attempts to create a given path if it does not exist.
Definition paths.cpp:477
DS_PROXY_VIEW_ITEM * getDrawingSheetProxyView(BOARD *aBrd)
void loadOverrideDrawingSheet(BOARD *brd, const wxString &aSheetPath)
PCBNEW_JOBS_HANDLER(KIWAY *aKiway)
TOOL_MANAGER * getToolManager(BOARD *aBrd)
BOARD * getBoard(const wxString &aPath=wxEmptyString)
std::unique_ptr< TOOL_MANAGER > m_toolManager
LSEQ convertLayerArg(wxString &aLayerString, BOARD *aBoard) const
int doFpExportSvg(JOB_FP_EXPORT_SVG *aSvgJob, const FOOTPRINT *aFootprint)
BOARD * GetBoard() const
The main frame for Pcbnew.
A #PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
static PCB_IO * PluginFind(PCB_FILE_T aFileType)
Return a #PLUGIN which the caller can use to import, export, save, or load design documents.
PCB_FILE_T
The set of file types that the PCB_IO_MGR knows about, and for which there has been a plugin written,...
Definition pcb_io_mgr.h:56
@ KICAD_SEXP
S-expression Pcbnew file format.
Definition pcb_io_mgr.h:58
static bool ConvertLibrary(std::map< std::string, UTF8 > *aOldFileProps, const wxString &aOldFilePath, const wxString &aNewFilePath, REPORTER *aReporter)
Convert a schematic symbol library to the latest KiCad format.
static PCB_FILE_T GuessPluginTypeFromLibPath(const wxString &aLibPath, int aCtl=0)
Return a plugin type given a footprint library's libPath.
void SetShapes(const std::vector< PCB_SHAPE > &aShapes)
Definition pcb_marker.h:154
bool Plot(const wxString &aOutputPath, const LSEQ &aLayersToPlot, const LSEQ &aCommonLayers, bool aUseGerberFileExtensions, bool aOutputPathIsSingle=false, std::optional< wxString > aLayerName=std::nullopt, std::optional< wxString > aSheetName=std::nullopt, std::optional< wxString > aSheetPath=std::nullopt)
static void PlotJobToPlotOpts(PCB_PLOT_PARAMS &aOpts, JOB_EXPORT_PCB_PLOT *aJob, REPORTER &aReporter)
Translate a JOB to PCB_PLOT_PARAMS.
Parameters and options when plotting/printing a board.
LSEQ GetPlotOnAllLayersSequence() const
void SetSkipPlotNPTH_Pads(bool aSkip)
void SetLayerSelection(const LSET &aSelection)
void SetPlotOnAllLayersSequence(LSEQ aSeq)
void SetPlotFrameRef(bool aFlag)
void SetPlotPadNumbers(bool aFlag)
LSET GetLayerSelection() const
void SetMirror(bool aFlag)
bool GetSketchPadsOnFabLayers() const
bool m_PDFSingle
Generate a single PDF file for all layers.
void SetSvgFitPageToBoard(int aSvgFitPageToBoard)
bool GetUseGerberProtelExtensions() const
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:125
Used to create Gerber drill files.
const wxString GetPlaceFileName(const wxString &aFullBaseFilename, PCB_LAYER_ID aLayer) const
int CreatePlaceFile(const wxString &aFullFilename, PCB_LAYER_ID aLayer, bool aIncludeBrdEdges, bool aExcludeDNP)
Create an pnp gerber file.
The ASCII format of the kicad place file is:
static wxString DecorateFilename(const wxString &aBaseName, bool aFront, bool aBack)
std::string GenPositionData()
build a string filled with the position data
Base plotter engine class.
Definition plotter.h:121
virtual bool EndPlot()=0
wxString m_BoardDrawingSheetFile
PcbNew params.
static S3D_CACHE * Get3DCacheManager(PROJECT *aProject, bool updateProjDir=false)
Return a pointer to an instance of the 3D cache manager.
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition project.cpp:156
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:162
virtual void ApplyTextVars(const std::map< wxString, wxString > &aVarsMap)
Applies the given var map, it will create or update existing vars.
Definition project.cpp:117
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:204
bool Redraw(bool aIsMoving, REPORTER *aStatusReporter, REPORTER *aWarningReporter) override
Redraw the view.
void SetCurWindowSize(const wxSize &aSize) override
Before each render, the canvas will tell the render what is the size of its windows,...
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
Is a LINE_READER that reads from a multiline 8 bit wide std::string.
Definition richio.h:253
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Master controller class:
TOOL_BASE * FindTool(int aId) const
Search for a tool with given ID.
void RegisterTool(TOOL_BASE *aTool)
Add a tool to the manager set and sets it up.
void SetEnvironment(EDA_ITEM *aModel, KIGFX::VIEW *aView, KIGFX::VIEW_CONTROLS *aViewControls, APP_SETTINGS_BASE *aSettings, TOOLS_HOLDER *aFrame)
Set the work environment (model, view, view controls and the parent window).
void Pan_T1(const SFVEC3F &aDeltaOffsetInc) override
void SetT0_and_T1_current_T() override
This will set T0 and T1 with the current values.
void Interpolate(float t) override
It will update the matrix to interpolate between T0 and T1 values.
wxString wx_str() const
Definition utf8.cpp:45
Handle actions specific to filling copper zones.
wxString GetDefaultPlotExtension(PLOT_FORMAT aFormat)
Return the default plot extension for a format.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:194
This file is part of the common library.
static DRILL_PRECISION precisionListForInches(2, 4)
static DRILL_PRECISION precisionListForMetric(3, 3)
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
#define IS_NEW
New item, just created.
EDA_UNITS
Definition eda_units.h:48
static FILENAME_RESOLVER * resolver
@ FRAME_PCB_EDITOR
Definition frame_type.h:42
@ FRAME_SCH
Definition frame_type.h:34
Classes used in drill files, map files and report files generation.
Classes used in drill files, map files and report files generation.
Classes used to generate a Gerber job file in JSON.
Classes used in place file generation.
static const std::string LegacySchematicFileExtension
static const std::string BrepFileExtension
static const std::string JpegFileExtension
static const std::string GerberJobFileExtension
static const std::string GerberFileExtension
static const std::string XaoFileExtension
static const std::string ReportFileExtension
static const std::string GltfBinaryFileExtension
static const std::string PngFileExtension
static const std::string FootprintPlaceFileExtension
static const std::string JsonFileExtension
static const std::string KiCadSchematicFileExtension
static const std::string CsvFileExtension
static const std::string U3DFileExtension
static const std::string PdfFileExtension
static const std::string Ipc2581FileExtension
static const std::string GencadFileExtension
static const std::string StlFileExtension
static const std::string IpcD356FileExtension
static const std::string PlyFileExtension
static const std::string StepFileExtension
static const std::string SVGFileExtension
static const std::string VrmlFileExtension
static const std::string ArchiveFileExtension
static const std::string KiCadPcbFileExtension
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
@ KIFACE_NETLIST_SCHEMATIC
Definition kiface_ids.h:56
KIID niluuid(0)
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
Definition layer_id.cpp:31
@ LAYER_3D_BACKGROUND_TOP
Definition layer_ids.h:550
@ LAYER_3D_BACKGROUND_BOTTOM
Definition layer_ids.h:549
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_CrtYd
Definition layer_ids.h:116
@ B_Adhes
Definition layer_ids.h:103
@ F_Paste
Definition layer_ids.h:104
@ F_Adhes
Definition layer_ids.h:102
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ F_Mask
Definition layer_ids.h:97
@ B_Paste
Definition layer_ids.h:105
@ F_Fab
Definition layer_ids.h:119
@ F_SilkS
Definition layer_ids.h:100
@ B_CrtYd
Definition layer_ids.h:115
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ B_SilkS
Definition layer_ids.h:101
@ F_Cu
Definition layer_ids.h:64
@ B_Fab
Definition layer_ids.h:118
This file contains miscellaneous commonly used macros and functions.
@ MAIL_SCH_GET_NETLIST
Definition mail_type.h:49
static const int ERR_ARGS
Definition exit_codes.h:31
static const int OK
Definition exit_codes.h:30
static const int ERR_RC_VIOLATIONS
Rules check violation count was greater than 0.
Definition exit_codes.h:37
static const int ERR_INVALID_INPUT_FILE
Definition exit_codes.h:33
static const int SUCCESS
Definition exit_codes.h:29
static const int ERR_INVALID_OUTPUT_CONFLICT
Definition exit_codes.h:34
static const int ERR_UNKNOWN
Definition exit_codes.h:32
#define SEXPR_BOARD_FILE_VERSION
Current s-expression file format version. 2 was the last legacy format version.
#define CTL_FOR_LIBRARY
Format output for a footprint library instead of clipboard or BOARD.
static DRILL_PRECISION precisionListForInches(2, 4)
static DRILL_PRECISION precisionListForMetric(3, 3)
SETTINGS_MANAGER * GetSettingsManager()
BOARD * CreateEmptyBoard()
Construct a default BOARD with a temporary (no filename) project.
bool SaveBoard(wxString &aFileName, BOARD *aBoard, PCB_IO_MGR::PCB_FILE_T aFormat, bool aSkipSettings)
BOARD * LoadBoard(const wxString &aFileName, bool aSetActive)
Loads a board from file This function identifies the file type by extension and determines the correc...
const wxString GetGerberProtelExtension(int aLayer)
Definition pcbplot.cpp:43
void BuildPlotFileName(wxFileName *aFilename, const wxString &aOutputDir, const wxString &aSuffix, const wxString &aExtension)
Complete a plot filename.
Definition pcbplot.cpp:379
PLOTTER * StartPlotBoard(BOARD *aBoard, const PCB_PLOT_PARAMS *aPlotOpts, int aLayer, const wxString &aLayerName, const wxString &aFullFileName, const wxString &aSheetName, const wxString &aSheetPath, const wxString &aPageName=wxT("1"), const wxString &aPageNumber=wxEmptyString, const int aPageCount=1)
Open a new plotfile using the options (and especially the format) specified in the options and prepar...
void PlotBoardLayers(BOARD *aBoard, PLOTTER *aPlotter, const LSEQ &aLayerSequence, const PCB_PLOT_PARAMS &aPlotOptions)
Plot a sequence of board layer IDs.
PGM_BASE & Pgm()
The global program "get" accessor.
Definition pgm_base.cpp:913
see class PGM_BASE
PLOT_FORMAT
The set of supported output plot formats.
Definition plotter.h:64
Plotting engines similar to ps (PostScript, Gerber, svg)
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
#define SKIP_SET_DIRTY
Definition sch_commit.h:42
#define SKIP_UNDO
Definition sch_commit.h:40
T * GetAppSettings(const char *aFilename)
const int scale
MODEL3D_FORMAT_TYPE fileType(const char *aFileName)
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
std::vector< KIGFX::COLOR4D > raytrace_lightColor
Implement a participant in the KIWAY alchemy.
Definition kiway.h:153
Declaration for a track ball camera.
double DEG2RAD(double deg)
Definition trigo.h:166
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
Definition of file extensions used in Kicad.
glm::vec3 SFVEC3F
Definition xv3d_types.h:44
#define ZONE_FILLER_TOOL_NAME