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