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#ifdef __linux__
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#ifdef __linux__
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 showTH =
249 [this]( const SELECTION& aSel )
250 {
251 return m_boardAdapter.m_Cfg->m_Render.show_footprints_normal;
252 };
253 auto showSMD =
254 [this]( const SELECTION& aSel )
255 {
256 return m_boardAdapter.m_Cfg->m_Render.show_footprints_insert;
257 };
258 auto showVirtual =
259 [this]( const SELECTION& aSel )
260 {
261 return m_boardAdapter.m_Cfg->m_Render.show_footprints_virtual;
262 };
263 auto show_NotInPosfile =
264 [this]( const SELECTION& aSel )
265 {
266 return m_boardAdapter.m_Cfg->m_Render.show_footprints_not_in_posfile;
267 };
268 auto show_DNP =
269 [this]( const SELECTION& aSel )
270 {
271 return m_boardAdapter.m_Cfg->m_Render.show_footprints_dnp;
272 };
273 auto showBBoxes =
274 [this]( const SELECTION& aSel )
275 {
276 return m_boardAdapter.m_Cfg->m_Render.show_model_bbox;
277 };
278 auto showNavig =
279 [this]( const SELECTION& aSel )
280 {
281 return m_boardAdapter.m_Cfg->m_Render.show_navigator;
282 };
283 auto ortho =
284 [this]( const SELECTION& )
285 {
286 return m_currentCamera.GetProjection() == PROJECTION_TYPE::ORTHO;
287 };
288
289 auto appearances =
290 [this]( const SELECTION& aSel )
291 {
292 return m_boardAdapter.m_Cfg->m_AuiPanels.show_layer_manager;
293 };
294
296
298 mgr->SetConditions( EDA_3D_ACTIONS::showSMD, ACTION_CONDITIONS().Check( showSMD ) );
299 mgr->SetConditions( EDA_3D_ACTIONS::showVirtual, ACTION_CONDITIONS().Check( showVirtual ) );
301 ACTION_CONDITIONS().Check( show_NotInPosfile ) );
302 mgr->SetConditions( EDA_3D_ACTIONS::showDNP, ACTION_CONDITIONS().Check( show_DNP ) );
303
304 mgr->SetConditions( EDA_3D_ACTIONS::showBBoxes, ACTION_CONDITIONS().Check( showBBoxes ) );
306
313
315 ACTION_CONDITIONS().Check( appearances ) );
316
317#undef GridSizeCheck
318}
319
320
321bool EDA_3D_VIEWER_FRAME::TryBefore( wxEvent& aEvent )
322{
323 static bool s_presetSwitcherShown = false;
324 static bool s_viewportSwitcherShown = false;
325
326 // wxWidgets generates no key events for the tab key when the ctrl key is held down. One
327 // way around this is to look at all events and inspect the keyboard state of the tab key.
328 // However, this runs into issues on some linux VMs where querying the keyboard state is
329 // very slow. Fortunately we only use ctrl-tab on Mac, so we implement this lovely hack:
330#ifdef __WXMAC__
331 if( wxGetKeyState( WXK_TAB ) )
332#else
333 if( ( aEvent.GetEventType() == wxEVT_CHAR || aEvent.GetEventType() == wxEVT_CHAR_HOOK )
334 && static_cast<wxKeyEvent&>( aEvent ).GetKeyCode() == WXK_TAB )
335#endif
336 {
337 if( !s_presetSwitcherShown && wxGetKeyState( PRESET_SWITCH_KEY ) )
338 {
339 if( m_appearancePanel && this->IsActive() )
340 {
341 wxArrayString mru = m_appearancePanel->GetLayerPresetsMRU();
342
343 if( mru.size() > 0 )
344 {
345 for( wxString& str : mru )
346 {
347 if( str == FOLLOW_PCB )
348 str = _( "Follow PCB Editor" );
349 else if( str == FOLLOW_PLOT_SETTINGS )
350 str = _( "Follow PCB Plot Settings" );
351 }
352
353 EDA_VIEW_SWITCHER switcher( this, mru, PRESET_SWITCH_KEY );
354
355 s_presetSwitcherShown = true;
356 switcher.ShowModal();
357 s_presetSwitcherShown = false;
358
359 int idx = switcher.GetSelection();
360
361 if( idx >= 0 && idx < (int) mru.size() )
362 {
363 wxString internalName = m_appearancePanel->GetLayerPresetsMRU()[idx];
364 m_appearancePanel->ApplyLayerPreset( internalName );
365 }
366
367 return true;
368 }
369 }
370 }
371 else if( !s_viewportSwitcherShown && wxGetKeyState( VIEWPORT_SWITCH_KEY ) )
372 {
373 if( this->IsActive() )
374 {
375 const wxArrayString& viewportMRU = m_appearancePanel->GetViewportsMRU();
376
377 if( viewportMRU.size() > 0 )
378 {
379 EDA_VIEW_SWITCHER switcher( this, viewportMRU, VIEWPORT_SWITCH_KEY );
380
381 s_viewportSwitcherShown = true;
382 switcher.ShowModal();
383 s_viewportSwitcherShown = false;
384
385 int idx = switcher.GetSelection();
386
387 if( idx >= 0 && idx < (int) viewportMRU.size() )
388 m_appearancePanel->ApplyViewport( viewportMRU[idx] );
389
390 return true;
391 }
392 }
393 }
394 }
395
396 return wxFrame::TryBefore( aEvent );
397}
398
399
400void EDA_3D_VIEWER_FRAME::handleIconizeEvent( wxIconizeEvent& aEvent )
401{
403
404 if( m_spaceMouse && aEvent.IsIconized() )
405 m_spaceMouse->SetFocus( false );
406}
407
408
410{
411 // This will schedule a request to load later
412 // ReloadRequest also updates the board pointer so always call it first
413 if( m_canvas )
414 m_canvas->ReloadRequest( GetBoard(), PROJECT_PCB::Get3DCacheManager( &Prj() ) );
415
417 m_appearancePanel->UpdateLayerCtls();
418}
419
420
421void EDA_3D_VIEWER_FRAME::NewDisplay( bool aForceImmediateRedraw )
422{
423 if( m_canvas )
424 m_canvas->ReloadRequest( GetBoard(), PROJECT_PCB::Get3DCacheManager( &Prj() ) );
425
426 // After the ReloadRequest call, the refresh often takes a bit of time,
427 // and it is made here only on request.
428 if( m_canvas && aForceImmediateRedraw )
429 m_canvas->Refresh();
430}
431
432
434{
435 // Only update in OpenGL for an interactive interaction
436 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
437 m_canvas->Request_refresh( true );
438}
439
440
442{
443 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
444 m_canvas->Request_refresh();
445 else
446 NewDisplay( true );
447}
448
449
450void EDA_3D_VIEWER_FRAME::Exit3DFrame( wxCommandEvent &event )
451{
452 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::Exit3DFrame" ) );
453
454 Close( true );
455}
456
457
458void EDA_3D_VIEWER_FRAME::OnCloseWindow( wxCloseEvent &event )
459{
460 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::OnCloseWindow" ) );
461
462 // Do not show the layer manager during closing to avoid flicker on some platforms (Windows)
463 // that generate useless redraw of items in the Layer Manager
464 if( m_auimgr.GetPane( wxS( "LayersManager" ) ).IsShown() )
465 m_auimgr.GetPane( wxS( "LayersManager" ) ).Show( false );
466
467 if( m_canvas )
468 m_canvas->Close();
469
470 Destroy();
471 event.Skip( true );
472}
473
474
476{
477 if( m_canvas == nullptr )
478 return;
479
480 switch( event.GetId() )
481 {
483 {
484 m_boardAdapter.SetLayerColors( m_boardAdapter.GetDefaultColors() );
485
487 cfg->ResetToDefaults();
488
490
491 // Tell canvas that we (may have) changed the render engine
493 NewDisplay( true );
494 return;
495 }
496
497 default:
498 wxFAIL_MSG( wxT( "Invalid event in EDA_3D_VIEWER_FRAME::Process_Special_Functions()" ) );
499 return;
500 }
501}
502
503
504void EDA_3D_VIEWER_FRAME::onDisableRayTracing( wxCommandEvent& aEvent )
505{
506 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::%s disabling ray tracing." ), __WXFUNCTION__ );
507
509 m_boardAdapter.m_Cfg->m_Render.engine = RENDER_ENGINE::OPENGL;
510}
511
512
513void EDA_3D_VIEWER_FRAME::OnActivate( wxActivateEvent &aEvent )
514{
515 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::OnActivate" ) );
516
517 if( aEvent.GetActive() && m_canvas )
518 {
519 // Reload data if 3D frame shows a board,
520 // because it can be changed since last frame activation
521 if( m_canvas->IsReloadRequestPending() )
522 m_canvas->Request_refresh();
523
524 // Activates again the focus of the canvas so it will catch mouse and key events
525 m_canvas->SetFocus();
526 }
527
528 if( m_spaceMouse )
529 m_spaceMouse->SetFocus( aEvent.GetActive() );
530
531 aEvent.Skip(); // required under wxMAC
532}
533
534
535void EDA_3D_VIEWER_FRAME::OnSetFocus( wxFocusEvent& aEvent )
536{
537 // Activates again the focus of the canvas so it will catch mouse and key events
538 if( m_canvas )
539 m_canvas->SetFocus();
540
541 aEvent.Skip();
542}
543
544
546{
548
549 // Dynamic_cast here will fail on Mac when called from CvPCB.
550 EDA_3D_VIEWER_SETTINGS* cfg = static_cast<EDA_3D_VIEWER_SETTINGS*>( aCfg );
551
552 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::LoadSettings" ) );
553
554 if( cfg )
555 {
556 applySettings( cfg );
557
558 m_boardAdapter.SetBoard( GetBoard() );
559
560 // When opening the 3D viewer, we use the OpenGL mode, never the ray tracing engine
561 // because the ray tracing is very time consuming, and can be seen as not working
562 // (freeze window) with large boards.
563 m_boardAdapter.m_Cfg->m_Render.engine = RENDER_ENGINE::OPENGL;
564
566 {
567 wxString legacyColorsPresetName = _( "legacy colors" );
568
569 cfg->m_UseStackupColors = false;
570
571 if( !cfg->FindPreset( legacyColorsPresetName ) )
572 {
573 cfg->m_LayerPresets.emplace_back( legacyColorsPresetName,
574 GetAdapter().GetDefaultVisibleLayers(),
575 GetAdapter().GetDefaultColors() );
576 }
577
579 }
580
581 m_boardAdapter.InitSettings( nullptr, nullptr );
582
584 m_appearancePanel->CommonSettingsChanged();
585 }
586}
587
588
590{
592
594 {
595 cfg->m_AuiPanels.right_panel_width = m_appearancePanel->GetSize().x;
596
597 cfg->m_Camera.animation_enabled = m_canvas->GetAnimationEnabled();
598 cfg->m_Camera.moving_speed_multiplier = m_canvas->GetMovingSpeedMultiplier();
599 cfg->m_Camera.projection_mode = m_canvas->GetProjectionMode();
600
601 if( EDA_3D_CONTROLLER* ctrlTool = GetToolManager()->GetTool<EDA_3D_CONTROLLER>() )
602 cfg->m_Camera.rotation_increment = ctrlTool->GetRotationIncrement();
603 }
604}
605
606
608{
609 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::CommonSettingsChanged" ) );
610
611 // Regen menu bars, etc
613
616
617 m_appearancePanel->CommonSettingsChanged();
618
619 NewDisplay( true );
620}
621
622
624{
626
627 SetTitle( _( "3D Viewer" ) );
629
631 {
632 wxAuiPaneInfo& lm_pane_info = m_auimgr.GetPane( m_appearancePanel );
633 lm_pane_info.Caption( _( "Appearance" ) );
634 }
635
636 SetStatusText( wxEmptyString, ACTIVITY );
637 SetStatusText( wxEmptyString, HOVERED_ITEM );
638}
639
640
642{
643 wxAuiPaneInfo& layersManager = m_auimgr.GetPane( "LayersManager" );
644
646 {
647 // show auxiliary Vertical layers and visibility manager toolbar
648 cfg->m_AuiPanels.show_layer_manager = !cfg->m_AuiPanels.show_layer_manager;
649
650 layersManager.Show( cfg->m_AuiPanels.show_layer_manager );
651
652 if( cfg->m_AuiPanels.show_layer_manager )
653 {
654 SetAuiPaneSize( m_auimgr, layersManager, cfg->m_AuiPanels.right_panel_width, -1 );
655 }
656 else
657 {
658 cfg->m_AuiPanels.right_panel_width = m_appearancePanel->GetSize().x;
659 m_auimgr.Update();
660 }
661 }
662}
663
664
666{
668 m_appearancePanel->OnDarkModeToggle();
669}
670
671
673{
674 wxString fullFileName;
675
677 {
678 if( !getExportFileName( aFormat, fullFileName ) )
679 return;
680 }
681
682 wxImage screenshotImage = captureCurrentViewScreenshot();
683
684 if( screenshotImage.IsOk() )
685 {
686 saveOrCopyImage( screenshotImage, aFormat, fullFileName );
687 }
688}
689
690
692{
693 // Ensure we have the latest 3D view (remember 3D view is buffered)
694 // Also ensure any highlighted item is not highlighted when creating screen shot
696 bool original_highlight = cfg.highlight_on_rollover;
697 cfg.highlight_on_rollover = false;
698
699 m_canvas->DoRePaint(); // init first buffer
700 m_canvas->DoRePaint(); // init second buffer
701
702 wxImage screenshotImage;
703
704 if( m_canvas )
705 {
706 // Build image from the 3D buffer
707 wxWindowUpdateLocker noUpdates( this );
708 m_canvas->GetScreenshot( screenshotImage );
709 }
710
711 // Restore highlight setting
712 cfg.highlight_on_rollover = original_highlight;
713
714 return screenshotImage;
715}
716
717
719{
720 wxString fullFileName;
721
723 {
724 if( !getExportFileName( aFormat, fullFileName ) )
725 return;
726 }
727
728 wxImage screenshotImage = captureScreenshot( aSize );
729
730 if( screenshotImage.IsOk() )
731 {
732 saveOrCopyImage( screenshotImage, aFormat, fullFileName );
733 }
734}
735
736
738{
739 // Create combined wildcard for both formats
740 const wxString wildcard = FILEEXT::JpegFileWildcard() + "|" + FILEEXT::PngFileWildcard();
741
744
745 // Set default extension based on current format
746 const wxString defaultExt = ( aFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG ) ?
748 m_defaultSaveScreenshotFileName.SetExt( defaultExt );
749
750 wxFileDialog dlg( this, _( "3D Image File Name" ),
752 m_defaultSaveScreenshotFileName.GetFullName(), wildcard,
753 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
754
756
757 // Set initial filter index based on current format
758 dlg.SetFilterIndex( ( aFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG ) ? 0 : 1 );
759
760 if( dlg.ShowModal() == wxID_CANCEL )
761 return false;
762
763 m_defaultSaveScreenshotFileName = dlg.GetPath();
764
765 // Determine format based on file extension first
766 wxString fileExt = m_defaultSaveScreenshotFileName.GetExt().Lower();
767 EDA_3D_VIEWER_EXPORT_FORMAT detectedFormat;
768 bool formatDetected = false;
769
770 if( fileExt == wxT("jpg") || fileExt == wxT("jpeg") )
771 {
772 detectedFormat = EDA_3D_VIEWER_EXPORT_FORMAT::JPEG;
773 formatDetected = true;
774 }
775 else if( fileExt == wxT("png") )
776 {
777 detectedFormat = EDA_3D_VIEWER_EXPORT_FORMAT::PNG;
778 formatDetected = true;
779 }
780
781 // If format can't be determined from extension, use dropdown selection
782 if( !formatDetected )
783 {
784 int filterIndex = dlg.GetFilterIndex();
785 detectedFormat = ( filterIndex == 0 ) ? EDA_3D_VIEWER_EXPORT_FORMAT::JPEG :
787
788 // Append appropriate extension
789 const wxString ext = ( detectedFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG ) ?
792 }
793
794 // Update the format parameter
795 aFormat = detectedFormat;
796
797 // Check directory permissions using the updated filename
798 wxFileName fn = m_defaultSaveScreenshotFileName;
799
800 if( !fn.IsDirWritable() )
801 {
802 wxString msg;
803 msg.Printf( _( "Insufficient permissions to save file '%s'." ),
804 m_defaultSaveScreenshotFileName.GetFullPath() );
805 wxMessageBox( msg, _( "Error" ), wxOK | wxICON_ERROR, this );
806 return false;
807 }
808
809 fullFileName = m_defaultSaveScreenshotFileName.GetFullPath();
810 return true;
811}
812
813
814wxImage EDA_3D_VIEWER_FRAME::captureScreenshot( const wxSize& aSize )
815{
817 camera.SetCurWindowSize( aSize );
818
820 EDA_3D_VIEWER_SETTINGS* backupCfg = m_boardAdapter.m_Cfg;
821
822 auto configRestorer = std::unique_ptr<void, std::function<void(void*)>>(
823 reinterpret_cast<void*>(1),
824 [&](void*) { m_boardAdapter.m_Cfg = backupCfg; }
825 );
826
827 if( cfg )
828 m_boardAdapter.m_Cfg = cfg;
829
830 if( cfg && cfg->m_Render.engine == RENDER_ENGINE::RAYTRACING )
831 return captureRaytracingScreenshot( m_boardAdapter, camera, aSize );
832 else
833 return captureOpenGLScreenshot( m_boardAdapter, camera, aSize );
834}
835
836
838{
840
841 if( cfg )
842 aAdapter.m_Cfg = cfg;
843}
844
845
846wxImage EDA_3D_VIEWER_FRAME::captureRaytracingScreenshot( BOARD_ADAPTER& aAdapter, TRACK_BALL& aCamera, const wxSize& aSize )
847{
848 BOARD_ADAPTER tempadapter;
849 tempadapter.SetBoard( GetBoard() );
850 tempadapter.m_Cfg = aAdapter.m_Cfg;
851 tempadapter.InitSettings( nullptr, nullptr );
852 tempadapter.Set3dCacheManager( aAdapter.Get3dCacheManager() );
853
854 RENDER_3D_RAYTRACE_RAM raytrace( tempadapter, aCamera );
855 raytrace.SetCurWindowSize( aSize );
856
857 while( raytrace.Redraw( false, nullptr, nullptr ) );
858
859 uint8_t* rgbaBuffer = raytrace.GetBuffer();
860 wxSize realSize = raytrace.GetRealBufferSize();
861
862 if( !rgbaBuffer )
863 return wxImage();
864
865 return convertRGBAToImage( rgbaBuffer, realSize );
866}
867
868
869wxImage EDA_3D_VIEWER_FRAME::convertRGBAToImage( uint8_t* aRGBABuffer, const wxSize& aRealSize )
870{
871 const unsigned int wxh = aRealSize.x * aRealSize.y;
872
873 unsigned char* rgbBuffer = (unsigned char*) malloc( wxh * 3 );
874 unsigned char* alphaBuffer = (unsigned char*) malloc( wxh );
875
876 unsigned char* rgbaPtr = aRGBABuffer;
877 unsigned char* rgbPtr = rgbBuffer;
878 unsigned char* alphaPtr = alphaBuffer;
879
880 for( int y = 0; y < aRealSize.y; y++ )
881 {
882 for( int x = 0; x < aRealSize.x; x++ )
883 {
884 rgbPtr[0] = rgbaPtr[0];
885 rgbPtr[1] = rgbaPtr[1];
886 rgbPtr[2] = rgbaPtr[2];
887 alphaPtr[0] = rgbaPtr[3];
888
889 rgbaPtr += 4;
890 rgbPtr += 3;
891 alphaPtr += 1;
892 }
893 }
894
895 wxImage screenshotImage;
896 screenshotImage.Create( aRealSize );
897 screenshotImage.SetData( rgbBuffer );
898 screenshotImage.SetAlpha( alphaBuffer );
899 return screenshotImage.Mirror( false );
900}
901
902
903wxImage EDA_3D_VIEWER_FRAME::captureOpenGLScreenshot( BOARD_ADAPTER& aAdapter, TRACK_BALL& aCamera, const wxSize& aSize )
904{
905 EDA_3D_VIEWER_SETTINGS* cfg = aAdapter.m_Cfg;
906 ANTIALIASING_MODE aaMode = cfg ? static_cast<ANTIALIASING_MODE>( cfg->m_Render.opengl_AA_mode )
908
909 wxFrame temp( this, wxID_ANY, wxEmptyString, wxDefaultPosition, aSize, wxFRAME_NO_TASKBAR );
910 temp.Hide();
911 BOARD_ADAPTER tempadapter;
912 tempadapter.SetBoard( GetBoard() );
913 tempadapter.m_Cfg = aAdapter.m_Cfg;
914 tempadapter.InitSettings( nullptr, nullptr );
915 tempadapter.Set3dCacheManager( aAdapter.Get3dCacheManager() );
916
917 auto canvas = std::make_unique<EDA_3D_CANVAS>( &temp,
918 OGL_ATT_LIST::GetAttributesList( aaMode, true ),
919 tempadapter, aCamera,
920 aAdapter.Get3dCacheManager() );
921
922 canvas->SetSize( aSize );
923 configureCanvas( canvas, cfg );
924 wxWindowUpdateLocker noUpdates( this );
925
926 // Temporarily disable highlight during screenshot
928 bool original_highlight = renderCfg.highlight_on_rollover;
929 bool original_navigator = renderCfg.show_navigator;
930 renderCfg.show_navigator = false;
931 renderCfg.highlight_on_rollover = false;
932
933 std::vector<unsigned char> buffer(aSize.x * aSize.y * 4); // RGBA format
934 canvas->RenderToFrameBuffer( buffer.data(), aSize.x, aSize.y );
935 wxImage result = convertRGBAToImage( buffer.data(), aSize );
936
937 // Restore highlight setting
938 renderCfg.highlight_on_rollover = original_highlight;
939 renderCfg.show_navigator = original_navigator;
940
941 return result;
942}
943
944
945void EDA_3D_VIEWER_FRAME::configureCanvas( std::unique_ptr<EDA_3D_CANVAS>& aCanvas, EDA_3D_VIEWER_SETTINGS* aCfg )
946{
947 if( aCfg )
948 {
949 aCanvas->SetAnimationEnabled( aCfg->m_Camera.animation_enabled );
950 aCanvas->SetMovingSpeedMultiplier( aCfg->m_Camera.moving_speed_multiplier );
951 aCanvas->SetProjectionMode( aCfg->m_Camera.projection_mode );
952 }
953
954 aCanvas->SetVcSettings( EDA_DRAW_PANEL_GAL::GetVcSettings() );
955}
956
957
958void EDA_3D_VIEWER_FRAME::saveOrCopyImage( const wxImage& aScreenshotImage, EDA_3D_VIEWER_EXPORT_FORMAT aFormat, const wxString& aFullFileName )
959{
961 {
962 copyImageToClipboard( aScreenshotImage );
963 }
964 else
965 {
966 saveImageToFile( aScreenshotImage, aFormat, aFullFileName );
967 }
968}
969
970
971void EDA_3D_VIEWER_FRAME::copyImageToClipboard( const wxImage& aScreenshotImage )
972{
973 wxBitmap bitmap( aScreenshotImage );
974 wxLogNull doNotLog;
975
976 if( wxTheClipboard->Open() )
977 {
978 wxBitmapDataObject* dobjBmp = new wxBitmapDataObject( bitmap );
979
980 if( !wxTheClipboard->SetData( dobjBmp ) )
981 wxMessageBox( _( "Failed to copy image to clipboard" ) );
982
983 wxTheClipboard->Flush();
984 wxTheClipboard->Close();
985 }
986}
987
988
989void EDA_3D_VIEWER_FRAME::saveImageToFile( const wxImage& aScreenshotImage, EDA_3D_VIEWER_EXPORT_FORMAT aFormat, const wxString& aFullFileName )
990{
991 bool fmt_is_jpeg = ( aFormat == EDA_3D_VIEWER_EXPORT_FORMAT::JPEG );
992
993 if( !aScreenshotImage.SaveFile( aFullFileName, fmt_is_jpeg ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG ) )
994 {
995 wxMessageBox( _( "Can't save file" ) );
996 }
997}
998
999
1001{
1002 wxLogTrace( m_logTrace, wxT( "EDA_3D_VIEWER_FRAME::RenderEngineChanged()" ) );
1003
1004 if( m_canvas )
1005 m_canvas->RenderEngineChanged();
1006}
1007
1008
1010{
1011 wxCHECK_RET( m_canvas, wxT( "Cannot load settings to null canvas" ) );
1012
1013 COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
1014
1015 // TODO(JE) use all control options
1016 m_boardAdapter.m_MousewheelPanning = settings->m_Input.scroll_modifier_zoom != 0;
1017}
1018
1019
1021{
1022 m_boardAdapter.m_Cfg = cfg;
1023
1024 m_canvas->SetAnimationEnabled( cfg->m_Camera.animation_enabled );
1025 m_canvas->SetMovingSpeedMultiplier( cfg->m_Camera.moving_speed_multiplier );
1026 m_canvas->SetProjectionMode( cfg->m_Camera.projection_mode );
1027
1028 m_canvas->SetVcSettings( EDA_DRAW_PANEL_GAL::GetVcSettings() );
1029}
@ 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: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.
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 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:294
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:535
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:132
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:162
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:203
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:76
#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:637
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:717
Declaration of the NL_3D_VIEWER_PLUGIN class.
ANTIALIASING_MODE
Anti-aliasing options.
#define _HKI(x)
Definition page_info.cpp:44
BOARD * GetBoard()
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.