KiCad PCB EDA Suite
Loading...
Searching...
No Matches
eda_3d_viewer_frame.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) 2015-2016 Mario Luzeiro <[email protected]>
5 * Copyright (C) 2023 CERN
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <wx/filename.h>
27#include <wx/msgdlg.h>
28#include <wx/string.h>
29#include <wx/wupdlock.h>
30#include <wx/clipbrd.h>
31#include <wx/filedlg.h>
32#include <wx/dialog.h>
33#include "eda_3d_viewer_frame.h"
38#include <3d_viewer_id.h>
42#include <board.h>
43#include <advanced_config.h>
44#include <bitmaps.h>
46#include <core/arraydim.h>
47#include <dpi_scaling_common.h>
48#include <pgm_base.h>
49#include <project.h>
53#include <tool/action_manager.h>
54#include <tool/common_control.h>
55#include <tool/tool_manager.h>
57#include <tool/action_toolbar.h>
58#include <widgets/wx_infobar.h>
61#include <project_pcb.h>
62#include <toolbars_3d.h>
63
65
73const wxChar* EDA_3D_VIEWER_FRAME::m_logTrace = wxT( "KI_TRACE_EDA_3D_VIEWER" );
74
75
76BEGIN_EVENT_TABLE( EDA_3D_VIEWER_FRAME, KIWAY_PLAYER )
77
79 EVT_SET_FOCUS( EDA_3D_VIEWER_FRAME::OnSetFocus )
80
82 EDA_3D_VIEWER_FRAME::Process_Special_Functions )
83
84 EVT_MENU( wxID_CLOSE, EDA_3D_VIEWER_FRAME::Exit3DFrame )
85 EVT_MENU( ID_DISABLE_RAY_TRACING, EDA_3D_VIEWER_FRAME::onDisableRayTracing )
86
87 EVT_CLOSE( EDA_3D_VIEWER_FRAME::OnCloseWindow )
88END_EVENT_TABLE()
89
90
92 const wxString& aTitle, long style ) :
93 KIWAY_PLAYER( aKiway, aParent, FRAME_PCB_DISPLAY3D, aTitle, wxDefaultPosition,
94 wxDefaultSize, style, QUALIFIED_VIEWER3D_FRAMENAME( aParent ), unityScale ),
95 m_canvas( nullptr ),
96 m_currentCamera( m_trackBallCamera ),
97 m_trackBallCamera( 2 * RANGE_SCALE_3D )
98{
99 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::EDA_3D_VIEWER_FRAME %s" ), aTitle );
100
101 m_disable_ray_tracing = false;
102 m_aboutTitle = _HKI( "KiCad 3D Viewer" );
103
104 // Give it an icon
105 wxIcon icon;
106 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_3d ) );
107 SetIcon( icon );
108
109 // Create the status line
110 static const int status_dims[5] = { -1, 170, 130, 130, 130 };
111
112 wxStatusBar *status_bar = CreateStatusBar( arrayDim( status_dims ) );
113 SetStatusWidths( arrayDim( status_dims ), status_dims );
114
116 EDA_3D_VIEWER_SETTINGS* cfg = GetAppSettings<EDA_3D_VIEWER_SETTINGS>( "3d_viewer" );
117
118 if( cfg )
119 aaMode = static_cast<ANTIALIASING_MODE>( cfg->m_Render.opengl_AA_mode );
120
121 m_canvas = new EDA_3D_CANVAS( this, OGL_ATT_LIST::GetAttributesList( aaMode, true ), m_boardAdapter,
122 m_currentCamera, PROJECT_PCB::Get3DCacheManager( &Prj() ) );
123
124 m_appearancePanel = new APPEARANCE_CONTROLS_3D( this, GetCanvas() );
125
126 LoadSettings( GetAppSettings<EDA_3D_VIEWER_SETTINGS>( "3d_viewer" ) );
127 loadCommonSettings();
128
129 m_appearancePanel->SetUserViewports( Prj().GetProjectFile().m_Viewports3D );
130
131 // Create the manager
132 m_toolManager = new TOOL_MANAGER;
133 m_toolManager->SetEnvironment( GetBoard(), nullptr, nullptr,
134 GetAppSettings<EDA_3D_VIEWER_SETTINGS>( "3d_viewer" ), this );
135
136 m_actions = new EDA_3D_ACTIONS();
137 m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager );
138 m_canvas->SetEventDispatcher( m_toolDispatcher );
139
140 // Register tools
141 m_toolManager->RegisterTool( new COMMON_CONTROL );
142 m_toolManager->RegisterTool( new EDA_3D_CONTROLLER );
143 m_toolManager->InitTools();
144
145 setupUIConditions();
146
147 if( EDA_3D_CONTROLLER* ctrlTool = GetToolManager()->GetTool<EDA_3D_CONTROLLER>() )
148 ctrlTool->SetRotationIncrement( cfg ? cfg->m_Camera.rotation_increment : 10.0 );
149
150 // Run the viewer control tool, it is supposed to be always active
151 m_toolManager->InvokeTool( "3DViewer.Control" );
152
153 ReCreateMenuBar();
154
155 m_toolbarSettings = Pgm().GetSettingsManager().GetToolbarSettings<EDA_3D_VIEWER_TOOLBAR_SETTINGS>( "3d_viewer-toolbars" );
156 configureToolbars();
157 RecreateToolbars();
158
159 m_infoBar = new WX_INFOBAR( this, &m_auimgr );
160
161 m_auimgr.SetManagedWindow( this );
162
163 m_auimgr.AddPane( m_tbTopMain, EDA_PANE().HToolbar().Name( wxS( "TopMainToolbar" ) )
164 .Top().Layer( 6 ) );
165 m_auimgr.AddPane( m_infoBar, EDA_PANE().InfoBar().Name( wxS( "InfoBar" ) )
166 .Top().Layer( 1 ) );
167 m_auimgr.AddPane( m_appearancePanel, EDA_PANE().Name( "LayersManager" )
168 .Right().Layer( 3 )
169 .Caption( _( "Appearance" ) ).PaneBorder( false )
170 .MinSize( 180, -1 ).BestSize( 190, -1 ) );
171 m_auimgr.AddPane( m_canvas, EDA_PANE().Canvas().Name( wxS( "DrawFrame" ) )
172 .Center() );
173
174 wxAuiPaneInfo& layersManager = m_auimgr.GetPane( "LayersManager" );
175
176 if( cfg && cfg->m_AuiPanels.right_panel_width > 0 )
177 SetAuiPaneSize( m_auimgr, layersManager, cfg->m_AuiPanels.right_panel_width, -1 );
178
179 if( cfg )
180 layersManager.Show( cfg->m_AuiPanels.show_layer_manager );
181
182 // Call Update() to fix all pane default sizes, especially the "InfoBar" pane before
183 // hiding it.
184 m_auimgr.Update();
185
186 // We don't want the infobar displayed right away
187 m_auimgr.GetPane( wxS( "InfoBar" ) ).Hide();
188 m_auimgr.Update();
189
190 m_canvas->SetInfoBar( m_infoBar );
191 m_canvas->SetStatusBar( status_bar );
192
193 try
194 {
195 m_spaceMouse = std::make_unique<NL_3D_VIEWER_PLUGIN>( m_canvas );
196 }
197 catch( const std::system_error& e )
198 {
199 wxLogTrace( wxT( "KI_TRACE_NAVLIB" ), e.what() );
200 }
201
202 // Fixes bug in Windows (XP and possibly others) where the canvas requires the focus
203 // in order to receive mouse events. Otherwise, the user has to click somewhere on
204 // the canvas before it will respond to mouse wheel events.
205 m_canvas->SetFocus();
206}
207
208
210{
212
213 m_canvas->SetEventDispatcher( nullptr );
214
215 m_auimgr.UnInit();
216}
217
218
220{
222
225
226// Helper to define check conditions
227#define GridSizeCheck( x ) ACTION_CONDITIONS().Check( cond.GridSize( x ) )
228
229 auto raytracing =
230 [this]( const SELECTION& aSel )
231 {
232 return m_boardAdapter.m_Cfg->m_Render.engine != RENDER_ENGINE::OPENGL;
233 };
234 auto showTH =
235 [this]( const SELECTION& aSel )
236 {
238 };
239 auto showSMD =
240 [this]( const SELECTION& aSel )
241 {
243 };
244 auto showVirtual =
245 [this]( const SELECTION& aSel )
246 {
248 };
249 auto show_NotInPosfile =
250 [this]( const SELECTION& aSel )
251 {
253 };
254 auto show_DNP =
255 [this]( const SELECTION& aSel )
256 {
258 };
259 auto showBBoxes =
260 [this]( const SELECTION& aSel )
261 {
263 };
264 auto showNavig = [this]( const SELECTION& aSel )
265 {
267 };
268 auto ortho =
269 [this]( const SELECTION& )
270 {
271 return m_currentCamera.GetProjection() == PROJECTION_TYPE::ORTHO;
272 };
273
274 auto appearances =
275 [this]( const SELECTION& aSel )
276 {
278 };
279
281
286 ACTION_CONDITIONS().Check( show_NotInPosfile ) );
288
291
292 mgr->SetConditions( EDA_3D_ACTIONS::noGrid, GridSizeCheck( GRID3D_TYPE::NONE ) );
293 mgr->SetConditions( EDA_3D_ACTIONS::show10mmGrid, GridSizeCheck( GRID3D_TYPE::GRID_10MM ) );
294 mgr->SetConditions( EDA_3D_ACTIONS::show5mmGrid, GridSizeCheck( GRID3D_TYPE::GRID_5MM ) );
295 mgr->SetConditions( EDA_3D_ACTIONS::show2_5mmGrid, GridSizeCheck( GRID3D_TYPE::GRID_2P5MM ) );
296 mgr->SetConditions( EDA_3D_ACTIONS::show1mmGrid, GridSizeCheck( GRID3D_TYPE::GRID_1MM ) );
298
300 ACTION_CONDITIONS().Check( appearances ) );
301
302#undef GridSizeCheck
303}
304
305
306bool EDA_3D_VIEWER_FRAME::TryBefore( wxEvent& aEvent )
307{
308 static bool s_presetSwitcherShown = false;
309 static bool s_viewportSwitcherShown = false;
310
311 // wxWidgets generates no key events for the tab key when the ctrl key is held down. One
312 // way around this is to look at all events and inspect the keyboard state of the tab key.
313 // However, this runs into issues on some linux VMs where querying the keyboard state is
314 // very slow. Fortunately we only use ctrl-tab on Mac, so we implement this lovely hack:
315#ifdef __WXMAC__
316 if( wxGetKeyState( WXK_TAB ) )
317#else
318 if( ( aEvent.GetEventType() == wxEVT_CHAR || aEvent.GetEventType() == wxEVT_CHAR_HOOK )
319 && static_cast<wxKeyEvent&>( aEvent ).GetKeyCode() == WXK_TAB )
320#endif
321 {
322 if( !s_presetSwitcherShown && wxGetKeyState( PRESET_SWITCH_KEY ) )
323 {
324 if( m_appearancePanel && this->IsActive() )
325 {
326 wxArrayString mru = m_appearancePanel->GetLayerPresetsMRU();
327
328 if( mru.size() > 0 )
329 {
330 for( wxString& str : mru )
331 {
332 if( str == FOLLOW_PCB )
333 str = _( "Follow PCB Editor" );
334 else if( str == FOLLOW_PLOT_SETTINGS )
335 str = _( "Follow PCB Plot Settings" );
336 }
337
338 EDA_VIEW_SWITCHER switcher( this, mru, PRESET_SWITCH_KEY );
339
340 s_presetSwitcherShown = true;
341 switcher.ShowModal();
342 s_presetSwitcherShown = false;
343
344 int idx = switcher.GetSelection();
345
346 if( idx >= 0 && idx < (int) mru.size() )
347 {
348 wxString internalName = m_appearancePanel->GetLayerPresetsMRU()[idx];
349 m_appearancePanel->ApplyLayerPreset( internalName );
350 }
351
352 return true;
353 }
354 }
355 }
356 else if( !s_viewportSwitcherShown && wxGetKeyState( VIEWPORT_SWITCH_KEY ) )
357 {
358 if( this->IsActive() )
359 {
360 const wxArrayString& viewportMRU = m_appearancePanel->GetViewportsMRU();
361
362 if( viewportMRU.size() > 0 )
363 {
364 EDA_VIEW_SWITCHER switcher( this, viewportMRU, VIEWPORT_SWITCH_KEY );
365
366 s_viewportSwitcherShown = true;
367 switcher.ShowModal();
368 s_viewportSwitcherShown = false;
369
370 int idx = switcher.GetSelection();
371
372 if( idx >= 0 && idx < (int) viewportMRU.size() )
373 m_appearancePanel->ApplyViewport( viewportMRU[idx] );
374
375 return true;
376 }
377 }
378 }
379 }
380
381 return wxFrame::TryBefore( aEvent );
382}
383
384
385void EDA_3D_VIEWER_FRAME::handleIconizeEvent( wxIconizeEvent& aEvent )
386{
388
389 if( m_spaceMouse && aEvent.IsIconized() )
390 m_spaceMouse->SetFocus( false );
391}
392
393
395{
396 // This will schedule a request to load later
397 // ReloadRequest also updates the board pointer so always call it first
398 if( m_canvas )
400
403}
404
405
406void EDA_3D_VIEWER_FRAME::NewDisplay( bool aForceImmediateRedraw )
407{
408 if( m_canvas )
410
411 // After the ReloadRequest call, the refresh often takes a bit of time,
412 // and it is made here only on request.
413 if( m_canvas && aForceImmediateRedraw )
414 m_canvas->Refresh();
415}
416
417
419{
420 // Only update in OpenGL for an interactive interaction
421 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
422 m_canvas->Request_refresh( true );
423}
424
425
427{
428 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
430 else
431 NewDisplay( true );
432}
433
434
435void EDA_3D_VIEWER_FRAME::Exit3DFrame( wxCommandEvent &event )
436{
437 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::Exit3DFrame" ) );
438
439 Close( true );
440}
441
442
443void EDA_3D_VIEWER_FRAME::OnCloseWindow( wxCloseEvent &event )
444{
445 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::OnCloseWindow" ) );
446
447 // Do not show the layer manager during closing to avoid flicker on some platforms (Windows)
448 // that generate useless redraw of items in the Layer Manager
449 if( m_auimgr.GetPane( wxS( "LayersManager" ) ).IsShown() )
450 m_auimgr.GetPane( wxS( "LayersManager" ) ).Show( false );
451
452 if( m_canvas )
453 m_canvas->Close();
454
455 Destroy();
456 event.Skip( true );
457}
458
459
461{
462 if( m_canvas == nullptr )
463 return;
464
465 switch( event.GetId() )
466 {
468 {
470
471 if( EDA_3D_VIEWER_SETTINGS* cfg = GetAppSettings<EDA_3D_VIEWER_SETTINGS>( "3d_viewer" ) )
472 cfg->ResetToDefaults();
473
474 LoadSettings( GetAppSettings<EDA_3D_VIEWER_SETTINGS>( "3d_viewer" ) );
475
476 // Tell canvas that we (may have) changed the render engine
478 NewDisplay( true );
479 return;
480 }
481
482 default:
483 wxFAIL_MSG( wxT( "Invalid event in EDA_3D_VIEWER_FRAME::Process_Special_Functions()" ) );
484 return;
485 }
486}
487
488
489void EDA_3D_VIEWER_FRAME::onDisableRayTracing( wxCommandEvent& aEvent )
490{
491 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::%s disabling ray tracing." ), __WXFUNCTION__ );
492
494 m_boardAdapter.m_Cfg->m_Render.engine = RENDER_ENGINE::OPENGL;
495}
496
497
498void EDA_3D_VIEWER_FRAME::OnActivate( wxActivateEvent &aEvent )
499{
500 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::OnActivate" ) );
501
502 if( aEvent.GetActive() && m_canvas )
503 {
504 // Reload data if 3D frame shows a board,
505 // because it can be changed since last frame activation
508
509 // Activates again the focus of the canvas so it will catch mouse and key events
510 m_canvas->SetFocus();
511 }
512
513 if( m_spaceMouse )
514 m_spaceMouse->SetFocus( aEvent.GetActive() );
515
516 aEvent.Skip(); // required under wxMAC
517}
518
519
520void EDA_3D_VIEWER_FRAME::OnSetFocus( wxFocusEvent& aEvent )
521{
522 // Activates again the focus of the canvas so it will catch mouse and key events
523 if( m_canvas )
524 m_canvas->SetFocus();
525
526 aEvent.Skip();
527}
528
529
531{
533
534 // Dynamic_cast here will fail on Mac when called from CvPCB.
535 EDA_3D_VIEWER_SETTINGS* cfg = static_cast<EDA_3D_VIEWER_SETTINGS*>( aCfg );
536
537 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::LoadSettings" ) );
538
539 if( cfg )
540 {
541 applySettings( cfg );
542
544
545 // When opening the 3D viewer, we use the OpenGL mode, never the ray tracing engine
546 // because the ray tracing is very time consuming, and can be seen as not working
547 // (freeze window) with large boards.
548 m_boardAdapter.m_Cfg->m_Render.engine = RENDER_ENGINE::OPENGL;
549
551 {
552 wxString legacyColorsPresetName = _( "legacy colors" );
553
554 cfg->m_UseStackupColors = false;
555
556 if( !cfg->FindPreset( legacyColorsPresetName ) )
557 {
558 cfg->m_LayerPresets.emplace_back( legacyColorsPresetName,
559 GetAdapter().GetDefaultVisibleLayers(),
560 GetAdapter().GetDefaultColors() );
561 }
562
564 }
565
566 m_boardAdapter.InitSettings( nullptr, nullptr );
567
570 }
571}
572
573
575{
576 EDA_BASE_FRAME::SaveSettings( GetAppSettings<EDA_3D_VIEWER_SETTINGS>( "3d_viewer" ) );
577
578 if( EDA_3D_VIEWER_SETTINGS* cfg = GetAppSettings<EDA_3D_VIEWER_SETTINGS>( "3d_viewer" ) )
579 {
580 cfg->m_AuiPanels.right_panel_width = m_appearancePanel->GetSize().x;
581
582 cfg->m_Camera.animation_enabled = m_canvas->GetAnimationEnabled();
583 cfg->m_Camera.moving_speed_multiplier = m_canvas->GetMovingSpeedMultiplier();
584 cfg->m_Camera.projection_mode = m_canvas->GetProjectionMode();
585
586 if( EDA_3D_CONTROLLER* ctrlTool = GetToolManager()->GetTool<EDA_3D_CONTROLLER>() )
587 cfg->m_Camera.rotation_increment = ctrlTool->GetRotationIncrement();
588 }
589}
590
591
593{
594 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::CommonSettingsChanged" ) );
595
596 // Regen menu bars, etc
598
600 applySettings( GetAppSettings<EDA_3D_VIEWER_SETTINGS>( "3d_viewer" ) );
601
603
604 NewDisplay( true );
605}
606
607
609{
611
612 SetTitle( _( "3D Viewer" ) );
614
616 {
617 wxAuiPaneInfo& lm_pane_info = m_auimgr.GetPane( m_appearancePanel );
618 lm_pane_info.Caption( _( "Appearance" ) );
619 }
620
621 SetStatusText( wxEmptyString, ACTIVITY );
622 SetStatusText( wxEmptyString, HOVERED_ITEM );
623}
624
625
627{
628 wxAuiPaneInfo& layersManager = m_auimgr.GetPane( "LayersManager" );
629
630 if( EDA_3D_VIEWER_SETTINGS* cfg = GetAppSettings<EDA_3D_VIEWER_SETTINGS>( "3d_viewer" ) )
631 {
632 // show auxiliary Vertical layers and visibility manager toolbar
633 cfg->m_AuiPanels.show_layer_manager = !cfg->m_AuiPanels.show_layer_manager;
634
635 layersManager.Show( cfg->m_AuiPanels.show_layer_manager );
636
637 if( cfg->m_AuiPanels.show_layer_manager )
638 {
639 SetAuiPaneSize( m_auimgr, layersManager, cfg->m_AuiPanels.right_panel_width, -1 );
640 }
641 else
642 {
643 cfg->m_AuiPanels.right_panel_width = m_appearancePanel->GetSize().x;
644 m_auimgr.Update();
645 }
646 }
647}
648
649
651{
654}
655
656
658{
659 wxString fullFileName;
660
661 if( aFormat != EDA_3D_VIEWER_EXPORT_FORMAT::CLIPBOARD )
662 {
663 if( !getExportFileName( aFormat, fullFileName ) )
664 return;
665 }
666
667 wxImage screenshotImage = captureCurrentViewScreenshot();
668
669 if( screenshotImage.IsOk() )
670 {
671 saveOrCopyImage( screenshotImage, aFormat, fullFileName );
672 }
673}
674
675
677{
678 // Ensure we have the latest 3D view (remember 3D view is buffered)
679 // Also ensure any highlighted item is not highlighted when creating screen shot
681 bool original_highlight = cfg.highlight_on_rollover;
682 cfg.highlight_on_rollover = false;
683
684 m_canvas->DoRePaint(); // init first buffer
685 m_canvas->DoRePaint(); // init second buffer
686
687 wxImage screenshotImage;
688
689 if( m_canvas )
690 {
691 // Build image from the 3D buffer
692 wxWindowUpdateLocker noUpdates( this );
693 m_canvas->GetScreenshot( screenshotImage );
694 }
695
696 // Restore highlight setting
697 cfg.highlight_on_rollover = original_highlight;
698
699 return screenshotImage;
700}
701
702
704{
705 wxString fullFileName;
706
707 if( aFormat != EDA_3D_VIEWER_EXPORT_FORMAT::CLIPBOARD )
708 {
709 if( !getExportFileName( aFormat, fullFileName ) )
710 return;
711 }
712
713 wxImage screenshotImage = captureScreenshot( aSize );
714
715 if( screenshotImage.IsOk() )
716 {
717 saveOrCopyImage( screenshotImage, aFormat, fullFileName );
718 }
719}
720
721
723{
724 // Create combined wildcard for both formats
725 const wxString wildcard = FILEEXT::JpegFileWildcard() + "|" + FILEEXT::PngFileWildcard();
726
729
730 // Set default extension based on current format
731 const wxString defaultExt = ( aFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG ) ?
733 m_defaultSaveScreenshotFileName.SetExt( defaultExt );
734
735 wxFileDialog dlg( this, _( "3D Image File Name" ),
737 m_defaultSaveScreenshotFileName.GetFullName(), wildcard,
738 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
739
740 // Set initial filter index based on current format
741 dlg.SetFilterIndex( ( aFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG ) ? 0 : 1 );
742
743 if( dlg.ShowModal() == wxID_CANCEL )
744 return false;
745
746 m_defaultSaveScreenshotFileName = dlg.GetPath();
747
748 // Determine format based on file extension first
749 wxString fileExt = m_defaultSaveScreenshotFileName.GetExt().Lower();
750 EDA_3D_VIEWER_EXPORT_FORMAT detectedFormat;
751 bool formatDetected = false;
752
753 if( fileExt == wxT("jpg") || fileExt == wxT("jpeg") )
754 {
755 detectedFormat = EDA_3D_VIEWER_EXPORT_FORMAT::JPEG;
756 formatDetected = true;
757 }
758 else if( fileExt == wxT("png") )
759 {
760 detectedFormat = EDA_3D_VIEWER_EXPORT_FORMAT::PNG;
761 formatDetected = true;
762 }
763
764 // If format can't be determined from extension, use dropdown selection
765 if( !formatDetected )
766 {
767 int filterIndex = dlg.GetFilterIndex();
768 detectedFormat = ( filterIndex == 0 ) ? EDA_3D_VIEWER_EXPORT_FORMAT::JPEG :
769 EDA_3D_VIEWER_EXPORT_FORMAT::PNG;
770
771 // Append appropriate extension
772 const wxString ext = ( detectedFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG ) ?
775 }
776
777 // Update the format parameter
778 aFormat = detectedFormat;
779
780 // Check directory permissions using the updated filename
781 wxFileName fn = m_defaultSaveScreenshotFileName;
782
783 if( !fn.IsDirWritable() )
784 {
785 wxString msg;
786 msg.Printf( _( "Insufficient permissions to save file '%s'." ),
787 m_defaultSaveScreenshotFileName.GetFullPath() );
788 wxMessageBox( msg, _( "Error" ), wxOK | wxICON_ERROR, this );
789 return false;
790 }
791
792 fullFileName = m_defaultSaveScreenshotFileName.GetFullPath();
793 return true;
794}
795
796
797wxImage EDA_3D_VIEWER_FRAME::captureScreenshot( const wxSize& aSize )
798{
800 camera.SetCurWindowSize( aSize );
801
802 EDA_3D_VIEWER_SETTINGS* cfg = GetAppSettings<EDA_3D_VIEWER_SETTINGS>( "3d_viewer" );
804
805 auto configRestorer = std::unique_ptr<void, std::function<void(void*)>>(
806 reinterpret_cast<void*>(1),
807 [&](void*) { m_boardAdapter.m_Cfg = backupCfg; }
808 );
809
810 if( cfg )
811 m_boardAdapter.m_Cfg = cfg;
812
813 if( cfg && cfg->m_Render.engine == RENDER_ENGINE::RAYTRACING )
814 return captureRaytracingScreenshot( m_boardAdapter, camera, aSize );
815 else
816 return captureOpenGLScreenshot( m_boardAdapter, camera, aSize );
817}
818
819
821{
822 EDA_3D_VIEWER_SETTINGS* cfg = GetAppSettings<EDA_3D_VIEWER_SETTINGS>( "3d_viewer" );
823
824 if( cfg )
825 aAdapter.m_Cfg = cfg;
826}
827
828
829wxImage EDA_3D_VIEWER_FRAME::captureRaytracingScreenshot( BOARD_ADAPTER& aAdapter, TRACK_BALL& aCamera, const wxSize& aSize )
830{
831 BOARD_ADAPTER tempadapter;
832 tempadapter.SetBoard( GetBoard() );
833 tempadapter.m_Cfg = aAdapter.m_Cfg;
834 tempadapter.InitSettings( nullptr, nullptr );
835 tempadapter.Set3dCacheManager( aAdapter.Get3dCacheManager() );
836
837 RENDER_3D_RAYTRACE_RAM raytrace( tempadapter, aCamera );
838 raytrace.SetCurWindowSize( aSize );
839
840 while( raytrace.Redraw( false, nullptr, nullptr ) );
841
842 uint8_t* rgbaBuffer = raytrace.GetBuffer();
843 wxSize realSize = raytrace.GetRealBufferSize();
844
845 if( !rgbaBuffer )
846 return wxImage();
847
848 return convertRGBAToImage( rgbaBuffer, realSize );
849}
850
851
852wxImage EDA_3D_VIEWER_FRAME::convertRGBAToImage( uint8_t* aRGBABuffer, const wxSize& aRealSize )
853{
854 const unsigned int wxh = aRealSize.x * aRealSize.y;
855
856 unsigned char* rgbBuffer = (unsigned char*) malloc( wxh * 3 );
857 unsigned char* alphaBuffer = (unsigned char*) malloc( wxh );
858
859 unsigned char* rgbaPtr = aRGBABuffer;
860 unsigned char* rgbPtr = rgbBuffer;
861 unsigned char* alphaPtr = alphaBuffer;
862
863 for( int y = 0; y < aRealSize.y; y++ )
864 {
865 for( int x = 0; x < aRealSize.x; x++ )
866 {
867 rgbPtr[0] = rgbaPtr[0];
868 rgbPtr[1] = rgbaPtr[1];
869 rgbPtr[2] = rgbaPtr[2];
870 alphaPtr[0] = rgbaPtr[3];
871
872 rgbaPtr += 4;
873 rgbPtr += 3;
874 alphaPtr += 1;
875 }
876 }
877
878 wxImage screenshotImage;
879 screenshotImage.Create( aRealSize );
880 screenshotImage.SetData( rgbBuffer );
881 screenshotImage.SetAlpha( alphaBuffer );
882 return screenshotImage.Mirror( false );
883}
884
885
886wxImage EDA_3D_VIEWER_FRAME::captureOpenGLScreenshot( BOARD_ADAPTER& aAdapter, TRACK_BALL& aCamera, const wxSize& aSize )
887{
888 EDA_3D_VIEWER_SETTINGS* cfg = aAdapter.m_Cfg;
889 ANTIALIASING_MODE aaMode = cfg ? static_cast<ANTIALIASING_MODE>( cfg->m_Render.opengl_AA_mode )
890 : ANTIALIASING_MODE::AA_NONE;
891
892 wxFrame temp( this, wxID_ANY, wxEmptyString, wxDefaultPosition, aSize, wxFRAME_NO_TASKBAR );
893 temp.Hide();
894 BOARD_ADAPTER tempadapter;
895 tempadapter.SetBoard( GetBoard() );
896 tempadapter.m_Cfg = aAdapter.m_Cfg;
897 tempadapter.InitSettings( nullptr, nullptr );
898 tempadapter.Set3dCacheManager( aAdapter.Get3dCacheManager() );
899
900 auto canvas = std::make_unique<EDA_3D_CANVAS>( &temp,
901 OGL_ATT_LIST::GetAttributesList( aaMode, true ),
902 tempadapter, aCamera,
903 aAdapter.Get3dCacheManager() );
904
905 canvas->SetSize( aSize );
906 configureCanvas( canvas, cfg );
907 wxWindowUpdateLocker noUpdates( this );
908
909 // Temporarily disable highlight during screenshot
911 bool original_highlight = renderCfg.highlight_on_rollover;
912 bool original_navigator = renderCfg.show_navigator;
913 renderCfg.show_navigator = false;
914 renderCfg.highlight_on_rollover = false;
915
916 std::vector<unsigned char> buffer(aSize.x * aSize.y * 4); // RGBA format
917 canvas->RenderToFrameBuffer( buffer.data(), aSize.x, aSize.y );
918 wxImage result = convertRGBAToImage( buffer.data(), aSize );
919
920 // Restore highlight setting
921 renderCfg.highlight_on_rollover = original_highlight;
922 renderCfg.show_navigator = original_navigator;
923
924 return result;
925}
926
927
928void EDA_3D_VIEWER_FRAME::configureCanvas( std::unique_ptr<EDA_3D_CANVAS>& aCanvas, EDA_3D_VIEWER_SETTINGS* aCfg )
929{
930 if( aCfg )
931 {
932 aCanvas->SetAnimationEnabled( aCfg->m_Camera.animation_enabled );
933 aCanvas->SetMovingSpeedMultiplier( aCfg->m_Camera.moving_speed_multiplier );
934 aCanvas->SetProjectionMode( aCfg->m_Camera.projection_mode );
935 }
936
937 aCanvas->SetVcSettings( EDA_DRAW_PANEL_GAL::GetVcSettings() );
938}
939
940
941void EDA_3D_VIEWER_FRAME::saveOrCopyImage( const wxImage& aScreenshotImage, EDA_3D_VIEWER_EXPORT_FORMAT aFormat, const wxString& aFullFileName )
942{
943 if( aFormat == EDA_3D_VIEWER_EXPORT_FORMAT::CLIPBOARD )
944 {
945 copyImageToClipboard( aScreenshotImage );
946 }
947 else
948 {
949 saveImageToFile( aScreenshotImage, aFormat, aFullFileName );
950 }
951}
952
953
954void EDA_3D_VIEWER_FRAME::copyImageToClipboard( const wxImage& aScreenshotImage )
955{
956 wxBitmap bitmap( aScreenshotImage );
957 wxLogNull doNotLog;
958
959 if( wxTheClipboard->Open() )
960 {
961 wxBitmapDataObject* dobjBmp = new wxBitmapDataObject( bitmap );
962
963 if( !wxTheClipboard->SetData( dobjBmp ) )
964 wxMessageBox( _( "Failed to copy image to clipboard" ) );
965
966 wxTheClipboard->Flush();
967 wxTheClipboard->Close();
968 }
969}
970
971
972void EDA_3D_VIEWER_FRAME::saveImageToFile( const wxImage& aScreenshotImage, EDA_3D_VIEWER_EXPORT_FORMAT aFormat, const wxString& aFullFileName )
973{
974 bool fmt_is_jpeg = ( aFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG );
975
976 if( !aScreenshotImage.SaveFile( aFullFileName, fmt_is_jpeg ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG ) )
977 {
978 wxMessageBox( _( "Can't save file" ) );
979 }
980}
981
982
984{
985 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::RenderEngineChanged()" ) );
986
987 if( m_canvas )
989}
990
991
993{
994 wxCHECK_RET( m_canvas, wxT( "Cannot load settings to null canvas" ) );
995
996 COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
997
998 // TODO(JE) use all control options
1000}
1001
1002
1004{
1005 m_boardAdapter.m_Cfg = cfg;
1006
1010
1012}
@ ID_DISABLE_RAY_TRACING
Definition: 3d_viewer_id.h:43
@ ID_START_COMMAND_3D
Definition: 3d_viewer_id.h:36
@ ID_MENU_COMMAND_END
Definition: 3d_viewer_id.h:41
@ ID_MENU3D_RESET_DEFAULTS
Definition: 3d_viewer_id.h:38
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
constexpr EDA_IU_SCALE unityScale
Definition: base_units.h:115
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:104
#define RANGE_SCALE_3D
This defines the range that all coord will have to be rendered.
Definition: board_adapter.h:66
Manage TOOL_ACTION objects.
void SetConditions(const TOOL_ACTION &aAction, const ACTION_CONDITIONS &aConditions)
Set the conditions the UI elements for activating a specific tool action should use for determining t...
std::vector< VIEWPORT3D > GetUserViewports() const
Return a list of viewports created by the user.
void ApplyViewport(const wxString &aPresetName)
const wxArrayString & GetViewportsMRU()
void ApplyLayerPreset(const wxString &aPresetName)
const wxArrayString & GetLayerPresetsMRU()
APP_SETTINGS_BASE is a settings class that should be derived for each standalone KiCad application.
Definition: app_settings.h:108
Helper class to handle information needed to display 3D board.
Definition: board_adapter.h:73
void InitSettings(REPORTER *aStatusReporter, REPORTER *aWarningReporter)
Function to be called by the render when it need to reload the settings for the board.
void SetBoard(BOARD *aBoard) noexcept
Set current board to be rendered.
void SetLayerColors(const std::map< int, COLOR4D > &aColors)
EDA_3D_VIEWER_SETTINGS * m_Cfg
bool m_MousewheelPanning
std::map< int, COLOR4D > GetDefaultColors() const
void Set3dCacheManager(S3D_CACHE *aCacheMgr) noexcept
Update the cache manager pointer.
Definition: board_adapter.h:84
S3D_CACHE * Get3dCacheManager() const noexcept
Definition: board_adapter.h:85
PROJECTION_TYPE GetProjection()
Definition: camera.h:205
bool SetCurWindowSize(const wxSize &aSize)
Update the windows size of the camera.
Definition: camera.cpp:571
Handle actions that are shared between different applications.
int ShowModal() override
EDA_3D_ACTIONS.
static TOOL_ACTION showNavigator
static TOOL_ACTION showLayersManager
static TOOL_ACTION showNotInPosFile
static TOOL_ACTION showTHT
static TOOL_ACTION noGrid
static TOOL_ACTION show2_5mmGrid
static TOOL_ACTION show1mmGrid
static TOOL_ACTION showDNP
static TOOL_ACTION toggleOrtho
static TOOL_ACTION show10mmGrid
static TOOL_ACTION toggleRaytacing
static TOOL_ACTION show5mmGrid
static TOOL_ACTION showSMD
static TOOL_ACTION showVirtual
static TOOL_ACTION showBBoxes
Implement a canvas based on a wxGLCanvas.
Definition: eda_3d_canvas.h:51
int GetMovingSpeedMultiplier() const
void SetProjectionMode(int aMode)
void SetAnimationEnabled(bool aEnable)
Enable or disable camera animation when switching to a pre-defined view.
int GetProjectionMode() const
void DoRePaint()
The actual function to repaint the canvas.
void SetEventDispatcher(TOOL_DISPATCHER *aEventDispatcher)
Set a dispatcher that processes events and forwards them to tools.
bool IsReloadRequestPending() const
Query if there is a pending reload request.
Definition: eda_3d_canvas.h:93
bool GetAnimationEnabled() const
void ReloadRequest(BOARD *aBoard=nullptr, S3D_CACHE *aCachePointer=nullptr)
void GetScreenshot(wxImage &aDstImage)
Request a screenshot and output it to the aDstImage.
void SetMovingSpeedMultiplier(int aMultiplier)
Set the camera animation moving speed multiplier option.
void RenderEngineChanged()
Notify that the render engine was changed.
void Request_refresh(bool aRedrawImmediately=true)
Schedule a refresh update of the canvas.
Handle view actions for various 3D canvases.
Create and handle a window for the 3d viewer connected to a Kiway and a pcbboard.
void saveOrCopyImage(const wxImage &screenshotImage, EDA_3D_VIEWER_EXPORT_FORMAT aFormat, const wxString &fullFileName)
Save image to file or copy to clipboard based on format.
void OnSetFocus(wxFocusEvent &event)
void onDisableRayTracing(wxCommandEvent &aEvent)
void CommonSettingsChanged(int aFlags) override
Notification that common settings are updated.
void applySettings(EDA_3D_VIEWER_SETTINGS *aSettings)
PCB_BASE_FRAME * Parent() const
void SaveSettings(APP_SETTINGS_BASE *aCfg) override
Save common frame parameters to a configuration data file.
void NewDisplay(bool aForceImmediateRedraw=false)
Reload and refresh (rebuild) the 3D scene.
void configureCanvas(std::unique_ptr< EDA_3D_CANVAS > &canvas, EDA_3D_VIEWER_SETTINGS *cfg)
Configure canvas settings for screenshot capture.
wxImage captureOpenGLScreenshot(BOARD_ADAPTER &adapter, TRACK_BALL &camera, const wxSize &aSize)
Capture screenshot using OpenGL renderer.
bool getExportFileName(EDA_3D_VIEWER_EXPORT_FORMAT &aFormat, wxString &fullFileName)
Get export filename through file dialog.
void setupUIConditions() override
Setup the UI conditions for the various actions and their controls in this frame.
void OnActivate(wxActivateEvent &event)
bool TryBefore(wxEvent &aEvent) override
EDA_3D_CANVAS * m_canvas
void Exit3DFrame(wxCommandEvent &event)
Called when user press the File->Exit.
BOARD_ADAPTER m_boardAdapter
std::unique_ptr< NL_3D_VIEWER_PLUGIN > m_spaceMouse
void RenderEngineChanged()
RenderEngineChanged - Update toolbar icon and call canvas RenderEngineChanged.
void Process_Special_Functions(wxCommandEvent &event)
wxImage captureCurrentViewScreenshot()
Capture screenshot of the current view using the configured renderer.
void OnCloseWindow(wxCloseEvent &event)
void ShowChangedLanguage() override
wxImage captureScreenshot(const wxSize &aSize)
Capture screenshot using appropriate rendering method.
void setupRenderingConfig(BOARD_ADAPTER &adapter)
Setup rendering configuration for screenshot capture.
void ReloadRequest()
Request reloading the 3D view.
wxFileName m_defaultSaveScreenshotFileName
wxImage convertRGBAToImage(uint8_t *rgbaBuffer, const wxSize &realSize)
Convert RGBA buffer to wxImage format.
BOARD_ADAPTER & GetAdapter()
void LoadSettings(APP_SETTINGS_BASE *aCfg) override
Load common frame parameters from a configuration file.
void saveImageToFile(const wxImage &screenshotImage, EDA_3D_VIEWER_EXPORT_FORMAT aFormat, const wxString &fullFileName)
Save image to file.
void copyImageToClipboard(const wxImage &screenshotImage)
Copy image to system clipboard.
void loadCommonSettings()
Load configuration from common settings.
void ExportImage(EDA_3D_VIEWER_EXPORT_FORMAT aFormat, const wxSize &aSize)
Export 3D viewer image to file or clipboard.
wxImage captureRaytracingScreenshot(BOARD_ADAPTER &adapter, TRACK_BALL &camera, const wxSize &aSize)
Capture screenshot using raytracing renderer.
void TakeScreenshot(EDA_3D_VIEWER_EXPORT_FORMAT aFormat)
Create a Screenshot of the current 3D view.
void handleIconizeEvent(wxIconizeEvent &aEvent) override
Handle a window iconize event.
LAYER_PRESET_3D * FindPreset(const wxString &aName)
std::vector< LAYER_PRESET_3D > m_LayerPresets
Toolbar configuration for the 3D viewer.
Definition: toolbars_3d.h:29
virtual void handleIconizeEvent(wxIconizeEvent &aEvent)
Handle a window iconize event.
APPEARANCE_CONTROLS_3D * m_appearancePanel
void CommonSettingsChanged(int aFlags) override
Notification event that some of the common (suite-wide) settings have changed.
void ShowChangedLanguage() override
Redraw the menus and what not in current language.
virtual void setupUIConditions()
Setup the UI conditions for the various actions and their controls in this frame.
wxAuiManager m_auimgr
virtual void RecreateToolbars()
virtual void LoadSettings(APP_SETTINGS_BASE *aCfg)
Load common frame parameters from a configuration file.
virtual void SaveSettings(APP_SETTINGS_BASE *aCfg)
Save common frame parameters to a configuration data file.
static KIGFX::VC_SETTINGS GetVcSettings()
Gets a populated View Controls settings object dervived from our program settings.
Specialization of the wxAuiPaneInfo class for KiCad panels.
int GetSelection() const
void SetVcSettings(const KIGFX::VC_SETTINGS &aVcSettings)
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
Definition: kiway_player.h:65
bool Destroy() override
Our version of Destroy() which is virtual from wxWidgets.
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition: kiway.h:286
static const wxGLAttributes GetAttributesList(ANTIALIASING_MODE aAntiAliasingMode, bool aAlpha=false)
Get a list of attributes to pass to wxGLCanvas.
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition: pgm_base.cpp:565
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition: pgm_base.h:125
std::vector< VIEWPORT3D > m_Viewports3D
List of stored viewports (pos + zoom)
Definition: project_file.h:205
static S3D_CACHE * Get3DCacheManager(PROJECT *aProject, bool updateProjDir=false)
Return a pointer to an instance of the 3D cache manager.
Definition: project_pcb.cpp:77
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition: project.cpp:143
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,...
T * GetToolbarSettings(const wxString &aFilename)
Return a handle to the given toolbar settings.
TOOL_MANAGER * m_toolManager
Definition: tools_holder.h:171
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:55
Master controller class:
Definition: tool_manager.h:62
ACTION_MANAGER * GetActionManager() const
Definition: tool_manager.h:306
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).
A modified version of the wxInfoBar class that allows us to:
Definition: wx_infobar.h:76
#define _HKI(x)
#define _(s)
#define GridSizeCheck(x)
EVT_TOOL_RANGE(ID_START_COMMAND_3D, ID_MENU_COMMAND_END, EDA_3D_VIEWER_FRAME::Process_Special_Functions) EDA_3D_VIEWER_FRAME
Declaration of the eda_3d_viewer class.
EDA_3D_VIEWER_EXPORT_FORMAT
@ ACTIVITY
@ HOVERED_ITEM
#define FOLLOW_PLOT_SETTINGS
#define LEGACY_PRESET_FLAG
#define FOLLOW_PCB
#define QUALIFIED_VIEWER3D_FRAMENAME(parent)
#define VIEWPORT_SWITCH_KEY
#define PRESET_SWITCH_KEY
@ FRAME_PCB_DISPLAY3D
Definition: frame_type.h:47
static const std::string JpegFileExtension
static const std::string PngFileExtension
static wxString PngFileWildcard()
static wxString JpegFileWildcard()
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
PROJECT & Prj()
Definition: kicad.cpp:608
Declaration of the NL_3D_VIEWER_PLUGIN class.
ANTIALIASING_MODE
Anti-aliasing options.
Definition: ogl_attr_list.h:37
BOARD * GetBoard()
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:902
see class PGM_BASE
Functors that can be used to figure out how the action controls should be displayed in the UI and if ...
ACTION_CONDITIONS & Check(const SELECTION_CONDITION &aCondition)
Definition of file extensions used in Kicad.
void SetAuiPaneSize(wxAuiManager &aManager, wxAuiPaneInfo &aPane, int aWidth, int aHeight)
Sets the size of an AUI pane, working around http://trac.wxwidgets.org/ticket/13180.