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 applySettings( cfg );
621
622 if( EDA_3D_CONTROLLER* ctrlTool = GetToolManager()->GetTool<EDA_3D_CONTROLLER>() )
623 ctrlTool->SetRotationIncrement( cfg ? cfg->m_Camera.rotation_increment : 10.0 );
624
625 m_appearancePanel->CommonSettingsChanged();
626
627 NewDisplay( true );
628}
629
630
632{
634
635 SetTitle( _( "3D Viewer" ) );
637
639 {
640 wxAuiPaneInfo& lm_pane_info = m_auimgr.GetPane( m_appearancePanel );
641 lm_pane_info.Caption( _( "Appearance" ) );
642 }
643
644 SetStatusText( wxEmptyString, ACTIVITY );
645 SetStatusText( wxEmptyString, HOVERED_ITEM );
646}
647
648
650{
651 wxAuiPaneInfo& layersManager = m_auimgr.GetPane( "LayersManager" );
652
654 {
655 // show auxiliary Vertical layers and visibility manager toolbar
656 cfg->m_AuiPanels.show_layer_manager = !cfg->m_AuiPanels.show_layer_manager;
657
658 layersManager.Show( cfg->m_AuiPanels.show_layer_manager );
659
660 if( cfg->m_AuiPanels.show_layer_manager )
661 {
662 SetAuiPaneSize( m_auimgr, layersManager, cfg->m_AuiPanels.right_panel_width, -1 );
663 }
664 else
665 {
666 cfg->m_AuiPanels.right_panel_width = m_appearancePanel->GetSize().x;
667 m_auimgr.Update();
668 }
669 }
670}
671
672
674{
676 m_appearancePanel->OnDarkModeToggle();
677}
678
679
681{
682 wxString fullFileName;
683
685 {
686 if( !getExportFileName( aFormat, fullFileName ) )
687 return;
688 }
689
690 wxImage screenshotImage = captureCurrentViewScreenshot();
691
692 if( screenshotImage.IsOk() )
693 {
694 saveOrCopyImage( screenshotImage, aFormat, fullFileName );
695 }
696}
697
698
700{
701 // Ensure we have the latest 3D view (remember 3D view is buffered)
702 // Also ensure any highlighted item is not highlighted when creating screen shot
704 bool original_highlight = cfg.highlight_on_rollover;
705 cfg.highlight_on_rollover = false;
706
707 m_canvas->DoRePaint(); // init first buffer
708 m_canvas->DoRePaint(); // init second buffer
709
710 wxImage screenshotImage;
711
712 if( m_canvas )
713 {
714 // Build image from the 3D buffer
715 wxWindowUpdateLocker noUpdates( this );
716 m_canvas->GetScreenshot( screenshotImage );
717 }
718
719 // Restore highlight setting
720 cfg.highlight_on_rollover = original_highlight;
721
722 return screenshotImage;
723}
724
725
727{
728 wxString fullFileName;
729
731 {
732 if( !getExportFileName( aFormat, fullFileName ) )
733 return;
734 }
735
736 wxImage screenshotImage = captureScreenshot( aSize );
737
738 if( screenshotImage.IsOk() )
739 {
740 saveOrCopyImage( screenshotImage, aFormat, fullFileName );
741 }
742}
743
744
746{
747 // Create combined wildcard for both formats
748 const wxString wildcard = FILEEXT::JpegFileWildcard() + "|" + FILEEXT::PngFileWildcard();
749
752
753 // Set default extension based on current format
754 const wxString defaultExt = ( aFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG ) ?
756 m_defaultSaveScreenshotFileName.SetExt( defaultExt );
757
758 wxFileDialog dlg( this, _( "3D Image File Name" ),
760 m_defaultSaveScreenshotFileName.GetFullName(), wildcard,
761 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
762
764
765 // Set initial filter index based on current format
766 dlg.SetFilterIndex( ( aFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG ) ? 0 : 1 );
767
768 if( dlg.ShowModal() == wxID_CANCEL )
769 return false;
770
771 m_defaultSaveScreenshotFileName = dlg.GetPath();
772
773 // Determine format based on file extension first
774 wxString fileExt = m_defaultSaveScreenshotFileName.GetExt().Lower();
775 EDA_3D_VIEWER_EXPORT_FORMAT detectedFormat;
776 bool formatDetected = false;
777
778 if( fileExt == wxT("jpg") || fileExt == wxT("jpeg") )
779 {
780 detectedFormat = EDA_3D_VIEWER_EXPORT_FORMAT::JPEG;
781 formatDetected = true;
782 }
783 else if( fileExt == wxT("png") )
784 {
785 detectedFormat = EDA_3D_VIEWER_EXPORT_FORMAT::PNG;
786 formatDetected = true;
787 }
788
789 // If format can't be determined from extension, use dropdown selection
790 if( !formatDetected )
791 {
792 int filterIndex = dlg.GetFilterIndex();
793 detectedFormat = ( filterIndex == 0 ) ? EDA_3D_VIEWER_EXPORT_FORMAT::JPEG :
795
796 // Append appropriate extension
797 const wxString ext = ( detectedFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG ) ?
800 }
801
802 // Update the format parameter
803 aFormat = detectedFormat;
804
805 // Check directory permissions using the updated filename
806 wxFileName fn = m_defaultSaveScreenshotFileName;
807
808 if( !fn.IsDirWritable() )
809 {
810 wxString msg;
811 msg.Printf( _( "Insufficient permissions to save file '%s'." ),
812 m_defaultSaveScreenshotFileName.GetFullPath() );
813 wxMessageBox( msg, _( "Error" ), wxOK | wxICON_ERROR, this );
814 return false;
815 }
816
817 fullFileName = m_defaultSaveScreenshotFileName.GetFullPath();
818 return true;
819}
820
821
822wxImage EDA_3D_VIEWER_FRAME::captureScreenshot( const wxSize& aSize )
823{
825 camera.SetCurWindowSize( aSize );
826
828 EDA_3D_VIEWER_SETTINGS* backupCfg = m_boardAdapter.m_Cfg;
829
830 auto configRestorer = std::unique_ptr<void, std::function<void(void*)>>(
831 reinterpret_cast<void*>(1),
832 [&](void*) { m_boardAdapter.m_Cfg = backupCfg; }
833 );
834
835 if( cfg )
836 m_boardAdapter.m_Cfg = cfg;
837
838 if( cfg && cfg->m_Render.engine == RENDER_ENGINE::RAYTRACING )
839 return captureRaytracingScreenshot( m_boardAdapter, camera, aSize );
840 else
841 return captureOpenGLScreenshot( m_boardAdapter, camera, aSize );
842}
843
844
846{
848
849 if( cfg )
850 aAdapter.m_Cfg = cfg;
851}
852
853
854wxImage EDA_3D_VIEWER_FRAME::captureRaytracingScreenshot( BOARD_ADAPTER& aAdapter, TRACK_BALL& aCamera, const wxSize& aSize )
855{
856 BOARD_ADAPTER tempadapter;
857 tempadapter.SetBoard( GetBoard() );
858 tempadapter.m_Cfg = aAdapter.m_Cfg;
859 tempadapter.InitSettings( nullptr, nullptr );
860 tempadapter.Set3dCacheManager( aAdapter.Get3dCacheManager() );
861
862 RENDER_3D_RAYTRACE_RAM raytrace( tempadapter, aCamera );
863 raytrace.SetCurWindowSize( aSize );
864
865 while( raytrace.Redraw( false, nullptr, nullptr ) );
866
867 uint8_t* rgbaBuffer = raytrace.GetBuffer();
868 wxSize realSize = raytrace.GetRealBufferSize();
869
870 if( !rgbaBuffer )
871 return wxImage();
872
873 return convertRGBAToImage( rgbaBuffer, realSize );
874}
875
876
877wxImage EDA_3D_VIEWER_FRAME::convertRGBAToImage( uint8_t* aRGBABuffer, const wxSize& aRealSize )
878{
879 const unsigned int wxh = aRealSize.x * aRealSize.y;
880
881 unsigned char* rgbBuffer = (unsigned char*) malloc( wxh * 3 );
882 unsigned char* alphaBuffer = (unsigned char*) malloc( wxh );
883
884 unsigned char* rgbaPtr = aRGBABuffer;
885 unsigned char* rgbPtr = rgbBuffer;
886 unsigned char* alphaPtr = alphaBuffer;
887
888 for( int y = 0; y < aRealSize.y; y++ )
889 {
890 for( int x = 0; x < aRealSize.x; x++ )
891 {
892 rgbPtr[0] = rgbaPtr[0];
893 rgbPtr[1] = rgbaPtr[1];
894 rgbPtr[2] = rgbaPtr[2];
895 alphaPtr[0] = rgbaPtr[3];
896
897 rgbaPtr += 4;
898 rgbPtr += 3;
899 alphaPtr += 1;
900 }
901 }
902
903 wxImage screenshotImage;
904 screenshotImage.Create( aRealSize );
905 screenshotImage.SetData( rgbBuffer );
906 screenshotImage.SetAlpha( alphaBuffer );
907 return screenshotImage.Mirror( false );
908}
909
910
911wxImage EDA_3D_VIEWER_FRAME::captureOpenGLScreenshot( BOARD_ADAPTER& aAdapter, TRACK_BALL& aCamera, const wxSize& aSize )
912{
913 EDA_3D_VIEWER_SETTINGS* cfg = aAdapter.m_Cfg;
914 ANTIALIASING_MODE aaMode = cfg ? static_cast<ANTIALIASING_MODE>( cfg->m_Render.opengl_AA_mode )
916
917 wxFrame temp( this, wxID_ANY, wxEmptyString, wxDefaultPosition, aSize, wxFRAME_NO_TASKBAR );
918 temp.Hide();
919 BOARD_ADAPTER tempadapter;
920 tempadapter.SetBoard( GetBoard() );
921 tempadapter.m_Cfg = aAdapter.m_Cfg;
922 tempadapter.InitSettings( nullptr, nullptr );
923 tempadapter.Set3dCacheManager( aAdapter.Get3dCacheManager() );
924
925 auto canvas = std::make_unique<EDA_3D_CANVAS>( &temp,
926 OGL_ATT_LIST::GetAttributesList( aaMode, true ),
927 tempadapter, aCamera,
928 aAdapter.Get3dCacheManager() );
929
930 canvas->SetSize( aSize );
931 configureCanvas( canvas, cfg );
932 wxWindowUpdateLocker noUpdates( this );
933
934 // Temporarily disable highlight during screenshot
936 bool original_highlight = renderCfg.highlight_on_rollover;
937 bool original_navigator = renderCfg.show_navigator;
938 renderCfg.show_navigator = false;
939 renderCfg.highlight_on_rollover = false;
940
941 std::vector<unsigned char> buffer(aSize.x * aSize.y * 4); // RGBA format
942 canvas->RenderToFrameBuffer( buffer.data(), aSize.x, aSize.y );
943 wxImage result = convertRGBAToImage( buffer.data(), aSize );
944
945 // Restore highlight setting
946 renderCfg.highlight_on_rollover = original_highlight;
947 renderCfg.show_navigator = original_navigator;
948
949 return result;
950}
951
952
953void EDA_3D_VIEWER_FRAME::configureCanvas( std::unique_ptr<EDA_3D_CANVAS>& aCanvas, EDA_3D_VIEWER_SETTINGS* aCfg )
954{
955 if( aCfg )
956 {
957 aCanvas->SetAnimationEnabled( aCfg->m_Camera.animation_enabled );
958 aCanvas->SetMovingSpeedMultiplier( aCfg->m_Camera.moving_speed_multiplier );
959 aCanvas->SetProjectionMode( aCfg->m_Camera.projection_mode );
960 }
961
962 aCanvas->SetVcSettings( EDA_DRAW_PANEL_GAL::GetVcSettings() );
963}
964
965
966void EDA_3D_VIEWER_FRAME::saveOrCopyImage( const wxImage& aScreenshotImage, EDA_3D_VIEWER_EXPORT_FORMAT aFormat, const wxString& aFullFileName )
967{
969 {
970 copyImageToClipboard( aScreenshotImage );
971 }
972 else
973 {
974 saveImageToFile( aScreenshotImage, aFormat, aFullFileName );
975 }
976}
977
978
979void EDA_3D_VIEWER_FRAME::copyImageToClipboard( const wxImage& aScreenshotImage )
980{
981 wxBitmap bitmap( aScreenshotImage );
982 wxLogNull doNotLog;
983
984 if( wxTheClipboard->Open() )
985 {
986 wxBitmapDataObject* dobjBmp = new wxBitmapDataObject( bitmap );
987
988 if( !wxTheClipboard->SetData( dobjBmp ) )
989 wxMessageBox( _( "Failed to copy image to clipboard" ) );
990
991 wxTheClipboard->Flush();
992 wxTheClipboard->Close();
993 }
994}
995
996
997void EDA_3D_VIEWER_FRAME::saveImageToFile( const wxImage& aScreenshotImage, EDA_3D_VIEWER_EXPORT_FORMAT aFormat, const wxString& aFullFileName )
998{
999 bool fmt_is_jpeg = ( aFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG );
1000
1001 if( !aScreenshotImage.SaveFile( aFullFileName, fmt_is_jpeg ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG ) )
1002 {
1003 wxMessageBox( _( "Can't save file" ) );
1004 }
1005}
1006
1007
1009{
1010 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::RenderEngineChanged()" ) );
1011
1012 if( m_canvas )
1013 m_canvas->RenderEngineChanged();
1014}
1015
1016
1018{
1019 wxCHECK_RET( m_canvas, wxT( "Cannot load settings to null canvas" ) );
1020
1021 COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
1022
1023 // TODO(JE) use all control options
1024 m_boardAdapter.m_MousewheelPanning = settings->m_Input.scroll_modifier_zoom != 0;
1025}
1026
1027
1029{
1030 m_boardAdapter.m_Cfg = cfg;
1031
1032 m_canvas->SetAnimationEnabled( cfg->m_Camera.animation_enabled );
1033 m_canvas->SetMovingSpeedMultiplier( cfg->m_Camera.moving_speed_multiplier );
1034 m_canvas->SetProjectionMode( cfg->m_Camera.projection_mode );
1035
1036 m_canvas->SetVcSettings( EDA_DRAW_PANEL_GAL::GetVcSettings() );
1037}
@ 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:655
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:435
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.