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