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 <kiplatform/ui.h>
62#include <project_pcb.h>
63#include <toolbars_3d.h>
64
65#if defined(__linux__) || defined(__FreeBSD__)
68#else
70#endif
71
79const wxChar* EDA_3D_VIEWER_FRAME::m_logTrace = wxT( "KI_TRACE_EDA_3D_VIEWER" );
80
81
82BEGIN_EVENT_TABLE( EDA_3D_VIEWER_FRAME, KIWAY_PLAYER )
83
85 EVT_SET_FOCUS( EDA_3D_VIEWER_FRAME::OnSetFocus )
86
88 EDA_3D_VIEWER_FRAME::Process_Special_Functions )
89
90 EVT_MENU( wxID_CLOSE, EDA_3D_VIEWER_FRAME::Exit3DFrame )
91 EVT_MENU( ID_DISABLE_RAY_TRACING, EDA_3D_VIEWER_FRAME::onDisableRayTracing )
92
93 EVT_CLOSE( EDA_3D_VIEWER_FRAME::OnCloseWindow )
94END_EVENT_TABLE()
95
96
98 const wxString& aTitle, long style ) :
99 KIWAY_PLAYER( aKiway, aParent, FRAME_PCB_DISPLAY3D, aTitle, wxDefaultPosition,
100 wxDefaultSize, style, QUALIFIED_VIEWER3D_FRAMENAME( aParent ), unityScale ),
101 m_canvas( nullptr ),
102 m_currentCamera( m_trackBallCamera ),
103 m_trackBallCamera( 2 * RANGE_SCALE_3D )
104{
105 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::EDA_3D_VIEWER_FRAME %s" ), aTitle );
106
107 m_disable_ray_tracing = false;
108 m_aboutTitle = _HKI( "KiCad 3D Viewer" );
109
110 // Give it an icon
111 wxIcon icon;
112 icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_3d ) );
113 SetIcon( icon );
114
115 // Create the status line
116 static const int status_dims[5] = { -1, 170, 130, 130, 130 };
117
118 wxStatusBar *status_bar = CreateStatusBar( arrayDim( status_dims ) );
119 SetStatusWidths( arrayDim( status_dims ), status_dims );
120
123
124 if( cfg )
125 aaMode = static_cast<ANTIALIASING_MODE>( cfg->m_Render.opengl_AA_mode );
126
127 m_canvas = new EDA_3D_CANVAS( this, OGL_ATT_LIST::GetAttributesList( aaMode, true ), m_boardAdapter,
128 m_currentCamera, PROJECT_PCB::Get3DCacheManager( &Prj() ) );
129
130 m_appearancePanel = new APPEARANCE_CONTROLS_3D( this, GetCanvas() );
131
132 LoadSettings( GetAppSettings<EDA_3D_VIEWER_SETTINGS>( "3d_viewer" ) );
133 loadCommonSettings();
134
135 m_appearancePanel->SetUserViewports( Prj().GetProjectFile().m_Viewports3D );
136
137 // Create the manager
138 m_toolManager = new TOOL_MANAGER;
139 m_toolManager->SetEnvironment( GetBoard(), nullptr, nullptr,
140 GetAppSettings<EDA_3D_VIEWER_SETTINGS>( "3d_viewer" ), this );
141
142 m_actions = new EDA_3D_ACTIONS();
143 m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager );
144 m_canvas->SetEventDispatcher( m_toolDispatcher );
145
146 // Register tools
147 m_toolManager->RegisterTool( new COMMON_CONTROL );
148 m_toolManager->RegisterTool( new EDA_3D_CONTROLLER );
149 m_toolManager->InitTools();
150
151 setupUIConditions();
152
153 if( EDA_3D_CONTROLLER* ctrlTool = GetToolManager()->GetTool<EDA_3D_CONTROLLER>() )
154 ctrlTool->SetRotationIncrement( cfg ? cfg->m_Camera.rotation_increment : 10.0 );
155
156 // Run the viewer control tool, it is supposed to be always active
157 m_toolManager->InvokeTool( "3DViewer.Control" );
158
159 ReCreateMenuBar();
160
161 m_toolbarSettings = Pgm().GetSettingsManager().GetToolbarSettings<EDA_3D_VIEWER_TOOLBAR_SETTINGS>( "3d_viewer-toolbars" );
162 configureToolbars();
163 RecreateToolbars();
164
165 m_infoBar = new WX_INFOBAR( this, &m_auimgr );
166
167 m_auimgr.SetManagedWindow( this );
168
169 m_auimgr.AddPane( m_tbTopMain, EDA_PANE().HToolbar().Name( wxS( "TopMainToolbar" ) )
170 .Top().Layer( 6 ) );
171 m_auimgr.AddPane( m_infoBar, EDA_PANE().InfoBar().Name( wxS( "InfoBar" ) )
172 .Top().Layer( 1 ) );
173 m_auimgr.AddPane( m_appearancePanel, EDA_PANE().Name( "LayersManager" )
174 .Right().Layer( 3 )
175 .Caption( _( "Appearance" ) ).PaneBorder( false )
176 .MinSize( FromDIP( 180 ), -1 ).BestSize( FromDIP( 190 ), -1 ) );
177 m_auimgr.AddPane( m_canvas, EDA_PANE().Canvas().Name( wxS( "DrawFrame" ) )
178 .Center() );
179
180 wxAuiPaneInfo& layersManager = m_auimgr.GetPane( "LayersManager" );
181
182 if( cfg && cfg->m_AuiPanels.right_panel_width > 0 )
183 SetAuiPaneSize( m_auimgr, layersManager, cfg->m_AuiPanels.right_panel_width, -1 );
184
185 if( cfg )
186 layersManager.Show( cfg->m_AuiPanels.show_layer_manager );
187
188 // Call Update() to fix all pane default sizes, especially the "InfoBar" pane before
189 // hiding it.
190 m_auimgr.Update();
191
192 // We don't want the infobar displayed right away
193 m_auimgr.GetPane( wxS( "InfoBar" ) ).Hide();
194 m_auimgr.Update();
195
196 m_canvas->SetInfoBar( m_infoBar );
197 m_canvas->SetStatusBar( status_bar );
198
199 try
200 {
201#if defined(__linux__) || defined(__FreeBSD__)
202 m_spaceMouse = std::make_unique<SPNAV_VIEWER_PLUGIN>( m_canvas );
203#else
204 m_spaceMouse = std::make_unique<NL_3D_VIEWER_PLUGIN>( m_canvas );
205#endif
206 }
207 catch( const std::system_error& e )
208 {
209 wxLogTrace( wxT( "KI_TRACE_NAVLIB" ), e.what() );
210 }
211
212 // Fixes bug in Windows (XP and possibly others) where the canvas requires the focus
213 // in order to receive mouse events. Otherwise, the user has to click somewhere on
214 // the canvas before it will respond to mouse wheel events.
215 m_canvas->SetFocus();
216}
217
218
220{
221 // Shutdown all running tools
222 if( m_toolManager )
223 m_toolManager->ShutdownAllTools();
224
225 Prj().GetProjectFile().m_Viewports3D = m_appearancePanel->GetUserViewports();
226
227 m_canvas->SetEventDispatcher( nullptr );
228
229 m_auimgr.UnInit();
230}
231
232
234{
236
237 ACTION_MANAGER* mgr = m_toolManager->GetActionManager();
239
240// Helper to define check conditions
241#define GridSizeCheck( x ) ACTION_CONDITIONS().Check( cond.GridSize( x ) )
242
243 auto raytracing =
244 [this]( const SELECTION& aSel )
245 {
246 return m_boardAdapter.m_Cfg->m_Render.engine != RENDER_ENGINE::OPENGL;
247 };
248 auto showMissingModels =
249 [this]( const SELECTION& aSel )
250 {
251 return m_boardAdapter.m_Cfg->m_Render.show_missing_models;
252 };
253 auto showTH =
254 [this]( const SELECTION& aSel )
255 {
256 return m_boardAdapter.m_Cfg->m_Render.show_footprints_normal;
257 };
258 auto showSMD =
259 [this]( const SELECTION& aSel )
260 {
261 return m_boardAdapter.m_Cfg->m_Render.show_footprints_insert;
262 };
263 auto showVirtual =
264 [this]( const SELECTION& aSel )
265 {
266 return m_boardAdapter.m_Cfg->m_Render.show_footprints_virtual;
267 };
268 auto show_NotInPosfile =
269 [this]( const SELECTION& aSel )
270 {
271 return m_boardAdapter.m_Cfg->m_Render.show_footprints_not_in_posfile;
272 };
273 auto show_DNP =
274 [this]( const SELECTION& aSel )
275 {
276 return m_boardAdapter.m_Cfg->m_Render.show_footprints_dnp;
277 };
278 auto showBBoxes =
279 [this]( const SELECTION& aSel )
280 {
281 return m_boardAdapter.m_Cfg->m_Render.show_model_bbox;
282 };
283 auto showNavig =
284 [this]( const SELECTION& aSel )
285 {
286 return m_boardAdapter.m_Cfg->m_Render.show_navigator;
287 };
288 auto ortho =
289 [this]( const SELECTION& )
290 {
291 return m_currentCamera.GetProjection() == PROJECTION_TYPE::ORTHO;
292 };
293
294 auto appearances =
295 [this]( const SELECTION& aSel )
296 {
297 return m_boardAdapter.m_Cfg->m_AuiPanels.show_layer_manager;
298 };
299
301 mgr->SetConditions( EDA_3D_ACTIONS::toggleShowMissingModels, ACTION_CONDITIONS().Check( showMissingModels ) );
302
304 mgr->SetConditions( EDA_3D_ACTIONS::showSMD, ACTION_CONDITIONS().Check( showSMD ) );
305 mgr->SetConditions( EDA_3D_ACTIONS::showVirtual, ACTION_CONDITIONS().Check( showVirtual ) );
307 ACTION_CONDITIONS().Check( show_NotInPosfile ) );
308 mgr->SetConditions( EDA_3D_ACTIONS::showDNP, ACTION_CONDITIONS().Check( show_DNP ) );
309
310 mgr->SetConditions( EDA_3D_ACTIONS::showBBoxes, ACTION_CONDITIONS().Check( showBBoxes ) );
312
319
321 ACTION_CONDITIONS().Check( appearances ) );
322
323#undef GridSizeCheck
324}
325
326
327bool EDA_3D_VIEWER_FRAME::TryBefore( wxEvent& aEvent )
328{
329 static bool s_presetSwitcherShown = false;
330 static bool s_viewportSwitcherShown = false;
331
332 // wxWidgets generates no key events for the tab key when the ctrl key is held down. One
333 // way around this is to look at all events and inspect the keyboard state of the tab key.
334 // However, this runs into issues on some linux VMs where querying the keyboard state is
335 // very slow. Fortunately we only use ctrl-tab on Mac, so we implement this lovely hack:
336#ifdef __WXMAC__
337 if( wxGetKeyState( WXK_TAB ) )
338#else
339 if( ( aEvent.GetEventType() == wxEVT_CHAR || aEvent.GetEventType() == wxEVT_CHAR_HOOK )
340 && static_cast<wxKeyEvent&>( aEvent ).GetKeyCode() == WXK_TAB )
341#endif
342 {
343 if( !s_presetSwitcherShown && wxGetKeyState( PRESET_SWITCH_KEY ) )
344 {
345 if( m_appearancePanel && this->IsActive() )
346 {
347 wxArrayString mru = m_appearancePanel->GetLayerPresetsMRU();
348
349 if( mru.size() > 0 )
350 {
351 for( wxString& str : mru )
352 {
353 if( str == FOLLOW_PCB )
354 str = _( "Follow PCB Editor" );
355 else if( str == FOLLOW_PLOT_SETTINGS )
356 str = _( "Follow PCB Plot Settings" );
357 }
358
359 EDA_VIEW_SWITCHER switcher( this, mru, PRESET_SWITCH_KEY );
360
361 s_presetSwitcherShown = true;
362 switcher.ShowModal();
363 s_presetSwitcherShown = false;
364
365 int idx = switcher.GetSelection();
366
367 if( idx >= 0 && idx < (int) mru.size() )
368 {
369 wxString internalName = m_appearancePanel->GetLayerPresetsMRU()[idx];
370 m_appearancePanel->ApplyLayerPreset( internalName );
371 }
372
373 return true;
374 }
375 }
376 }
377 else if( !s_viewportSwitcherShown && wxGetKeyState( VIEWPORT_SWITCH_KEY ) )
378 {
379 if( this->IsActive() )
380 {
381 const wxArrayString& viewportMRU = m_appearancePanel->GetViewportsMRU();
382
383 if( viewportMRU.size() > 0 )
384 {
385 EDA_VIEW_SWITCHER switcher( this, viewportMRU, VIEWPORT_SWITCH_KEY );
386
387 s_viewportSwitcherShown = true;
388 switcher.ShowModal();
389 s_viewportSwitcherShown = false;
390
391 int idx = switcher.GetSelection();
392
393 if( idx >= 0 && idx < (int) viewportMRU.size() )
394 m_appearancePanel->ApplyViewport( viewportMRU[idx] );
395
396 return true;
397 }
398 }
399 }
400 }
401
402 return wxFrame::TryBefore( aEvent );
403}
404
405
406void EDA_3D_VIEWER_FRAME::handleIconizeEvent( wxIconizeEvent& aEvent )
407{
409
410 if( m_spaceMouse && aEvent.IsIconized() )
411 m_spaceMouse->SetFocus( false );
412}
413
414
416{
417 // This will schedule a request to load later
418 // ReloadRequest also updates the board pointer so always call it first
419 if( m_canvas )
420 m_canvas->ReloadRequest( GetBoard(), PROJECT_PCB::Get3DCacheManager( &Prj() ) );
421
423 m_appearancePanel->UpdateLayerCtls();
424}
425
426
427void EDA_3D_VIEWER_FRAME::NewDisplay( bool aForceImmediateRedraw )
428{
429 if( m_canvas )
430 m_canvas->ReloadRequest( GetBoard(), PROJECT_PCB::Get3DCacheManager( &Prj() ) );
431
432 // After the ReloadRequest call, the refresh often takes a bit of time,
433 // and it is made here only on request.
434 if( m_canvas && aForceImmediateRedraw )
435 m_canvas->Refresh();
436}
437
438
440{
441 // Only update in OpenGL for an interactive interaction
442 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
443 m_canvas->Request_refresh( true );
444}
445
446
448{
449 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
450 m_canvas->Request_refresh();
451 else
452 NewDisplay( true );
453}
454
455
456void EDA_3D_VIEWER_FRAME::Exit3DFrame( wxCommandEvent &event )
457{
458 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::Exit3DFrame" ) );
459
460 Close( true );
461}
462
463
464void EDA_3D_VIEWER_FRAME::OnCloseWindow( wxCloseEvent &event )
465{
466 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::OnCloseWindow" ) );
467
468 // Do not show the layer manager during closing to avoid flicker on some platforms (Windows)
469 // that generate useless redraw of items in the Layer Manager
470 if( m_auimgr.GetPane( wxS( "LayersManager" ) ).IsShown() )
471 m_auimgr.GetPane( wxS( "LayersManager" ) ).Show( false );
472
473 if( m_canvas )
474 m_canvas->Close();
475
476 Destroy();
477 event.Skip( true );
478}
479
480
482{
483 if( m_canvas == nullptr )
484 return;
485
486 switch( event.GetId() )
487 {
489 {
490 m_boardAdapter.SetLayerColors( m_boardAdapter.GetDefaultColors() );
491
493 cfg->ResetToDefaults();
494
496
497 // Tell canvas that we (may have) changed the render engine
499 NewDisplay( true );
500 return;
501 }
502
503 default:
504 wxFAIL_MSG( wxT( "Invalid event in EDA_3D_VIEWER_FRAME::Process_Special_Functions()" ) );
505 return;
506 }
507}
508
509
510void EDA_3D_VIEWER_FRAME::onDisableRayTracing( wxCommandEvent& aEvent )
511{
512 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::%s disabling ray tracing." ), __WXFUNCTION__ );
513
515 m_boardAdapter.m_Cfg->m_Render.engine = RENDER_ENGINE::OPENGL;
516}
517
518
519void EDA_3D_VIEWER_FRAME::OnActivate( wxActivateEvent &aEvent )
520{
521 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::OnActivate" ) );
522
523 if( aEvent.GetActive() && m_canvas )
524 {
525 // Reload data if 3D frame shows a board,
526 // because it can be changed since last frame activation
527 if( m_canvas->IsReloadRequestPending() )
528 m_canvas->Request_refresh();
529
530 // Activates again the focus of the canvas so it will catch mouse and key events
531 m_canvas->SetFocus();
532 }
533
534 if( m_spaceMouse )
535 m_spaceMouse->SetFocus( aEvent.GetActive() );
536
537 aEvent.Skip(); // required under wxMAC
538}
539
540
541void EDA_3D_VIEWER_FRAME::OnSetFocus( wxFocusEvent& aEvent )
542{
543 // Activates again the focus of the canvas so it will catch mouse and key events
544 if( m_canvas )
545 m_canvas->SetFocus();
546
547 aEvent.Skip();
548}
549
550
552{
554
555 // Dynamic_cast here will fail on Mac when called from CvPCB.
556 EDA_3D_VIEWER_SETTINGS* cfg = static_cast<EDA_3D_VIEWER_SETTINGS*>( aCfg );
557
558 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::LoadSettings" ) );
559
560 if( cfg )
561 {
562 applySettings( cfg );
563
564 m_boardAdapter.SetBoard( GetBoard() );
565
566 // When opening the 3D viewer, we use the OpenGL mode, never the ray tracing engine
567 // because the ray tracing is very time consuming, and can be seen as not working
568 // (freeze window) with large boards.
569 m_boardAdapter.m_Cfg->m_Render.engine = RENDER_ENGINE::OPENGL;
570
572 {
573 wxString legacyColorsPresetName = _( "legacy colors" );
574
575 cfg->m_UseStackupColors = false;
576
577 if( !cfg->FindPreset( legacyColorsPresetName ) )
578 {
579 cfg->m_LayerPresets.emplace_back( legacyColorsPresetName,
580 GetAdapter().GetDefaultVisibleLayers(),
581 GetAdapter().GetDefaultColors() );
582 }
583
585 }
586
587 m_boardAdapter.InitSettings( nullptr, nullptr );
588
590 m_appearancePanel->CommonSettingsChanged();
591 }
592}
593
594
596{
598
600 {
601 cfg->m_AuiPanels.right_panel_width = m_appearancePanel->GetSize().x;
602
603 cfg->m_Camera.animation_enabled = m_canvas->GetAnimationEnabled();
604 cfg->m_Camera.moving_speed_multiplier = m_canvas->GetMovingSpeedMultiplier();
605 cfg->m_Camera.projection_mode = m_canvas->GetProjectionMode();
606 }
607}
608
609
611{
612 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::CommonSettingsChanged" ) );
613
614 // Regen menu bars, etc
616
618
620
621 if( cfg )
622 applySettings( cfg );
623
624 if( EDA_3D_CONTROLLER* ctrlTool = GetToolManager()->GetTool<EDA_3D_CONTROLLER>() )
625 ctrlTool->SetRotationIncrement( cfg ? cfg->m_Camera.rotation_increment : 10.0 );
626
627 m_appearancePanel->CommonSettingsChanged();
628
629 NewDisplay( true );
630}
631
632
634{
636
637 SetTitle( _( "3D Viewer" ) );
639
641 {
642 wxAuiPaneInfo& lm_pane_info = m_auimgr.GetPane( m_appearancePanel );
643 lm_pane_info.Caption( _( "Appearance" ) );
644 }
645
646 SetStatusText( wxEmptyString, ACTIVITY );
647 SetStatusText( wxEmptyString, HOVERED_ITEM );
648}
649
650
652{
653 wxAuiPaneInfo& layersManager = m_auimgr.GetPane( "LayersManager" );
654
656 {
657 // show auxiliary Vertical layers and visibility manager toolbar
658 cfg->m_AuiPanels.show_layer_manager = !cfg->m_AuiPanels.show_layer_manager;
659
660 layersManager.Show( cfg->m_AuiPanels.show_layer_manager );
661
662 if( cfg->m_AuiPanels.show_layer_manager )
663 {
664 SetAuiPaneSize( m_auimgr, layersManager, cfg->m_AuiPanels.right_panel_width, -1 );
665 }
666 else
667 {
668 cfg->m_AuiPanels.right_panel_width = m_appearancePanel->GetSize().x;
669 m_auimgr.Update();
670 }
671 }
672}
673
674
676{
678 m_appearancePanel->OnDarkModeToggle();
679}
680
681
683{
684 wxString fullFileName;
685
687 {
688 if( !getExportFileName( aFormat, fullFileName ) )
689 return;
690 }
691
692 wxImage screenshotImage = captureCurrentViewScreenshot();
693
694 if( screenshotImage.IsOk() )
695 {
696 saveOrCopyImage( screenshotImage, aFormat, fullFileName );
697 }
698}
699
700
702{
703 // Ensure we have the latest 3D view (remember 3D view is buffered)
704 // Also ensure any highlighted item is not highlighted when creating screen shot
706 bool original_highlight = cfg.highlight_on_rollover;
707 cfg.highlight_on_rollover = false;
708
709 m_canvas->DoRePaint(); // init first buffer
710 m_canvas->DoRePaint(); // init second buffer
711
712 wxImage screenshotImage;
713
714 if( m_canvas )
715 {
716 // Build image from the 3D buffer
717 wxWindowUpdateLocker noUpdates( this );
718 m_canvas->GetScreenshot( screenshotImage );
719 }
720
721 // Restore highlight setting
722 cfg.highlight_on_rollover = original_highlight;
723
724 return screenshotImage;
725}
726
727
729{
730 wxString fullFileName;
731
733 {
734 if( !getExportFileName( aFormat, fullFileName ) )
735 return;
736 }
737
738 wxImage screenshotImage = captureScreenshot( aSize );
739
740 if( screenshotImage.IsOk() )
741 {
742 saveOrCopyImage( screenshotImage, aFormat, fullFileName );
743 }
744}
745
746
748{
749 // Create combined wildcard for both formats
750 const wxString wildcard = FILEEXT::JpegFileWildcard() + "|" + FILEEXT::PngFileWildcard();
751
754
755 // Set default extension based on current format
756 const wxString defaultExt = ( aFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG ) ?
758 m_defaultSaveScreenshotFileName.SetExt( defaultExt );
759
760 wxFileDialog dlg( this, _( "3D Image File Name" ),
762 m_defaultSaveScreenshotFileName.GetFullName(), wildcard,
763 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
764
766
767 // Set initial filter index based on current format
768 dlg.SetFilterIndex( ( aFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG ) ? 0 : 1 );
769
770 if( dlg.ShowModal() == wxID_CANCEL )
771 return false;
772
773 m_defaultSaveScreenshotFileName = dlg.GetPath();
774
775 // Determine format based on file extension first
776 wxString fileExt = m_defaultSaveScreenshotFileName.GetExt().Lower();
777 EDA_3D_VIEWER_EXPORT_FORMAT detectedFormat;
778 bool formatDetected = false;
779
780 if( fileExt == wxT("jpg") || fileExt == wxT("jpeg") )
781 {
782 detectedFormat = EDA_3D_VIEWER_EXPORT_FORMAT::JPEG;
783 formatDetected = true;
784 }
785 else if( fileExt == wxT("png") )
786 {
787 detectedFormat = EDA_3D_VIEWER_EXPORT_FORMAT::PNG;
788 formatDetected = true;
789 }
790
791 // If format can't be determined from extension, use dropdown selection
792 if( !formatDetected )
793 {
794 int filterIndex = dlg.GetFilterIndex();
795 detectedFormat = ( filterIndex == 0 ) ? EDA_3D_VIEWER_EXPORT_FORMAT::JPEG :
797
798 // Append appropriate extension
799 const wxString ext = ( detectedFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG ) ?
802 }
803
804 // Update the format parameter
805 aFormat = detectedFormat;
806
807 // Check directory permissions using the updated filename
808 wxFileName fn = m_defaultSaveScreenshotFileName;
809
810 if( !fn.IsDirWritable() )
811 {
812 wxString msg;
813 msg.Printf( _( "Insufficient permissions to save file '%s'." ),
814 m_defaultSaveScreenshotFileName.GetFullPath() );
815 wxMessageBox( msg, _( "Error" ), wxOK | wxICON_ERROR, this );
816 return false;
817 }
818
819 fullFileName = m_defaultSaveScreenshotFileName.GetFullPath();
820 return true;
821}
822
823
824wxImage EDA_3D_VIEWER_FRAME::captureScreenshot( const wxSize& aSize )
825{
827 camera.SetCurWindowSize( aSize );
828
830 EDA_3D_VIEWER_SETTINGS* backupCfg = m_boardAdapter.m_Cfg;
831
832 auto configRestorer = std::unique_ptr<void, std::function<void(void*)>>(
833 reinterpret_cast<void*>(1),
834 [&](void*) { m_boardAdapter.m_Cfg = backupCfg; }
835 );
836
837 if( cfg )
838 m_boardAdapter.m_Cfg = cfg;
839
840 if( cfg && cfg->m_Render.engine == RENDER_ENGINE::RAYTRACING )
841 return captureRaytracingScreenshot( m_boardAdapter, camera, aSize );
842 else
843 return captureOpenGLScreenshot( m_boardAdapter, camera, aSize );
844}
845
846
848{
850
851 if( cfg )
852 aAdapter.m_Cfg = cfg;
853}
854
855
856wxImage EDA_3D_VIEWER_FRAME::captureRaytracingScreenshot( BOARD_ADAPTER& aAdapter, TRACK_BALL& aCamera, const wxSize& aSize )
857{
858 BOARD_ADAPTER tempadapter;
859 tempadapter.SetBoard( GetBoard() );
860 tempadapter.m_Cfg = aAdapter.m_Cfg;
861 tempadapter.InitSettings( nullptr, nullptr );
862 tempadapter.Set3dCacheManager( aAdapter.Get3dCacheManager() );
863
864 RENDER_3D_RAYTRACE_RAM raytrace( tempadapter, aCamera );
865 raytrace.SetCurWindowSize( aSize );
866
867 while( raytrace.Redraw( false, nullptr, nullptr ) );
868
869 uint8_t* rgbaBuffer = raytrace.GetBuffer();
870 wxSize realSize = raytrace.GetRealBufferSize();
871
872 if( !rgbaBuffer )
873 return wxImage();
874
875 return convertRGBAToImage( rgbaBuffer, realSize );
876}
877
878
879wxImage EDA_3D_VIEWER_FRAME::convertRGBAToImage( uint8_t* aRGBABuffer, const wxSize& aRealSize )
880{
881 const unsigned int wxh = aRealSize.x * aRealSize.y;
882
883 unsigned char* rgbBuffer = (unsigned char*) malloc( wxh * 3 );
884 unsigned char* alphaBuffer = (unsigned char*) malloc( wxh );
885
886 unsigned char* rgbaPtr = aRGBABuffer;
887 unsigned char* rgbPtr = rgbBuffer;
888 unsigned char* alphaPtr = alphaBuffer;
889
890 for( int y = 0; y < aRealSize.y; y++ )
891 {
892 for( int x = 0; x < aRealSize.x; x++ )
893 {
894 rgbPtr[0] = rgbaPtr[0];
895 rgbPtr[1] = rgbaPtr[1];
896 rgbPtr[2] = rgbaPtr[2];
897 alphaPtr[0] = rgbaPtr[3];
898
899 rgbaPtr += 4;
900 rgbPtr += 3;
901 alphaPtr += 1;
902 }
903 }
904
905 wxImage screenshotImage;
906 screenshotImage.Create( aRealSize );
907 screenshotImage.SetData( rgbBuffer );
908 screenshotImage.SetAlpha( alphaBuffer );
909 return screenshotImage.Mirror( false );
910}
911
912
913wxImage EDA_3D_VIEWER_FRAME::captureOpenGLScreenshot( BOARD_ADAPTER& aAdapter, TRACK_BALL& aCamera, const wxSize& aSize )
914{
915 EDA_3D_VIEWER_SETTINGS* cfg = aAdapter.m_Cfg;
916 ANTIALIASING_MODE aaMode = cfg ? static_cast<ANTIALIASING_MODE>( cfg->m_Render.opengl_AA_mode )
918
919 wxFrame temp( this, wxID_ANY, wxEmptyString, wxDefaultPosition, aSize, wxFRAME_NO_TASKBAR );
920 temp.Hide();
921 BOARD_ADAPTER tempadapter;
922 tempadapter.SetBoard( GetBoard() );
923 tempadapter.m_Cfg = aAdapter.m_Cfg;
924 tempadapter.InitSettings( nullptr, nullptr );
925 tempadapter.Set3dCacheManager( aAdapter.Get3dCacheManager() );
926
927 auto canvas = std::make_unique<EDA_3D_CANVAS>( &temp,
928 OGL_ATT_LIST::GetAttributesList( aaMode, true ),
929 tempadapter, aCamera,
930 aAdapter.Get3dCacheManager() );
931
932 canvas->SetSize( aSize );
933 configureCanvas( canvas, cfg );
934 wxWindowUpdateLocker noUpdates( this );
935
936 // Temporarily disable highlight during screenshot
938 bool original_highlight = renderCfg.highlight_on_rollover;
939 bool original_navigator = renderCfg.show_navigator;
940 renderCfg.show_navigator = false;
941 renderCfg.highlight_on_rollover = false;
942
943 std::vector<unsigned char> buffer(aSize.x * aSize.y * 4); // RGBA format
944 canvas->RenderToFrameBuffer( buffer.data(), aSize.x, aSize.y );
945 wxImage result = convertRGBAToImage( buffer.data(), aSize );
946
947 // Restore highlight setting
948 renderCfg.highlight_on_rollover = original_highlight;
949 renderCfg.show_navigator = original_navigator;
950
951 return result;
952}
953
954
955void EDA_3D_VIEWER_FRAME::configureCanvas( std::unique_ptr<EDA_3D_CANVAS>& aCanvas, EDA_3D_VIEWER_SETTINGS* aCfg )
956{
957 if( aCfg )
958 {
959 aCanvas->SetAnimationEnabled( aCfg->m_Camera.animation_enabled );
960 aCanvas->SetMovingSpeedMultiplier( aCfg->m_Camera.moving_speed_multiplier );
961 aCanvas->SetProjectionMode( aCfg->m_Camera.projection_mode );
962 }
963
964 aCanvas->SetVcSettings( EDA_DRAW_PANEL_GAL::GetVcSettings() );
965}
966
967
968void EDA_3D_VIEWER_FRAME::saveOrCopyImage( const wxImage& aScreenshotImage, EDA_3D_VIEWER_EXPORT_FORMAT aFormat, const wxString& aFullFileName )
969{
971 {
972 copyImageToClipboard( aScreenshotImage );
973 }
974 else
975 {
976 saveImageToFile( aScreenshotImage, aFormat, aFullFileName );
977 }
978}
979
980
981void EDA_3D_VIEWER_FRAME::copyImageToClipboard( const wxImage& aScreenshotImage )
982{
983 wxBitmap bitmap( aScreenshotImage );
984 wxLogNull doNotLog;
985
986 if( wxTheClipboard->Open() )
987 {
988 wxBitmapDataObject* dobjBmp = new wxBitmapDataObject( bitmap );
989
990 if( !wxTheClipboard->SetData( dobjBmp ) )
991 wxMessageBox( _( "Failed to copy image to clipboard" ) );
992
993 wxTheClipboard->Flush();
994 wxTheClipboard->Close();
995 }
996}
997
998
999void EDA_3D_VIEWER_FRAME::saveImageToFile( const wxImage& aScreenshotImage, EDA_3D_VIEWER_EXPORT_FORMAT aFormat, const wxString& aFullFileName )
1000{
1001 bool fmt_is_jpeg = ( aFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG );
1002
1003 if( !aScreenshotImage.SaveFile( aFullFileName, fmt_is_jpeg ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG ) )
1004 {
1005 wxMessageBox( _( "Can't save file" ) );
1006 }
1007}
1008
1009
1011{
1012 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::RenderEngineChanged()" ) );
1013
1014 if( m_canvas )
1015 m_canvas->RenderEngineChanged();
1016}
1017
1018
1020{
1021 wxCHECK_RET( m_canvas, wxT( "Cannot load settings to null canvas" ) );
1022
1023 COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
1024
1025 // TODO(JE) use all control options
1026 m_boardAdapter.m_MousewheelPanning = settings->m_Input.scroll_modifier_zoom != 0;
1027}
1028
1029
1031{
1032 m_boardAdapter.m_Cfg = cfg;
1033
1034 m_canvas->SetAnimationEnabled( cfg->m_Camera.animation_enabled );
1035 m_canvas->SetMovingSpeedMultiplier( cfg->m_Camera.moving_speed_multiplier );
1036 m_canvas->SetProjectionMode( cfg->m_Camera.projection_mode );
1037
1038 m_canvas->SetVcSettings( EDA_DRAW_PANEL_GAL::GetVcSettings() );
1039}
@ ID_DISABLE_RAY_TRACING
@ ID_START_COMMAND_3D
@ ID_MENU_COMMAND_END
@ ID_MENU3D_RESET_DEFAULTS
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:128
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.
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...
APP_SETTINGS_BASE is a settings class that should be derived for each standalone KiCad application.
Helper class to handle information needed to display 3D board.
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.
EDA_3D_VIEWER_SETTINGS * m_Cfg
void Set3dCacheManager(S3D_CACHE *aCacheMgr) noexcept
Update the cache manager pointer.
S3D_CACHE * Get3dCacheManager() const noexcept
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 toggleShowMissingModels
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.
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
void Exit3DFrame(wxCommandEvent &event)
Called when user press the File->Exit.
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.
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.
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:315
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:541
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:130
std::vector< VIEWPORT3D > m_Viewports3D
List of stored viewports (pos + zoom)
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:181
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
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Master controller class:
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:77
#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
@ 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:669
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:448
Declaration of the NL_3D_VIEWER_PLUGIN class.
ANTIALIASING_MODE
Anti-aliasing options.
#define _HKI(x)
Definition page_info.cpp:44
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
T * GetAppSettings(const char *aFilename)
Functors that can be used to figure out how the action controls should be displayed in the UI and if ...
wxString result
Test unit parsing edge cases and error handling.
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.