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