KiCad PCB EDA Suite
Loading...
Searching...
No Matches
eda_3d_canvas.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 The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <gal/opengl/kiglew.h> // Must be included first
26#include <gal/opengl/gl_utils.h>
27#include <wx/tokenzr.h>
28
29#include "../common_ogl/ogl_utils.h"
30#include "eda_3d_canvas.h"
31#include <eda_3d_viewer_frame.h>
34#include <3d_viewer_id.h>
35#include <advanced_config.h>
36#include <build_version.h>
37#include <board.h>
38#include <reporter.h>
40#include <core/profile.h> // To use GetRunningMicroSecs or another profiling utility
41#include <bitmaps.h>
42#include <macros.h>
43#include <pgm_base.h>
46
48
49
57const wxChar* EDA_3D_CANVAS::m_logTrace = wxT( "KI_TRACE_EDA_3D_CANVAS" );
58
59
60// A custom event, used to call DoRePaint during an idle time
61wxDEFINE_EVENT( wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent );
62
63
64BEGIN_EVENT_TABLE( EDA_3D_CANVAS, HIDPI_GL_3D_CANVAS )
65 EVT_PAINT( EDA_3D_CANVAS::OnPaint )
66
67 // mouse events
68 EVT_LEFT_DOWN( EDA_3D_CANVAS::OnLeftDown )
69 EVT_LEFT_UP( EDA_3D_CANVAS::OnLeftUp )
70 EVT_MIDDLE_UP( EDA_3D_CANVAS::OnMiddleUp )
71 EVT_MIDDLE_DOWN( EDA_3D_CANVAS::OnMiddleDown)
72 EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel )
73 EVT_MOTION( EDA_3D_CANVAS::OnMouseMove )
74 EVT_MAGNIFY( EDA_3D_CANVAS::OnMagnify )
75
76 // touch gesture events
77 EVT_GESTURE_ZOOM( wxID_ANY, EDA_3D_CANVAS::OnZoomGesture )
78 EVT_GESTURE_PAN( wxID_ANY, EDA_3D_CANVAS::OnPanGesture )
79 EVT_GESTURE_ROTATE( wxID_ANY, EDA_3D_CANVAS::OnRotateGesture )
80
81 // other events
82 EVT_ERASE_BACKGROUND( EDA_3D_CANVAS::OnEraseBackground )
83 EVT_CUSTOM(wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1, EDA_3D_CANVAS::OnRefreshRequest )
84
86 EVT_SIZE( EDA_3D_CANVAS::OnResize )
87END_EVENT_TABLE()
88
89
90EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const wxGLAttributes& aGLAttribs,
91 BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera,
92 S3D_CACHE* a3DCachePointer ) :
93 HIDPI_GL_3D_CANVAS( EDA_DRAW_PANEL_GAL::GetVcSettings(), aCamera, aParent, aGLAttribs,
94 EDA_3D_CANVAS_ID, wxDefaultPosition,
95 wxDefaultSize, wxFULL_REPAINT_ON_RESIZE ),
96 m_eventDispatcher( nullptr ),
97 m_parentStatusBar( nullptr ),
98 m_parentInfoBar( nullptr ),
99 m_glRC( nullptr ),
100 m_is_opengl_initialized( false ),
101 m_is_opengl_version_supported( true ),
102 m_editing_timeout_timer( this, wxID_HIGHEST + 1 ),
103 m_redraw_trigger_timer( this, wxID_HIGHEST + 2 ),
104 m_render_pivot( false ),
105 m_camera_moving_speed( 1.0f ),
106 m_strtime_camera_movement( 0 ),
107 m_animation_enabled( true ),
108 m_moving_speed_multiplier( 3 ),
109 m_boardAdapter( aBoardAdapter ),
110 m_3d_render( nullptr ),
111 m_opengl_supports_raytracing( true ),
112 m_render_raytracing_was_requested( false ),
113 m_accelerator3DShapes( nullptr ),
114 m_currentRollOverItem( nullptr )
115{
116 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::EDA_3D_CANVAS" ) );
117
118 m_editing_timeout_timer.SetOwner( this );
119 Connect( m_editing_timeout_timer.GetId(), wxEVT_TIMER,
120 wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Editing ), nullptr, this );
121
122 m_redraw_trigger_timer.SetOwner( this );
123 Connect( m_redraw_trigger_timer.GetId(), wxEVT_TIMER,
124 wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Redraw ), nullptr, this );
125
126 m_is_currently_painting.clear();
127
128 m_3d_render_raytracing = new RENDER_3D_RAYTRACE_GL( this, m_boardAdapter, m_camera );
129 m_3d_render_opengl = new RENDER_3D_OPENGL( this, m_boardAdapter, m_camera );
130
131 wxASSERT( m_3d_render_raytracing != nullptr );
132 wxASSERT( m_3d_render_opengl != nullptr );
133
134 auto busy_indicator_factory =
135 []()
136 {
137 return std::make_unique<WX_BUSY_INDICATOR>();
138 };
139
140 m_3d_render_raytracing->SetBusyIndicatorFactory( busy_indicator_factory );
141 m_3d_render_opengl->SetBusyIndicatorFactory( busy_indicator_factory );
142
143 // We always start with the opengl engine (raytracing is avoided due to very
144 // long calculation time)
145 m_3d_render = m_3d_render_opengl;
146
147 m_boardAdapter.ReloadColorSettings();
148
149 wxASSERT( a3DCachePointer != nullptr );
150 m_boardAdapter.Set3dCacheManager( a3DCachePointer );
151
152#if defined( __WXMSW__ )
153 EnableTouchEvents( wxTOUCH_ZOOM_GESTURE | wxTOUCH_ROTATE_GESTURE | wxTOUCH_PAN_GESTURES );
154#elif defined( __WXGTK__ )
155 EnableTouchEvents( wxTOUCH_ZOOM_GESTURE | wxTOUCH_ROTATE_GESTURE );
156#endif
157
158 const wxEventType events[] =
159 {
160 // Binding both EVT_CHAR and EVT_CHAR_HOOK ensures that all key events,
161 // especially special key like arrow keys, are handled by the GAL event dispatcher,
162 // and not sent to GUI without filtering, because they have a default action (scroll)
163 // that must not be called.
164 wxEVT_LEFT_UP, wxEVT_LEFT_DOWN, wxEVT_LEFT_DCLICK,
165 wxEVT_RIGHT_UP, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_DCLICK,
166 wxEVT_MIDDLE_UP, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_DCLICK,
167 wxEVT_MOTION, wxEVT_MOUSEWHEEL, wxEVT_CHAR, wxEVT_CHAR_HOOK,
168 wxEVT_MAGNIFY,
169 wxEVT_MENU_OPEN, wxEVT_MENU_CLOSE, wxEVT_MENU_HIGHLIGHT
170 };
171
172 for( wxEventType eventType : events )
173 Connect( eventType, wxEventHandler( EDA_3D_CANVAS::OnEvent ), nullptr, m_eventDispatcher );
174}
175
176
178{
179 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::~EDA_3D_CANVAS" ) );
180
182 m_accelerator3DShapes = nullptr;
183
185}
186
187
189{
190 if( m_glRC )
191 {
193 gl_mgr->LockCtx( m_glRC, this );
194
196 m_3d_render_raytracing = nullptr;
197
198 delete m_3d_render_opengl;
199 m_3d_render_opengl = nullptr;
200
201 // This is just a copy of a pointer, can safely be set to NULL.
202 m_3d_render = nullptr;
203
204 gl_mgr->UnlockCtx( m_glRC );
205 gl_mgr->DestroyCtx( m_glRC );
206 m_glRC = nullptr;
207 }
208}
209
210
211void EDA_3D_CANVAS::OnCloseWindow( wxCloseEvent& event )
212{
214
215 event.Skip();
216}
217
218
219void EDA_3D_CANVAS::OnResize( wxSizeEvent& event )
220{
222}
223
224
226{
227 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::initializeOpenGL" ) );
228
229 const GLenum err = glewInit();
230
231 if( GLEW_OK != err )
232 {
233 const wxString msgError = (const char*) glewGetErrorString( err );
234
235 wxLogMessage( msgError );
236
237 return false;
238 }
239 else
240 {
241 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::initializeOpenGL Using GLEW version %s" ),
242 From_UTF8( (char*) glewGetString( GLEW_VERSION ) ) );
243 }
244
245 SetOpenGLInfo( (const char*) glGetString( GL_VENDOR ), (const char*) glGetString( GL_RENDERER ),
246 (const char*) glGetString( GL_VERSION ) );
247
248 wxString version = From_UTF8( (char *) glGetString( GL_VERSION ) );
249
250 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL version string %s." ),
251 __WXFUNCTION__, version );
252
253 // Extract OpenGL version from string. This method is used because prior to OpenGL 2,
254 // getting the OpenGL major and minor version as integers didn't exist.
255 wxString tmp;
256
257 wxStringTokenizer tokenizer( version );
258
259 if( tokenizer.HasMoreTokens() )
260 {
261 long major = 0;
262 long minor = 0;
263
264 tmp = tokenizer.GetNextToken();
265
266 tokenizer.SetString( tmp, wxString( wxT( "." ) ) );
267
268 if( tokenizer.HasMoreTokens() )
269 tokenizer.GetNextToken().ToLong( &major );
270
271 if( tokenizer.HasMoreTokens() )
272 tokenizer.GetNextToken().ToLong( &minor );
273
274 if( major < 2 || ( ( major == 2 ) && ( minor < 1 ) ) )
275 {
276 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL ray tracing not supported." ),
277 __WXFUNCTION__ );
278
279 if( GetParent() )
280 {
281 wxCommandEvent evt( wxEVT_MENU, ID_DISABLE_RAY_TRACING );
282 GetParent()->ProcessWindowEvent( evt );
283 }
284
286 }
287
288 if( ( major == 1 ) && ( minor < 5 ) )
289 {
290 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::%s OpenGL not supported." ),
291 __WXFUNCTION__ );
292
294 }
295 }
296
299
300 return true;
301}
302
303
304void EDA_3D_CANVAS::GetScreenshot( wxImage& aDstImage )
305{
306 OglGetScreenshot( aDstImage );
307}
308
309
310void EDA_3D_CANVAS::ReloadRequest( BOARD* aBoard , S3D_CACHE* aCachePointer )
311{
312 if( aCachePointer != nullptr )
313 m_boardAdapter.Set3dCacheManager( aCachePointer );
314
315 if( aBoard != nullptr )
316 m_boardAdapter.SetBoard( aBoard );
317
319
320 if( m_3d_render )
322}
323
324
326{
328
329 if( m_3d_render )
331
333
335}
336
337
339{
341 {
342 wxString msg;
343
344 msg.Printf( wxT( "dx %3.2f" ), m_camera.GetCameraPos().x );
345 m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::X_POS ) );
346
347 msg.Printf( wxT( "dy %3.2f" ), m_camera.GetCameraPos().y );
348 m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::Y_POS ) );
349
350 msg.Printf( wxT( "zoom %3.2f" ), 1 / m_camera.GetZoom() );
351 m_parentStatusBar->SetStatusText( msg,
352 static_cast<int>( EDA_3D_VIEWER_STATUSBAR::ZOOM_LEVEL ) );
353 }
354}
355
356
357void EDA_3D_CANVAS::OnPaint( wxPaintEvent& aEvent )
358{
359 // Please have a look at: https://lists.launchpad.net/kicad-developers/msg25149.html
360 DoRePaint();
361}
362
363
365{
366 if( m_is_currently_painting.test_and_set() )
367 return;
368
369 // SwapBuffer requires the window to be shown before calling
370 if( !IsShownOnScreen() )
371 {
372 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::DoRePaint !IsShown" ) );
374 return;
375 }
376
377 // Because the board to draw is handled by the parent viewer frame,
378 // ensure this parent is still alive. When it is closed before the viewer
379 // frame, a paint event can be generated after the parent is closed,
380 // therefore with invalid board.
381 // This is dependent of the platform.
382 // Especially on OSX, but also on Windows, it frequently happens
383 if( !GetParent()->GetParent()->IsShownOnScreen() )
384 return; // The parent board editor frame is no more alive
385
386 wxString err_messages;
387 INFOBAR_REPORTER warningReporter( m_parentInfoBar );
388 STATUSBAR_REPORTER activityReporter( m_parentStatusBar, EDA_3D_VIEWER_STATUSBAR::ACTIVITY );
389 int64_t start_time = GetRunningMicroSecs();
391
392 // "Makes the OpenGL state that is represented by the OpenGL rendering
393 // context context current, i.e. it will be used by all subsequent OpenGL calls.
394 // This function may only be called when the window is shown on screen"
395
396 // Explicitly create a new rendering context instance for this canvas.
397 if( m_glRC == nullptr )
398 m_glRC = gl_mgr->CreateCtx( this );
399
400 // CreateCtx could and does fail per sentry crash events, lets be graceful
401 if( m_glRC == nullptr )
402 {
403 warningReporter.Report( _( "OpenGL context creation error" ), RPT_SEVERITY_ERROR );
404 warningReporter.Finalize();
406 return;
407 }
408
409 gl_mgr->LockCtx( m_glRC, this );
410
411 // Set the OpenGL viewport according to the client size of this canvas.
412 // This is done here rather than in a wxSizeEvent handler because our
413 // OpenGL rendering context (and thus viewport setting) is used with
414 // multiple canvases: If we updated the viewport in the wxSizeEvent
415 // handler, changing the size of one canvas causes a viewport setting that
416 // is wrong when next another canvas is repainted.
417 wxSize clientSize = GetNativePixelSize();
418
419 const bool windows_size_changed = m_camera.SetCurWindowSize( clientSize );
420
421 // Initialize openGL if need
423 {
424 if( !initializeOpenGL() )
425 {
426 gl_mgr->UnlockCtx( m_glRC );
428
429 return;
430 }
431
433 {
434 warningReporter.Report( _( "Your OpenGL version is not supported. Minimum required "
435 "is 1.5." ), RPT_SEVERITY_ERROR );
436
437 warningReporter.Finalize();
438 }
439 }
440
442 {
443 glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
444 glClear( GL_COLOR_BUFFER_BIT );
445
446 SwapBuffers();
447
448 gl_mgr->UnlockCtx( m_glRC );
450
451 return;
452 }
453
454 // Don't attend to ray trace if OpenGL doesn't support it.
456 {
459 m_boardAdapter.m_Cfg->m_Render.engine = RENDER_ENGINE::OPENGL;
460 }
461
462 // Check if a raytacing was requested and need to switch to raytracing mode
463 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
464 {
465 const bool was_camera_changed = m_camera.ParametersChanged();
466
467 // It reverts back to OpenGL mode if it was requested a raytracing
468 // render of the current scene. AND the mouse / camera is moving
469 if( ( m_mouse_is_moving || m_camera_is_moving || was_camera_changed
470 || windows_size_changed )
472 {
475 }
476 }
477
478 float curtime_delta_s = 0.0f;
479
481 {
482 const int64_t curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
483 curtime_delta_s = ( curtime_delta / 1e6 ) * m_camera_moving_speed;
484 m_camera.Interpolate( curtime_delta_s );
485
486 if( curtime_delta_s > 1.0f )
487 {
488 m_render_pivot = false;
489 m_camera_is_moving = false;
490 m_mouse_was_moved = true;
491
494 }
495 else
496 {
498 }
499 }
500
501 // It will return true if the render request a new redraw
502 bool requested_redraw = false;
503
504 if( m_3d_render )
505 {
506 try
507 {
508 m_3d_render->SetCurWindowSize( clientSize );
509
510 bool reloadRaytracingForCalculations = false;
511
512 if( m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL
514 {
515 reloadRaytracingForCalculations = true;
516 }
517
519 &activityReporter, &warningReporter );
520
521 // Raytracer renderer is responsible for some features also used by the OpenGL
522 // renderer.
523 // FIXME: Presumably because raytracing renderer reload is called only after current
524 // renderer redraw, the old zoom value stays for a single frame. This is ugly, but only
525 // cosmetic, so I'm not fixing that for now: I don't know how to do this without
526 // reloading twice (maybe it's not too bad of an idea?) or doing a complicated
527 // refactor.
528 if( reloadRaytracingForCalculations )
529 m_3d_render_raytracing->Reload( nullptr, nullptr, true );
530 }
531 catch( std::runtime_error& )
532 {
536 gl_mgr->UnlockCtx( m_glRC );
538 return;
539 }
540 }
541
542 if( m_render_pivot )
543 {
544 const float scale = glm::min( m_camera.GetZoom(), 1.0f );
545 render_pivot( curtime_delta_s, scale );
546 }
547
548 // This will only be enabled by the 3d mouse plugin, so we can leave
549 // it as a simple if statement
551 {
552 const float scale = glm::min( m_camera.GetZoom(), 1.0f );
554 }
555
556 // "Swaps the double-buffer of this window, making the back-buffer the
557 // front-buffer and vice versa, so that the output of the previous OpenGL
558 // commands is displayed on the window."
559 SwapBuffers();
560
561 gl_mgr->UnlockCtx( m_glRC );
562
564 {
565 // Calculation time in milliseconds
566 const double calculation_time = (double)( GetRunningMicroSecs() - start_time ) / 1e3;
567
568 activityReporter.Report( wxString::Format( _( "Last render time %.0f ms" ),
569 calculation_time ) );
570 }
571
572 // This will reset the flag of camera parameters changed
574
575 warningReporter.Finalize();
576
577 if( !err_messages.IsEmpty() )
578 wxLogMessage( err_messages );
579
580 if( ( !m_camera_is_moving ) && requested_redraw )
581 {
582 m_mouse_was_moved = false;
583 Request_refresh( false );
584 }
585
587}
588
589
591{
592 m_eventDispatcher = aEventDispatcher;
593}
594
595
596void EDA_3D_CANVAS::OnEvent( wxEvent& aEvent )
597{
598 if( !m_eventDispatcher )
599 aEvent.Skip();
600 else
602
603 Refresh();
604}
605
606
607void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent& event )
608{
609 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnEraseBackground" ) );
610 // Do nothing, to avoid flashing.
611}
612
613
614void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent& event )
615{
616 wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnMouseWheel" ) );
617
619
621 {
625 }
626}
627
628
629void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event )
630{
631 SetFocus();
632
634 return;
635
636 //m_is_moving_mouse = true;
638
639 float magnification = ( event.GetMagnification() + 1.0f );
640
641 m_camera.Zoom( magnification );
642
645}
646
647
648void EDA_3D_CANVAS::OnZoomGesture( wxZoomGestureEvent& aEvent )
649{
650 SetFocus();
651
652 if( aEvent.IsGestureStart() )
653 {
655 m_camera.SetCurMousePosition( aEvent.GetPosition() );
656 }
657
659 return;
660
662
663 m_camera.Pan( aEvent.GetPosition() );
664 m_camera.SetCurMousePosition( aEvent.GetPosition() );
665
666 m_camera.Zoom( aEvent.GetZoomFactor() / m_gestureLastZoomFactor );
667
668 m_gestureLastZoomFactor = aEvent.GetZoomFactor();
669
672}
673
674
675void EDA_3D_CANVAS::OnPanGesture( wxPanGestureEvent& aEvent )
676{
677 SetFocus();
678
679 if( aEvent.IsGestureStart() )
680 m_camera.SetCurMousePosition( aEvent.GetPosition() );
681
683 return;
684
685 m_camera.Pan( aEvent.GetPosition() );
686 m_camera.SetCurMousePosition( aEvent.GetPosition() );
687
690}
691
692
693void EDA_3D_CANVAS::OnRotateGesture( wxRotateGestureEvent& aEvent )
694{
695 SetFocus();
696
697 if( aEvent.IsGestureStart() )
698 {
700 m_camera.SetCurMousePosition( aEvent.GetPosition() );
701
702 // We don't want to process the first angle
703 return;
704 }
705
707 return;
708
709 m_camera.RotateScreen( m_gestureLastAngle - aEvent.GetRotationAngle() );
710 m_gestureLastAngle = aEvent.GetRotationAngle();
711
714}
715
716
717void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent& event )
718{
720 return; // Prevents using invalid m_3d_render_raytracing data
721
723 return;
724
725 OnMouseMoveCamera( event );
726
728 {
731 // *Do not* reactivate the timer here during the mouse move command:
732 // OnMiddleUp() will do it at the end of mouse drag/move command
733 }
734
735 if( !event.Dragging() && m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
736 {
737 STATUSBAR_REPORTER reporter( m_parentStatusBar, EDA_3D_VIEWER_STATUSBAR::HOVERED_ITEM );
739 BOARD_ITEM* rollOverItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
740
741 auto printNetInfo = []( BOARD_CONNECTED_ITEM* aItem )
742 {
743 return wxString::Format( _( "Net %s\tNet class %s" ), aItem->GetNet()->GetNetname(),
744 aItem->GetNet()->GetNetClass()->GetHumanReadableName() );
745 };
746
747 if( rollOverItem )
748 {
749 wxString msg;
750
751 if( rollOverItem != m_currentRollOverItem )
752 {
754 m_currentRollOverItem = rollOverItem;
755
757 }
758
759 switch( rollOverItem->Type() )
760 {
761 case PCB_PAD_T:
762 {
763 PAD* pad = static_cast<PAD*>( rollOverItem );
764
765 if( !pad->GetNumber().IsEmpty() )
766 msg += wxString::Format( _( "Pad %s\t" ), pad->GetNumber() );
767
768 if( pad->IsOnCopperLayer() )
769 msg += printNetInfo( pad );
770
771 break;
772 }
773
774 case PCB_FOOTPRINT_T:
775 {
776 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( rollOverItem );
777 msg += footprint->GetReference();
778 break;
779 }
780
781 case PCB_TRACE_T:
782 case PCB_VIA_T:
783 case PCB_ARC_T:
784 {
785 PCB_TRACK* track = static_cast<PCB_TRACK*>( rollOverItem );
786 msg += printNetInfo( track );
787 break;
788 }
789
790 case PCB_ZONE_T:
791 {
792 ZONE* zone = static_cast<ZONE*>( rollOverItem );
793
794 if( !zone->GetZoneName().IsEmpty() )
795 {
796 if( zone->GetIsRuleArea() )
797 msg += wxString::Format( _( "Rule area %s\t" ), zone->GetZoneName() );
798 else
799 msg += wxString::Format( _( "Zone %s\t" ), zone->GetZoneName() );
800 }
801
802 if( zone->IsOnCopperLayer() )
803 msg += printNetInfo( zone );
804
805 break;
806 }
807
808 default:
809 break;
810 }
811
812 reporter.Report( msg );
813 }
814 else
815 {
817 && m_boardAdapter.m_Cfg->m_Render.engine == RENDER_ENGINE::OPENGL )
818 {
821
822 reporter.Report( wxEmptyString );
823 }
824
825 m_currentRollOverItem = nullptr;
826 }
827 }
828}
829
830
831void EDA_3D_CANVAS::OnLeftDown( wxMouseEvent& event )
832{
833 SetFocus();
835
836 // Ensure m_camera.m_lastPosition (current mouse position) is up to date for
837 // future drag events (can be not the case when left clicking after
838 // opening a context menu)
839 OnMouseMoveCamera( event );
840
841 if( !event.Dragging() && ( m_3d_render_raytracing != nullptr ) )
842 {
844
845 BOARD_ITEM *intersectedBoardItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
846
847 // !TODO: send a selection item to pcbnew, eg: via kiway?
848 }
849}
850
851
852void EDA_3D_CANVAS::OnLeftUp( wxMouseEvent& event )
853{
855 return;
856
858 {
859 m_mouse_is_moving = false;
861 }
862}
863
864
865void EDA_3D_CANVAS::OnMiddleDown( wxMouseEvent& event )
866{
867 SetFocus();
869}
870
871
872void EDA_3D_CANVAS::OnMiddleUp( wxMouseEvent& event )
873{
875 return;
876
878 {
879 m_mouse_is_moving = false;
881 }
882 else
883 {
885 }
886}
887
888
889void EDA_3D_CANVAS::OnTimerTimeout_Editing( wxTimerEvent& aEvent )
890{
891 if( aEvent.GetId() != m_editing_timeout_timer.GetId() )
892 {
893 aEvent.Skip();
894 return;
895 }
896
897 m_mouse_is_moving = false;
898 m_mouse_was_moved = false;
899
901}
902
903
905{
907}
908
909
911{
912 if( m_3d_render )
914}
915
916
917void EDA_3D_CANVAS::OnTimerTimeout_Redraw( wxTimerEvent& aEvent )
918{
919 if( aEvent.GetId() != m_redraw_trigger_timer.GetId() )
920 {
921 aEvent.Skip();
922 return;
923 }
924
925 Request_refresh( true );
926}
927
928
929void EDA_3D_CANVAS::OnRefreshRequest( wxEvent& aEvent )
930{
931 Refresh();
932}
933
934
935void EDA_3D_CANVAS::Request_refresh( bool aRedrawImmediately )
936{
937 if( aRedrawImmediately )
938 {
939 // Just calling Refresh() does not work always
940 // Using an event to call DoRepaint ensure the repaint code will be executed,
941 // and PostEvent will take priority to other events like mouse movements, keys, etc.
942 // and is executed during the next idle time
943 wxCommandEvent redrawEvent( wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1 );
944 wxPostEvent( this, redrawEvent );
945 }
946 else
947 {
948 // Schedule a timed redraw
949 m_redraw_trigger_timer.Start( 10 , wxTIMER_ONE_SHOT );
950 }
951}
952
953
954void EDA_3D_CANVAS::request_start_moving_camera( float aMovingSpeed, bool aRenderPivot )
955{
956 wxASSERT( aMovingSpeed > FLT_EPSILON );
957
958 // Fast forward the animation if the animation is disabled
960 {
961 m_camera.Interpolate( 1.0f );
964 return;
965 }
966
967 // Map speed multiplier option to actual multiplier value
968 // [1,2,3,4,5] -> [0.25, 0.5, 1, 2, 4]
969 aMovingSpeed *= ( 1 << m_moving_speed_multiplier ) / 8.0f;
970
971 m_render_pivot = aRenderPivot;
972 m_camera_moving_speed = aMovingSpeed;
973
975
978
979 m_camera_is_moving = true;
980
982}
983
984
986{
988
989 float hit_t;
990
991 // Test it with the board bounding box
992 if( m_boardAdapter.GetBBox().Intersect( mouseRay, &hit_t ) )
993 {
994 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
996 m_camera.SetLookAtPos_T1( mouseRay.at( hit_t ) );
998
1000 }
1001}
1002
1003
1005{
1006 if( m_camera_is_moving )
1007 return false;
1008
1009 const float delta_move = m_delta_move_step_factor * m_camera.GetZoom();
1010 const float arrow_moving_time_speed = 8.0f;
1011
1012 switch( aRequestedView )
1013 {
1014 case VIEW3D_TYPE::VIEW3D_PIVOT_CENTER:
1016 return true;
1017
1018 case VIEW3D_TYPE::VIEW3D_PAN_LEFT:
1019 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1021 m_camera.Pan_T1( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
1022 request_start_moving_camera( arrow_moving_time_speed, false );
1023 return true;
1024
1025 case VIEW3D_TYPE::VIEW3D_PAN_RIGHT:
1026 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1028 m_camera.Pan_T1( SFVEC3F( +delta_move, 0.0f, 0.0f ) );
1029 request_start_moving_camera( arrow_moving_time_speed, false );
1030 return true;
1031
1032 case VIEW3D_TYPE::VIEW3D_PAN_UP:
1033 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1035 m_camera.Pan_T1( SFVEC3F( 0.0f, +delta_move, 0.0f ) );
1036 request_start_moving_camera( arrow_moving_time_speed, false );
1037 return true;
1038
1039 case VIEW3D_TYPE::VIEW3D_PAN_DOWN:
1040 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::LINEAR );
1042 m_camera.Pan_T1( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
1043 request_start_moving_camera( arrow_moving_time_speed, false );
1044 return true;
1045
1046 case VIEW3D_TYPE::VIEW3D_FIT_SCREEN:
1047 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1050 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 1 / 1.26f ), 1.26f ) );
1051 return true;
1052
1053 case VIEW3D_TYPE::VIEW3D_ZOOM_IN:
1054 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1056
1057 if( m_camera.Zoom_T1( 1.26f ) ) // 3 steps per doubling
1059
1060 return true;
1061
1062 case VIEW3D_TYPE::VIEW3D_ZOOM_OUT:
1063 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1065
1066 if( m_camera.Zoom_T1( 1/1.26f ) ) // 3 steps per halving
1068
1069 return true;
1070
1071 case VIEW3D_TYPE::VIEW3D_RIGHT:
1072 case VIEW3D_TYPE::VIEW3D_LEFT:
1073 case VIEW3D_TYPE::VIEW3D_FRONT:
1074 case VIEW3D_TYPE::VIEW3D_BACK:
1075 case VIEW3D_TYPE::VIEW3D_FLIP:
1076 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1078 m_camera.ViewCommand_T1( aRequestedView );
1080 return true;
1081
1082 case VIEW3D_TYPE::VIEW3D_TOP:
1083 case VIEW3D_TYPE::VIEW3D_BOTTOM:
1084 m_camera.SetInterpolateMode( CAMERA_INTERPOLATION::BEZIER );
1086 m_camera.ViewCommand_T1( aRequestedView );
1087 request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
1088 return true;
1089
1090 default:
1091 return false;
1092 }
1093}
1094
1095
1097{
1100
1101 switch( cfg->m_Render.engine )
1102 {
1103 case RENDER_ENGINE::OPENGL: m_3d_render = m_3d_render_opengl; break;
1104 case RENDER_ENGINE::RAYTRACING: m_3d_render = m_3d_render_raytracing; break;
1105 default: m_3d_render = nullptr; break;
1106 }
1107
1108 if( m_3d_render )
1110
1111 m_mouse_was_moved = false;
1112
1114}
1115
1116
1118{
1119 SFVEC3F rayOrigin;
1120 SFVEC3F rayDir;
1121
1122 // Generate a ray origin and direction based on current mouser position and camera
1123 m_camera.MakeRayAtCurrentMousePosition( rayOrigin, rayDir );
1124
1125 RAY mouseRay;
1126 mouseRay.Init( rayOrigin, rayDir );
1127
1128 return mouseRay;
1129}
VIEW3D_TYPE
Definition: 3d_enums.h:78
@ ID_CUSTOM_EVENT_1
Definition: 3d_viewer_id.h:53
@ ID_DISABLE_RAY_TRACING
Definition: 3d_viewer_id.h:51
void SetOpenGLInfo(const char *aVendor, const char *aRenderer, const char *aVersion)
A setter for OpenGL info when it's initialized.
Helper class to handle information needed to display 3D board.
Definition: board_adapter.h:73
const BBOX_3D & GetBBox() const noexcept
Get the board outline bounding box.
void ReloadColorSettings() noexcept
void SetBoard(BOARD *aBoard) noexcept
Set current board to be rendered.
EDA_3D_VIEWER_SETTINGS * m_Cfg
bool m_MousewheelPanning
void Set3dCacheManager(S3D_CACHE *aCacheMgr) noexcept
Update the cache manager pointer.
Definition: board_adapter.h:84
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:295
A class used to derive camera objects from.
Definition: camera.h:103
bool Zoom(float aFactor)
Definition: camera.cpp:598
virtual void Pan(const wxPoint &aNewMousePosition)=0
void RotateScreen(float aAngleInRadians)
Rotates the camera in screen plane.
Definition: camera.cpp:651
bool Zoom_T1(float aFactor)
Definition: camera.cpp:629
virtual void Reset_T1()
Definition: camera.cpp:161
virtual void Pan_T1(const SFVEC3F &aDeltaOffsetInc)=0
virtual void Interpolate(float t)
It will update the matrix to interpolate between T0 and T1 values.
Definition: camera.cpp:712
bool SetCurWindowSize(const wxSize &aSize)
Update the windows size of the camera.
Definition: camera.cpp:571
void SetInterpolateMode(CAMERA_INTERPOLATION aInterpolateMode)
Definition: camera.h:264
float GetZoom() const
Definition: camera.h:221
void ResetXYpos_T1()
Definition: camera.cpp:503
bool ViewCommand_T1(VIEW3D_TYPE aRequestedView)
Definition: camera.cpp:110
virtual void SetT0_and_T1_current_T()
This will set T0 and T1 with the current values.
Definition: camera.cpp:698
void MakeRayAtCurrentMousePosition(SFVEC3F &aOutOrigin, SFVEC3F &aOutDirection) const
Make a ray based on the latest mouse position.
Definition: camera.cpp:462
void SetLookAtPos_T1(const SFVEC3F &aLookAtPos)
Definition: camera.h:162
const SFVEC3F & GetCameraPos() const
Definition: camera.h:169
bool ParametersChanged()
Definition: camera.cpp:730
void SetCurMousePosition(const wxPoint &aPosition)
Update the current mouse position without make any new calculations on camera.
Definition: camera.cpp:554
Implement a canvas based on a wxGLCanvas.
Definition: eda_3d_canvas.h:51
RENDER_3D_OPENGL * m_3d_render_opengl
TOOL_DISPATCHER * m_eventDispatcher
void OnEvent(wxEvent &aEvent)
Used to forward events to the canvas from popups, etc.
void OnMagnify(wxMouseEvent &event)
void restart_editingTimeOut_Timer()
Reset the editing timer.
BOARD_ITEM * m_currentRollOverItem
RENDER_3D_BASE * m_3d_render
bool m_is_opengl_initialized
void OnResize(wxSizeEvent &event)
bool m_render3dmousePivot
void OnMouseWheel(wxMouseEvent &event)
WX_INFOBAR * m_parentInfoBar
wxTimer m_editing_timeout_timer
int64_t m_strtime_camera_movement
void OnLeftDown(wxMouseEvent &event)
void OnPanGesture(wxPanGestureEvent &event)
wxGLContext * m_glRC
bool initializeOpenGL()
ACCELERATOR_3D * m_accelerator3DShapes
void OnTimerTimeout_Redraw(wxTimerEvent &event)
wxStatusBar * m_parentStatusBar
void DoRePaint()
The actual function to repaint the canvas.
int m_moving_speed_multiplier
bool m_is_opengl_version_supported
wxTimer m_redraw_trigger_timer
BOARD_ADAPTER & m_boardAdapter
void DisplayStatus()
Update the status bar with the position information.
void render3dmousePivot(float aScale)
Render the 3dmouse pivot cursor.
void OnPaint(wxPaintEvent &aEvent)
void RenderRaytracingRequest()
Request to render the current view in Raytracing mode.
void SetEventDispatcher(TOOL_DISPATCHER *aEventDispatcher)
Set a dispatcher that processes events and forwards them to tools.
bool m_render_raytracing_was_requested
float m_camera_moving_speed
bool m_animation_enabled
void OnLeftUp(wxMouseEvent &event)
void ReloadRequest(BOARD *aBoard=nullptr, S3D_CACHE *aCachePointer=nullptr)
RAY getRayAtCurrentMousePosition()
double m_gestureLastZoomFactor
Used to track gesture events.
void OnCloseWindow(wxCloseEvent &event)
bool SetView3D(VIEW3D_TYPE aRequestedView)
Select a specific 3D view or operation.
void OnMiddleDown(wxMouseEvent &event)
void GetScreenshot(wxImage &aDstImage)
Request a screenshot and output it to the aDstImage.
RENDER_3D_RAYTRACE_GL * m_3d_render_raytracing
void OnZoomGesture(wxZoomGestureEvent &event)
void OnMiddleUp(wxMouseEvent &event)
void render_pivot(float t, float aScale)
Render the pivot cursor.
void request_start_moving_camera(float aMovingSpeed=2.0f, bool aRenderPivot=true)
Start a camera movement.
void RenderEngineChanged()
Notify that the render engine was changed.
std::atomic_flag m_is_currently_painting
void OnRefreshRequest(wxEvent &aEvent)
void OnEraseBackground(wxEraseEvent &event)
void releaseOpenGL()
Free created targets and openGL context.
void Request_refresh(bool aRedrawImmediately=true)
Schedule a refresh update of the canvas.
bool m_opengl_supports_raytracing
void OnMouseMove(wxMouseEvent &event)
void OnRotateGesture(wxRotateGestureEvent &event)
void move_pivot_based_on_cur_mouse_position()
This function hits a ray to the board and start a movement.
double m_gestureLastAngle
void OnTimerTimeout_Editing(wxTimerEvent &event)
void stop_editingTimeOut_Timer()
Stop the editing time so it will not timeout.
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
const wxString & GetReference() const
Definition: footprint.h:620
void UnlockCtx(wxGLContext *aContext)
Allow other canvases to bind an OpenGL context.
void DestroyCtx(wxGLContext *aContext)
Destroy a managed OpenGL context.
void LockCtx(wxGLContext *aContext, wxGLCanvas *aCanvas)
Set a context as current and prevents other canvases from switching it.
wxGLContext * CreateCtx(wxGLCanvas *aCanvas, const wxGLContext *aOther=nullptr)
Create a managed OpenGL context.
static int SetSwapInterval(int aVal)
Attempt to set the OpenGL swap interval.
Definition: gl_utils.h:49
Provides basic 3D controls ( zoom, rotate, translate, ... )
static const float m_delta_move_step_factor
void OnMouseWheelCamera(wxMouseEvent &event, bool aPan)
void OnMouseMoveCamera(wxMouseEvent &event)
virtual wxSize GetNativePixelSize() const
A wrapper for reporting to a WX_INFOBAR UI element.
Definition: wx_infobar.h:326
void Finalize()
Update the infobar with the reported text.
Definition: wx_infobar.cpp:450
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition: wx_infobar.cpp:434
Definition: pad.h:54
GL_CONTEXT_MANAGER * GetGLContextManager()
Definition: pgm_base.h:115
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition: pgm_base.h:125
virtual bool Redraw(bool aIsMoving, REPORTER *aStatusReporter=nullptr, REPORTER *aWarningReporter=nullptr)=0
Redraw the view.
virtual int GetWaitForEditingTimeOut()=0
Give the interface the time (in ms) that it should wait for editing or movements before (this works f...
void ReloadRequest()
virtual void SetCurWindowSize(const wxSize &aSize)=0
Before each render, the canvas will tell the render what is the size of its windows,...
bool IsReloadRequestPending() const
Query if there is a pending reload request.
Object to render the board using openGL.
void SetCurrentRollOverItem(BOARD_ITEM *aRollOverItem)
void Reload(REPORTER *aStatusReporter, REPORTER *aWarningReporter, bool aOnlyLoadCopperAndShapes)
BOARD_ITEM * IntersectBoardItem(const RAY &aRay)
Cache for storing the 3D shapes.
Definition: 3d_cache.h:55
T * GetAppSettings(const wxString &aFilename)
Return a handle to the a given settings by type.
A wrapper for reporting to a specific text location in a statusbar.
Definition: reporter.h:289
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition: reporter.cpp:210
virtual void DispatchWxEvent(wxEvent &aEvent)
Process wxEvents (mostly UI events), translate them to TOOL_EVENTs, and make tools handle those.
Handle a list of polygons defining a copper zone.
Definition: zone.h:73
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:724
const wxString & GetZoneName() const
Definition: zone.h:135
bool IsOnCopperLayer() const override
Definition: zone.cpp:480
#define _(s)
wxDEFINE_EVENT(wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent)
#define EDA_3D_CANVAS_ID
Definition: eda_3d_canvas.h:45
Declaration of the eda_3d_viewer class.
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
This file contains miscellaneous commonly used macros and functions.
void OglGetScreenshot(wxImage &aDstImage)
Get the pixel data of current OpenGL image.
Definition: ogl_utils.cpp:37
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:1073
see class PGM_BASE
int64_t GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
@ RPT_SEVERITY_ERROR
const int scale
wxString From_UTF8(const char *cstring)
bool Intersect(const RAY &aRay, float *t) const
Definition: bbox_3d_ray.cpp:46
Definition: ray.h:63
void Init(const SFVEC3F &o, const SFVEC3F &d)
Definition: ray.cpp:35
SFVEC3F at(float t) const
Definition: ray.h:84
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:107
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
glm::vec3 SFVEC3F
Definition: xv3d_types.h:44