KiCad PCB EDA Suite
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 <mrluzeiro@ua.pt>
5  * Copyright (C) 1992-2020 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 <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 <board.h>
36 #include <reporter.h>
37 #include <gl_context_mgr.h>
38 #include <profile.h> // To use GetRunningMicroSecs or another profiling utility
39 #include <bitmaps.h>
40 #include <macros.h>
41 #include <menus_helpers.h>
42 #include <pgm_base.h>
44 #include <tool/tool_dispatcher.h>
45 
47 
48 
56 const wxChar* EDA_3D_CANVAS::m_logTrace = wxT( "KI_TRACE_EDA_3D_CANVAS" );
57 
59 
60 
61 // A custom event, used to call DoRePaint during an idle time
62 wxDEFINE_EVENT( wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent);
63 
64 
65 BEGIN_EVENT_TABLE( EDA_3D_CANVAS, wxGLCanvas )
66  EVT_PAINT( EDA_3D_CANVAS::OnPaint )
67 
68  // mouse events
69  EVT_LEFT_DOWN( EDA_3D_CANVAS::OnLeftDown )
70  EVT_LEFT_UP( EDA_3D_CANVAS::OnLeftUp )
71  EVT_MIDDLE_UP( EDA_3D_CANVAS::OnMiddleUp )
73  EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel )
74  EVT_MOTION( EDA_3D_CANVAS::OnMouseMove )
75 
76 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
77  EVT_MAGNIFY( EDA_3D_CANVAS::OnMagnify )
78 #endif
79 
80  // other events
81  EVT_ERASE_BACKGROUND( EDA_3D_CANVAS::OnEraseBackground )
82  //EVT_REFRESH_CUSTOM_COMMAND( ID_CUSTOM_EVENT_1, EDA_3D_CANVAS::OnRefreshRequest )
83  EVT_CUSTOM(wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1, EDA_3D_CANVAS::OnRefreshRequest )
84 
85  EVT_CLOSE( EDA_3D_CANVAS::OnCloseWindow )
86  EVT_SIZE( EDA_3D_CANVAS::OnResize )
87 END_EVENT_TABLE()
88 
89 
90 EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const int* aAttribList,
91  BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera,
92  S3D_CACHE* a3DCachePointer )
93  : HIDPI_GL_CANVAS( aParent, wxID_ANY, aAttribList, wxDefaultPosition, wxDefaultSize,
94  wxFULL_REPAINT_ON_RESIZE ),
95  m_eventDispatcher( nullptr ),
96  m_parentStatusBar( nullptr ),
97  m_parentInfoBar( nullptr ),
98  m_glRC( nullptr ),
99  m_is_opengl_initialized( false ),
100  m_is_opengl_version_supported( true ),
101  m_mouse_is_moving( false ),
102  m_mouse_was_moved( false ),
103  m_camera_is_moving( false ),
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_camera( aCamera ),
111  m_3d_render( nullptr ),
112  m_opengl_supports_raytracing( true ),
113  m_render_raytracing_was_requested( false ),
114  m_accelerator3DShapes( nullptr ),
115  m_currentRollOverItem( nullptr )
116 {
117  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::EDA_3D_CANVAS" );
118 
119  m_editing_timeout_timer.SetOwner( this );
120  Connect( m_editing_timeout_timer.GetId(), wxEVT_TIMER,
121  wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Editing ), nullptr, this );
122 
123  m_redraw_trigger_timer.SetOwner( this );
124  Connect( m_redraw_trigger_timer.GetId(), wxEVT_TIMER,
125  wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Redraw ), nullptr, this );
126 
127  m_is_currently_painting.clear();
128 
129  m_3d_render_raytracing = new RENDER_3D_RAYTRACE( this, m_boardAdapter, m_camera );
130  m_3d_render_ogl_legacy = new RENDER_3D_LEGACY( this, m_boardAdapter, m_camera );
131 
132  wxASSERT( m_3d_render_raytracing != nullptr );
133  wxASSERT( m_3d_render_ogl_legacy != nullptr );
134 
135  auto busy_indicator_factory = []() { return std::make_unique<WX_BUSY_INDICATOR>(); };
136 
137  m_3d_render_raytracing->SetBusyIndicatorFactory( busy_indicator_factory );
138  m_3d_render_ogl_legacy->SetBusyIndicatorFactory( busy_indicator_factory );
139 
140  RenderEngineChanged();
141 
142  m_boardAdapter.SetColorSettings( Pgm().GetSettingsManager().GetColorSettings() );
143 
144  wxASSERT( a3DCachePointer != nullptr );
145  m_boardAdapter.Set3dCacheManager( a3DCachePointer );
146 
147  const wxEventType events[] =
148  {
149  // Binding both EVT_CHAR and EVT_CHAR_HOOK ensures that all key events,
150  // especially special key like arrow keys, are handled by the GAL event dispatcher,
151  // and not sent to GUI without filtering, because they have a default action (scroll)
152  // that must not be called.
153  wxEVT_LEFT_UP, wxEVT_LEFT_DOWN, wxEVT_LEFT_DCLICK,
154  wxEVT_RIGHT_UP, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_DCLICK,
155  wxEVT_MIDDLE_UP, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_DCLICK,
156  wxEVT_MOTION, wxEVT_MOUSEWHEEL, wxEVT_CHAR, wxEVT_CHAR_HOOK,
157 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
158  wxEVT_MAGNIFY,
159 #endif
160  wxEVT_MENU_OPEN, wxEVT_MENU_CLOSE, wxEVT_MENU_HIGHLIGHT
161  };
162 
163  for( wxEventType eventType : events )
164  Connect( eventType, wxEventHandler( EDA_3D_CANVAS::OnEvent ), nullptr, m_eventDispatcher );
165 }
166 
167 
169 {
170  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::~EDA_3D_CANVAS" );
171 
172  delete m_accelerator3DShapes;
173  m_accelerator3DShapes = nullptr;
174 
175  releaseOpenGL();
176 }
177 
178 
180 {
181  if( m_glRC )
182  {
184 
185  delete m_3d_render_raytracing;
186  m_3d_render_raytracing = nullptr;
187 
188  delete m_3d_render_ogl_legacy;
189  m_3d_render_ogl_legacy = nullptr;
190 
191  // This is just a copy of a pointer, can safely be set to NULL.
192  m_3d_render = nullptr;
193 
196  m_glRC = nullptr;
197  }
198 }
199 
200 
201 void EDA_3D_CANVAS::OnCloseWindow( wxCloseEvent& event )
202 {
203  releaseOpenGL();
204 
205  event.Skip();
206 }
207 
208 
209 void EDA_3D_CANVAS::OnResize( wxSizeEvent& event )
210 {
211  this->Request_refresh();
212 }
213 
214 
216 {
217  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::initializeOpenGL" );
218 
219  const GLenum err = glewInit();
220 
221  if( GLEW_OK != err )
222  {
223  const wxString msgError = (const char*) glewGetErrorString( err );
224 
225  wxLogMessage( msgError );
226 
227  return false;
228  }
229  else
230  {
231  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::initializeOpenGL Using GLEW version %s",
232  FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) );
233  }
234 
235  wxString version = FROM_UTF8( (char *) glGetString( GL_VERSION ) );
236 
237  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::%s OpenGL version string %s.",
238  __WXFUNCTION__, version );
239 
240  // Extract OpenGL version from string. This method is used because prior to OpenGL 2,
241  // getting the OpenGL major and minor version as integers didn't exist.
242  wxString tmp;
243 
244  wxStringTokenizer tokenizer( version );
245 
246  if( tokenizer.HasMoreTokens() )
247  {
248  long major = 0;
249  long minor = 0;
250 
251  tmp = tokenizer.GetNextToken();
252 
253  tokenizer.SetString( tmp, wxString( "." ) );
254 
255  if( tokenizer.HasMoreTokens() )
256  tokenizer.GetNextToken().ToLong( &major );
257 
258  if( tokenizer.HasMoreTokens() )
259  tokenizer.GetNextToken().ToLong( &minor );
260 
261  if( major < 2 || ( ( major == 2 ) && ( minor < 1 ) ) )
262  {
263  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::%s OpenGL ray tracing not supported.",
264  __WXFUNCTION__ );
265 
266  if( GetParent() )
267  {
268  wxCommandEvent evt( wxEVT_MENU, ID_DISABLE_RAY_TRACING );
269  GetParent()->ProcessWindowEvent( evt );
270  }
271 
273  }
274 
275  if( ( major == 1 ) && ( minor < 5 ) )
276  {
277  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::%s OpenGL not supported.", __WXFUNCTION__ );
278 
280  }
281  }
282 
285 
286  return true;
287 }
288 
289 
290 void EDA_3D_CANVAS::GetScreenshot( wxImage& aDstImage )
291 {
292  OglGetScreenshot( aDstImage );
293 }
294 
295 
296 void EDA_3D_CANVAS::ReloadRequest( BOARD* aBoard , S3D_CACHE* aCachePointer )
297 {
298  if( aCachePointer != nullptr )
299  m_boardAdapter.Set3dCacheManager( aCachePointer );
300 
301  if( aBoard != nullptr )
302  m_boardAdapter.SetBoard( aBoard );
303 
304  m_boardAdapter.SetColorSettings( Pgm().GetSettingsManager().GetColorSettings() );
305 
306  if( m_3d_render )
308 }
309 
310 
312 {
314 
315  if( m_3d_render )
317 
319 
320  Request_refresh();
321 }
322 
323 
325 {
326  if( m_parentStatusBar )
327  {
328  wxString msg;
329 
330  msg.Printf( "dx %3.2f", m_camera.GetCameraPos().x );
331  m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::X_POS ) );
332 
333  msg.Printf( "dy %3.2f", m_camera.GetCameraPos().y );
334  m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::Y_POS ) );
335  }
336 }
337 
338 
339 void EDA_3D_CANVAS::OnPaint( wxPaintEvent& aEvent )
340 {
341  // Please have a look at: https://lists.launchpad.net/kicad-developers/msg25149.html
342  DoRePaint();
343 }
344 
345 
347 {
348  if( m_is_currently_painting.test_and_set() )
349  return;
350 
351  // SwapBuffer requires the window to be shown before calling
352  if( !IsShownOnScreen() )
353  {
354  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::DoRePaint !IsShown" );
355  m_is_currently_painting.clear();
356  return;
357  }
358 
359  // Because the board to draw is handled by the parent viewer frame,
360  // ensure this parent is still alive. When it is closed before the viewer
361  // frame, a paint event can be generated after the parent is closed,
362  // therefore with invalid board.
363  // This is dependent of the platform.
364  // Especially on OSX, but also on Windows, it frequently happens
365  if( !GetParent()->GetParent()->IsShown() )
366  return; // The parent board editor frame is no more alive
367 
368  wxString err_messages;
369 
370  // !TODO: implement error reporter
371  INFOBAR_REPORTER warningReporter( m_parentInfoBar );
372  STATUSBAR_REPORTER activityReporter( m_parentStatusBar,
374 
375  unsigned strtime = GetRunningMicroSecs();
376 
377  // "Makes the OpenGL state that is represented by the OpenGL rendering
378  // context context current, i.e. it will be used by all subsequent OpenGL calls.
379  // This function may only be called when the window is shown on screen"
380 
381  // Explicitly create a new rendering context instance for this canvas.
382  if( m_glRC == nullptr )
384 
386 
387  // Set the OpenGL viewport according to the client size of this canvas.
388  // This is done here rather than in a wxSizeEvent handler because our
389  // OpenGL rendering context (and thus viewport setting) is used with
390  // multiple canvases: If we updated the viewport in the wxSizeEvent
391  // handler, changing the size of one canvas causes a viewport setting that
392  // is wrong when next another canvas is repainted.
393  wxSize clientSize = GetNativePixelSize();
394 
395  const bool windows_size_changed = m_camera.SetCurWindowSize( clientSize );
396 
397  // Initialize openGL if need
399  {
400  if( !initializeOpenGL() )
401  {
403  m_is_currently_painting.clear();
404 
405  return;
406  }
407 
409  {
410  warningReporter.Report( _( "Your OpenGL version is not supported. Minimum required "
411  "is 1.5." ), RPT_SEVERITY_ERROR );
412 
413  warningReporter.Finalize();
414  }
415  }
416 
418  {
419  glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
420  glClear( GL_COLOR_BUFFER_BIT );
421 
422  SwapBuffers();
423 
425  m_is_currently_painting.clear();
426 
427  return;
428  }
429 
430  // Don't attend to ray trace if OpenGL doesn't support it.
432  {
436  }
437 
438  // Check if a raytacing was requested and need to switch to raytracing mode
440  {
441  const bool was_camera_changed = m_camera.ParametersChanged();
442 
443  // It reverts back to OpenGL mode if it was requested a raytracing
444  // render of the current scene. AND the mouse / camera is moving
445  if( ( m_mouse_is_moving || m_camera_is_moving || was_camera_changed
446  || windows_size_changed )
448  {
451  }
452  }
453 
454  float curtime_delta_s = 0.0f;
455 
456  if( m_camera_is_moving )
457  {
458  const unsigned curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
459  curtime_delta_s = (curtime_delta / 1e6) * m_camera_moving_speed;
460  m_camera.Interpolate( curtime_delta_s );
461 
462  if( curtime_delta_s > 1.0f )
463  {
464  m_render_pivot = false;
465  m_camera_is_moving = false;
466  m_mouse_was_moved = true;
467 
469  DisplayStatus();
470  }
471  else
472  {
473  Request_refresh();
474  }
475  }
476 
477  // It will return true if the render request a new redraw
478  bool requested_redraw = false;
479 
480  if( m_3d_render )
481  {
482  try
483  {
484  m_3d_render->SetCurWindowSize( clientSize );
485 
486  bool reloadRaytracingForIntersectionCalculations = false;
487 
490  {
491  reloadRaytracingForIntersectionCalculations = true;
492  }
493 
494  requested_redraw = m_3d_render->Redraw( m_mouse_was_moved || m_camera_is_moving,
495  &activityReporter, &warningReporter );
496 
497  if( reloadRaytracingForIntersectionCalculations )
498  m_3d_render_raytracing->Reload( nullptr, nullptr, true );
499  }
500  catch( std::runtime_error& )
501  {
504  m_is_opengl_initialized = false;
506  m_is_currently_painting.clear();
507  return;
508  }
509  }
510 
511  if( m_render_pivot )
512  {
513  const float scale = glm::min( m_camera.ZoomGet(), 1.0f );
514  render_pivot( curtime_delta_s, scale * scale );
515  }
516 
517  // "Swaps the double-buffer of this window, making the back-buffer the
518  // front-buffer and vice versa, so that the output of the previous OpenGL
519  // commands is displayed on the window."
520  SwapBuffers();
521 
523 
524  if( !activityReporter.HasMessage() )
525  {
527  {
528  // Calculation time in milliseconds
529  const double calculation_time = (double)( GetRunningMicroSecs() - strtime) / 1e3;
530 
531  activityReporter.Report( wxString::Format( _( "Last render time %.0f ms" ),
532  calculation_time ) );
533  }
534  }
535 
536  // This will reset the flag of camera parameters changed
538 
539  warningReporter.Finalize();
540 
541  if( !err_messages.IsEmpty() )
542  wxLogMessage( err_messages );
543 
544  if( ( !m_camera_is_moving ) && requested_redraw )
545  {
546  m_mouse_was_moved = false;
547  Request_refresh( false );
548  }
549 
550  m_is_currently_painting.clear();
551 }
552 
553 
555 {
556  m_eventDispatcher = aEventDispatcher;
557 }
558 
559 
560 void EDA_3D_CANVAS::OnEvent( wxEvent& aEvent )
561 {
562  if( !m_eventDispatcher )
563  aEvent.Skip();
564  else
566 
567  Refresh();
568 }
569 
570 
571 void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent& event )
572 {
573  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::OnEraseBackground" );
574  // Do nothing, to avoid flashing.
575 }
576 
577 
578 void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent& event )
579 {
580  bool mouseActivity = false;
581 
582  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::OnMouseWheel" );
583 
584  if( m_camera_is_moving )
585  return;
586 
587  float delta_move = m_delta_move_step_factor * m_camera.ZoomGet();
588 
590  delta_move *= 0.01f * event.GetWheelRotation();
591  else
592  if( event.GetWheelRotation() < 0 )
593  delta_move = -delta_move;
594 
595  // mousewheel_panning enabled:
596  // wheel -> pan;
597  // wheel + shift -> horizontal scrolling;
598  // wheel + ctrl -> zooming;
599  // mousewheel_panning disabled:
600  // wheel + shift -> vertical scrolling;
601  // wheel + ctrl -> horizontal scrolling;
602  // wheel -> zooming.
603 
604  if( m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) && !event.ControlDown() )
605  {
606  if( event.GetWheelAxis() == wxMOUSE_WHEEL_HORIZONTAL || event.ShiftDown() )
607  m_camera.Pan( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
608  else
609  m_camera.Pan( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
610 
611  mouseActivity = true;
612  }
613  else if( event.ShiftDown() && !m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) )
614  {
615  m_camera.Pan( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
616  mouseActivity = true;
617  }
618  else if( event.ControlDown() && !m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) )
619  {
620  m_camera.Pan( SFVEC3F( delta_move, 0.0f, 0.0f ) );
621  mouseActivity = true;
622  }
623  else
624  {
625  mouseActivity = m_camera.Zoom( event.GetWheelRotation() > 0 ? 1.1f : 1/1.1f );
626  }
627 
628  // If it results on a camera movement
629  if( mouseActivity )
630  {
631  DisplayStatus();
632  Request_refresh();
633 
634  m_mouse_is_moving = true;
635  m_mouse_was_moved = true;
636 
638  }
639 
640  // Update the cursor current mouse position on the camera
641  m_camera.SetCurMousePosition( event.GetPosition() );
642 }
643 
644 
645 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
646 void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event )
647 {
648  SetFocus();
649 
650  if( m_camera_is_moving )
651  return;
652 
653  //m_is_moving_mouse = true;
655 
656  float magnification = ( event.GetMagnification() + 1.0f );
657 
658  m_camera.Zoom( magnification );
659 
660  DisplayStatus();
661  Request_refresh();
662 }
663 #endif
664 
665 
666 void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent& event )
667 {
668  //wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnMouseMove" ) );
669 
670  if( m_camera_is_moving )
671  return;
672 
674 
675  if( event.Dragging() )
676  {
677  if( event.LeftIsDown() ) // Drag
678  m_camera.Drag( event.GetPosition() );
679  else if( event.MiddleIsDown() ) // Pan
680  m_camera.Pan( event.GetPosition() );
681 
682  m_mouse_is_moving = true;
683  m_mouse_was_moved = true;
684 
685  // orientation has changed, redraw mesh
686  DisplayStatus();
687  Request_refresh();
688  }
689 
690  const wxPoint eventPosition = event.GetPosition();
691  m_camera.SetCurMousePosition( eventPosition );
692 
693  if( !event.Dragging() &&
695  {
697  static_cast<int>( EDA_3D_VIEWER_STATUSBAR::HOVERED_ITEM ) );
698 
699  RAY mouseRay = getRayAtCurrrentMousePosition();
700 
701  BOARD_ITEM *rollOverItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
702 
703  if( rollOverItem )
704  {
705  if( rollOverItem != m_currentRollOverItem )
706  {
708  m_currentRollOverItem = rollOverItem;
709 
710  Request_refresh();
711  }
712 
713  switch( rollOverItem->Type() )
714  {
715  case PCB_PAD_T:
716  {
717  PAD* pad = dynamic_cast<PAD*>( rollOverItem );
718 
719  if( pad && pad->IsOnCopperLayer() )
720  {
721  reporter.Report( wxString::Format( _( "Net %s\tNetClass %s\tPadName %s" ),
722  pad->GetNet()->GetNetname(),
723  pad->GetNet()->GetNetClassName(),
724  pad->GetNumber() ) );
725  }
726  }
727  break;
728 
729  case PCB_FOOTPRINT_T:
730  {
731  FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( rollOverItem );
732 
733  if( footprint )
734  reporter.Report( footprint->GetReference() );
735  }
736  break;
737 
738  case PCB_TRACE_T:
739  case PCB_VIA_T:
740  case PCB_ARC_T:
741  {
742  PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( rollOverItem );
743 
744  if( track )
745  {
746  reporter.Report( wxString::Format( _( "Net %s\tNetClass %s" ),
747  track->GetNet()->GetNetname(),
748  track->GetNet()->GetNetClassName() ) );
749  }
750  }
751  break;
752 
753  case PCB_ZONE_T:
754  {
755  ZONE* zone = dynamic_cast<ZONE*>( rollOverItem );
756 
757  if( zone && zone->IsOnCopperLayer() )
758  {
759  reporter.Report( wxString::Format( _( "Net %s\tNetClass %s" ),
760  zone->GetNet()->GetNetname(),
761  zone->GetNet()->GetNetClassName() ) );
762  }
763  }
764  break;
765 
766  default:
767  break;
768  }
769  }
770  else
771  {
772  if( ( m_currentRollOverItem != nullptr ) &&
774  {
776  Request_refresh();
777 
778  reporter.Report( "" );
779  }
780 
781  m_currentRollOverItem = nullptr;
782  }
783  }
784 }
785 
786 
787 void EDA_3D_CANVAS::OnLeftDown( wxMouseEvent& event )
788 {
789  SetFocus();
791 
792  if( !event.Dragging() && ( m_3d_render_raytracing != nullptr ) )
793  {
794  RAY mouseRay = getRayAtCurrrentMousePosition();
795 
796  BOARD_ITEM *intersectedBoardItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
797 
798  // !TODO: send a selection item to pcbnew, eg: via kiway?
799  }
800 }
801 
802 
803 void EDA_3D_CANVAS::OnLeftUp( wxMouseEvent& event )
804 {
805  if( m_camera_is_moving )
806  return;
807 
808  if( m_mouse_is_moving )
809  {
810  m_mouse_is_moving = false;
812  }
813 }
814 
815 
816 void EDA_3D_CANVAS::OnMiddleDown( wxMouseEvent& event )
817 {
818  SetFocus();
820 }
821 
822 
823 void EDA_3D_CANVAS::OnMiddleUp( wxMouseEvent& event )
824 {
825  if( m_camera_is_moving )
826  return;
827 
828  if( m_mouse_is_moving )
829  {
830  m_mouse_is_moving = false;
832  }
833  else
834  {
836  }
837 }
838 
839 
840 void EDA_3D_CANVAS::OnTimerTimeout_Editing( wxTimerEvent& event )
841 {
842  (void)event;
843 
844  m_mouse_is_moving = false;
845  m_mouse_was_moved = false;
846 
847  Request_refresh();
848 }
849 
850 
852 {
854 }
855 
856 
858 {
859  if( m_3d_render )
860  m_editing_timeout_timer.Start( m_3d_render->GetWaitForEditingTimeOut(), wxTIMER_ONE_SHOT );
861 }
862 
863 
864 void EDA_3D_CANVAS::OnTimerTimeout_Redraw( wxTimerEvent& event )
865 {
866  Request_refresh( true );
867 }
868 
869 
870 void EDA_3D_CANVAS::OnRefreshRequest( wxEvent& aEvent )
871 {
872  DoRePaint();
873 }
874 
875 
876 void EDA_3D_CANVAS::Request_refresh( bool aRedrawImmediately )
877 {
878  if( aRedrawImmediately )
879  {
880  // Just calling Refresh() does not work always
881  // Using an event to call DoRepaint ensure the repaint code will be executed,
882  // and PostEvent will take priority to other events like mouse movements, keys, etc.
883  // and is executed during the next idle time
884  wxCommandEvent redrawEvent( wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1 );
885  wxPostEvent( this, redrawEvent );
886  }
887  else
888  {
889  // Schedule a timed redraw
890  m_redraw_trigger_timer.Start( 10 , wxTIMER_ONE_SHOT );
891  }
892 }
893 
894 
895 void EDA_3D_CANVAS::request_start_moving_camera( float aMovingSpeed, bool aRenderPivot )
896 {
897  wxASSERT( aMovingSpeed > FLT_EPSILON );
898 
899  // Fast forward the animation if the animation is disabled
900  if( !m_animation_enabled )
901  {
902  m_camera.Interpolate( 1.0f );
903  DisplayStatus();
904  Request_refresh();
905  return;
906  }
907 
908  // Map speed multiplier option to actual multiplier value
909  // [1,2,3,4,5] -> [0.25, 0.5, 1, 2, 4]
910  aMovingSpeed *= ( 1 << m_moving_speed_multiplier ) / 8.0f;
911 
912  m_render_pivot = aRenderPivot;
913  m_camera_moving_speed = aMovingSpeed;
914 
916 
917  DisplayStatus();
918  Request_refresh();
919 
920  m_camera_is_moving = true;
921 
923 }
924 
925 
927 {
928  RAY mouseRay = getRayAtCurrrentMousePosition();
929 
930  float hit_t;
931 
932  // Test it with the board bounding box
933  if( m_boardAdapter.GetBBox().Intersect( mouseRay, &hit_t ) )
934  {
937  m_camera.SetLookAtPos_T1( mouseRay.at( hit_t ) );
939 
941  }
942 }
943 
944 
945 bool EDA_3D_CANVAS::SetView3D( int aKeycode )
946 {
947  if( m_camera_is_moving )
948  return false;
949 
950  const float delta_move = m_delta_move_step_factor * m_camera.ZoomGet();
951  const float arrow_moving_time_speed = 8.0f;
952  bool handled = false;
953 
954  switch( aKeycode )
955  {
956  case WXK_SPACE:
958  return true;
959 
960  case WXK_LEFT:
963  m_camera.Pan_T1( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
964  request_start_moving_camera( arrow_moving_time_speed, false );
965  return true;
966 
967  case WXK_RIGHT:
970  m_camera.Pan_T1( SFVEC3F( +delta_move, 0.0f, 0.0f ) );
971  request_start_moving_camera( arrow_moving_time_speed, false );
972  return true;
973 
974  case WXK_UP:
977  m_camera.Pan_T1( SFVEC3F( 0.0f, +delta_move, 0.0f ) );
978  request_start_moving_camera( arrow_moving_time_speed, false );
979  return true;
980 
981  case WXK_DOWN:
984  m_camera.Pan_T1( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
985  request_start_moving_camera( arrow_moving_time_speed, false );
986  return true;
987 
988  case WXK_HOME:
991  m_camera.Reset_T1();
992  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 1/1.26f ), 1.26f ) );
993  return true;
994 
995  case WXK_END:
996  break;
997 
998  case WXK_TAB:
1001  m_camera.RotateZ_T1( glm::radians( 45.0f ) );
1003  handled = true;
1004  break;
1005 
1006  case WXK_F1:
1009 
1010  if( m_camera.Zoom_T1( 1.26f ) ) // 3 steps per doubling
1012 
1013  return true;
1014 
1015  case WXK_F2:
1018 
1019  if( m_camera.Zoom_T1( 1/1.26f ) ) // 3 steps per halving
1021 
1022  return true;
1023 
1024  case ID_VIEW3D_RESET:
1027  m_camera.Reset_T1();
1028  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 0.5f ), 1.125f ) );
1029  return true;
1030 
1031  case ID_VIEW3D_RIGHT:
1034  m_camera.Reset_T1();
1035  m_camera.RotateZ_T1( glm::radians( -90.0f ) );
1036  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1038  return true;
1039 
1040  case ID_VIEW3D_LEFT:
1043  m_camera.Reset_T1();
1044  m_camera.RotateZ_T1( glm::radians( 90.0f ) );
1045  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1047  return true;
1048 
1049  case ID_VIEW3D_FRONT:
1052  m_camera.Reset_T1();
1053  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1055  return true;
1056 
1057  case ID_VIEW3D_BACK:
1060  m_camera.Reset_T1();
1061  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1062 
1063  // The rotation angle should be 180.
1064  // We use 179.999 (180 - epsilon) to avoid a full 360 deg rotation when
1065  // using 180 deg if the previous rotated position was already 180 deg
1066  m_camera.RotateZ_T1( glm::radians( 179.999f ) );
1068  return true;
1069 
1070  case ID_VIEW3D_TOP:
1073  m_camera.Reset_T1();
1074  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 0.5f ), 1.125f ) );
1075  return true;
1076 
1077  case ID_VIEW3D_BOTTOM:
1080  m_camera.Reset_T1();
1081  m_camera.RotateY_T1( glm::radians( 179.999f ) ); // Rotation = 180 - epsilon
1082  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 0.5f ), 1.125f ) );
1083  return true;
1084 
1085  case ID_VIEW3D_FLIP:
1088  m_camera.RotateY_T1( glm::radians( 179.999f ) );
1090  return true;
1091 
1092  default:
1093  return false;
1094  }
1095 
1096  m_mouse_was_moved = true;
1097 
1099 
1100  DisplayStatus();
1101  Request_refresh();
1102 
1103  return handled;
1104 }
1105 
1106 
1108 {
1109  switch( m_boardAdapter.GetRenderEngine() )
1110  {
1113  default: m_3d_render = nullptr; break;
1114  }
1115 
1116  if( m_3d_render )
1118 
1119  m_mouse_was_moved = false;
1120 
1121  Request_refresh();
1122 }
1123 
1124 
1126 {
1127  SFVEC3F rayOrigin;
1128  SFVEC3F rayDir;
1129 
1130  // Generate a ray origin and direction based on current mouser position and camera
1131  m_camera.MakeRayAtCurrrentMousePosition( rayOrigin, rayDir );
1132 
1133  RAY mouseRay;
1134  mouseRay.Init( rayOrigin, rayDir );
1135 
1136  return mouseRay;
1137 }
A wrapper for reporting to a specific text location in a statusbar.
Definition: reporter.h:286
virtual wxSize GetNativePixelSize() const
void SetLookAtPos_T1(const SFVEC3F &aLookAtPos)
Definition: camera.h:125
virtual void Pan(const wxPoint &aNewMousePosition)=0
bool GetFlag(DISPLAY3D_FLG aFlag) const
Get a configuration status of a flag.
void RotateZ_T1(float aAngleInRadians)
Definition: camera.cpp:544
void SetCurrentRollOverItem(BOARD_ITEM *aRollOverItem)
bool IsReloadRequestPending() const
Query if there is a pending reload request.
void OnMouseMove(wxMouseEvent &event)
bool Zoom_T1(float aFactor)
Definition: camera.cpp:485
void OnResize(wxSizeEvent &event)
void ReloadRequest()
void request_start_moving_camera(float aMovingSpeed=2.0f, bool aRenderPivot=true)
Start a camera movement.
void releaseOpenGL()
Free created targets and openGL context.
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
bool m_mouse_is_moving
virtual void Pan_T1(const SFVEC3F &aDeltaOffsetInc)=0
void OnRefreshRequest(wxEvent &aEvent)
unsigned m_strtime_camera_movement
wxDEFINE_EVENT(wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent)
WX_INFOBAR * m_parentInfoBar
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:80
RAY getRayAtCurrrentMousePosition()
wxString GetNetClassName() const
Definition: netinfo.h:108
void stop_editingTimeOut_Timer()
Stop the editing time so it will not timeout.
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
void Init(const SFVEC3F &o, const SFVEC3F &d)
Definition: ray.cpp:41
bool Intersect(const RAY &aRay, float *t) const
Definition: bbox_3d_ray.cpp:46
wxStatusBar * m_parentStatusBar
virtual void SetCurWindowSize(const wxSize &aSize)=0
Before each render, the canvas will tell the render what is the size of its windows,...
wxGLContext * m_glRC
void RotateY_T1(float aAngleInRadians)
Definition: camera.cpp:538
void OnCloseWindow(wxCloseEvent &event)
wxGLCanvas wrapper for HiDPI/Retina support.
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:97
Implement a canvas based on a wxGLCanvas.
Definition: eda_3d_canvas.h:48
wxTimer m_editing_timeout_timer
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
class PAD, a pad in a footprint
Definition: typeinfo.h:89
Definition: ray.h:67
void DisplayStatus()
Update the status bar with the position information.
Cache for storing the 3D shapes.
Definition: 3d_cache.h:52
void Request_refresh(bool aRedrawImmediately=true)
Schedule a refresh update of the canvas.
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
void UnlockCtx(wxGLContext *aContext)
Allow other canvases to bind an OpenGL context.
virtual void Drag(const wxPoint &aNewMousePosition)=0
Calculate a new mouse drag position.
SFVEC3F at(float t) const
Definition: ray.h:89
void DoRePaint()
The actual function to repaint the canvas.
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
This file contains miscellaneous commonly used macros and functions.
void SetBoard(BOARD *aBoard) noexcept
Set current board to be rendered.
BOARD_ADAPTER & m_boardAdapter
void OnLeftDown(wxMouseEvent &event)
float m_camera_moving_speed
RENDER_ENGINE GetRenderEngine() const noexcept
bool m_is_opengl_version_supported
A class used to derive camera objects from.
Definition: camera.h:77
static GL_CONTEXT_MANAGER & Get()
Return the GL_CONTEXT_MANAGER instance (singleton).
void OnLeftUp(wxMouseEvent &event)
virtual int GetWaitForEditingTimeOut()=0
Give the interface the time (in ms) that it should wait for editing or movements before (this works f...
void restart_editingTimeOut_Timer()
Reset the editing timer.
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.
RENDER_3D_LEGACY * m_3d_render_ogl_legacy
void Set3dCacheManager(S3D_CACHE *aCachePointer) noexcept
Update the cache manager pointer.
Definition: board_adapter.h:80
void OnMiddleDown(wxMouseEvent &event)
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
bool m_mouse_was_moved
void OnPaint(wxPaintEvent &aEvent)
Called by a wxPaintEvent event.
void OnEraseBackground(wxEraseEvent &event)
void OnMouseWheel(wxMouseEvent &event)
void RenderEngineChanged()
Notify that the render engine was changed.
RENDER_3D_RAYTRACE * m_3d_render_raytracing
Object to render the board using openGL legacy mode.
virtual void DispatchWxEvent(wxEvent &aEvent)
Process wxEvents (mostly UI events), translate them to TOOL_EVENTs, and make tools handle those.
bool m_camera_is_moving
void SetCurMousePosition(const wxPoint &aPosition)
Update the current mouse position without make any new calculations on camera.
Definition: camera.cpp:419
RENDER_3D_BASE * m_3d_render
EVT_MIDDLE_DOWN(mpWindow::OnMouseMiddleDown) EVT_MOUSEWHEEL(mpWindow
Definition: mathplot.cpp:1586
const wxString & GetNetname() const
Definition: netinfo.h:119
virtual bool Redraw(bool aIsMoving, REPORTER *aStatusReporter=nullptr, REPORTER *aWarningReporter=nullptr)=0
Redraw the view.
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition: reporter.cpp:186
void Reload(REPORTER *aStatusReporter, REPORTER *aWarningReporter, bool aOnlyLoadCopperAndShapes)
const wxString & GetReference() const
Definition: footprint.h:430
#define _(s)
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition: reporter.cpp:209
virtual void Reset_T1()
Definition: camera.cpp:102
Handle a list of polygons defining a copper zone.
Definition: zone.h:56
class ZONE, a copper pour area
Definition: typeinfo.h:105
void Finalize()
Update the infobar with the reported text.
Definition: reporter.cpp:225
wxGLContext * CreateCtx(wxGLCanvas *aCanvas, const wxGLContext *aOther=nullptr)
Create a managed OpenGL context.
void MakeRayAtCurrrentMousePosition(SFVEC3F &aOutOrigin, SFVEC3F &aOutDirection) const
Make a ray based on the latest mouse position.
Definition: camera.cpp:365
void OnTimerTimeout_Redraw(wxTimerEvent &event)
SETTINGS_MANAGER * GetSettingsManager()
float ZoomGet() const
Definition: camera.cpp:505
void SetRenderEngine(RENDER_ENGINE aRenderEngine) noexcept
bool m_animation_enabled
void LockCtx(wxGLContext *aContext, wxGLCanvas *aCanvas)
Set a context as current and prevents other canvases from switching it.
bool Zoom(float aFactor)
Definition: camera.cpp:462
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
BOARD_ITEM * IntersectBoardItem(const RAY &aRay)
bool m_is_opengl_initialized
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
void SetInterpolateMode(CAMERA_INTERPOLATION aInterpolateMode)
Definition: camera.h:198
void OnMiddleUp(wxMouseEvent &event)
bool SetCurWindowSize(const wxSize &aSize)
Update the windows size of the camera.
Definition: camera.cpp:436
const BBOX_3D & GetBBox() const noexcept
Get the board outling bounding box.
TOOL_DISPATCHER * m_eventDispatcher
void GetScreenshot(wxImage &aDstImage)
Request a screenshot and output it to the aDstImage.
int m_moving_speed_multiplier
const int scale
virtual void SetT0_and_T1_current_T()
This will set T0 and T1 with the current values.
Definition: camera.cpp:550
see class PGM_BASE
bool initializeOpenGL()
void OnEvent(wxEvent &aEvent)
Used to forward events to the canvas from popups, etc.
bool m_opengl_supports_raytracing
ACCELERATOR_3D * m_accelerator3DShapes
void move_pivot_based_on_cur_mouse_position()
This function hits a ray to the board and start a movement.
std::atomic_flag m_is_currently_painting
virtual void Interpolate(float t)
It will update the matrix to interpolate between T0 and T1 values.
Definition: camera.cpp:564
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
unsigned GetRunningMicroSecs()
An alternate way to calculate an elapset time (in microsecondes) to class PROF_COUNTER.
glm::vec3 SFVEC3F
Definition: xv3d_types.h:44
bool SetView3D(int aKeycode)
Helper function to call view commands.
bool IsOnCopperLayer() const override
Definition: zone.cpp:224
const SFVEC3F & GetCameraPos() const
Definition: camera.h:132
void OnTimerTimeout_Editing(wxTimerEvent &event)
void ReloadRequest(BOARD *aBoard=nullptr, S3D_CACHE *aCachePointer=nullptr)
bool ParametersChanged()
Definition: camera.cpp:582
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
void DestroyCtx(wxGLContext *aContext)
Destroy a managed OpenGL context.
void SetColorSettings(COLOR_SETTINGS *aSettings) noexcept
void ResetXYpos_T1()
Definition: camera.cpp:400
Definition: pad.h:57
static int SetSwapInterval(int aVal)
Attempts to set the OpenGL swap interval.
Definition: gl_utils.h:40
BOARD_ITEM * m_currentRollOverItem
void OglGetScreenshot(wxImage &aDstImage)
Get the pixel data of current OpenGL image.
Definition: ogl_utils.cpp:37
CAMERA & m_camera
wxTimer m_redraw_trigger_timer
Helper class to handle information needed to display 3D board.
Definition: board_adapter.h:68
static const float m_delta_move_step_factor
void render_pivot(float t, float aScale)
Render the pivot cursor.
A wrapper for reporting to a WX_INFOBAR UI element.
Definition: reporter.h:315
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
void RotateX_T1(float aAngleInRadians)
Definition: camera.cpp:532
bool HasMessage() const override
Returns true if the reporter client is non-empty.
Definition: reporter.cpp:195
bool m_render_raytracing_was_requested