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