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