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 
30 #include <gal/opengl/kiglew.h> // Must be included first
31 #include <gl_utils.h>
32 #include <wx/tokenzr.h>
33 
34 #include "../common_ogl/ogl_utils.h"
35 #include "eda_3d_canvas.h"
36 #include <eda_3d_viewer.h>
40 #include <3d_viewer_id.h>
41 #include <board.h>
42 #include <reporter.h>
43 #include <gl_context_mgr.h>
44 #include <profile.h> // To use GetRunningMicroSecs or another profiling utility
45 #include <bitmaps.h>
46 #include <menus_helpers.h>
47 #include <pgm_base.h>
49 #include <tool/tool_dispatcher.h>
50 
52 
53 
61 const wxChar * EDA_3D_CANVAS::m_logTrace = wxT( "KI_TRACE_EDA_3D_CANVAS" );
62 
64 
65 // A custom event, used to call DoRePaint during an idle time
66 wxDEFINE_EVENT( wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent);
67 
68 
69 BEGIN_EVENT_TABLE( EDA_3D_CANVAS, wxGLCanvas )
70  EVT_PAINT( EDA_3D_CANVAS::OnPaint )
71 
72  // mouse events
73  EVT_LEFT_DOWN( EDA_3D_CANVAS::OnLeftDown )
74  EVT_LEFT_UP( EDA_3D_CANVAS::OnLeftUp )
75  EVT_MIDDLE_UP( EDA_3D_CANVAS::OnMiddleUp )
77  EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel )
78  EVT_MOTION( EDA_3D_CANVAS::OnMouseMove )
79 
80 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
81  EVT_MAGNIFY( EDA_3D_CANVAS::OnMagnify )
82 #endif
83 
84  // other events
85  EVT_ERASE_BACKGROUND( EDA_3D_CANVAS::OnEraseBackground )
86  //EVT_REFRESH_CUSTOM_COMMAND( ID_CUSTOM_EVENT_1, EDA_3D_CANVAS::OnRefreshRequest )
87  EVT_CUSTOM(wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1, EDA_3D_CANVAS::OnRefreshRequest )
88 
89  EVT_CLOSE( EDA_3D_CANVAS::OnCloseWindow )
90  EVT_SIZE( EDA_3D_CANVAS::OnResize )
91 END_EVENT_TABLE()
92 
93 
94 EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const int* aAttribList, BOARD* aBoard,
95  BOARD_ADAPTER& aBoardAdapter, CCAMERA& aCamera,
96  S3D_CACHE* a3DCachePointer )
97  : HIDPI_GL_CANVAS( aParent, wxID_ANY, aAttribList, wxDefaultPosition, wxDefaultSize,
98  wxFULL_REPAINT_ON_RESIZE ),
99  m_eventDispatcher( nullptr ),
100  m_parentStatusBar( nullptr ),
101  m_parentInfoBar( nullptr ),
102  m_glRC( nullptr ),
103  m_is_opengl_initialized( false ),
104  m_is_opengl_version_supported( true ),
105  m_mouse_is_moving( false ),
106  m_mouse_was_moved( false ),
107  m_camera_is_moving( false ),
108  m_render_pivot( false ),
109  m_camera_moving_speed( 1.0f ),
110  m_strtime_camera_movement( 0 ),
111  m_animation_enabled( true ),
112  m_moving_speed_multiplier( 3 ),
113  m_boardAdapter( aBoardAdapter ),
114  m_camera( aCamera ),
115  m_3d_render( nullptr ),
116  m_opengl_supports_raytracing( true ),
117  m_render_raytracing_was_requested( false ),
118  m_accelerator3DShapes( nullptr ),
119  m_currentIntersectedBoardItem( nullptr )
120 {
121  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::EDA_3D_CANVAS" );
122 
123  m_editing_timeout_timer.SetOwner( this );
124  Connect( m_editing_timeout_timer.GetId(),
125  wxEVT_TIMER,
126  wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Editing ),
127  NULL,
128  this );
129 
130  m_redraw_trigger_timer.SetOwner( this );
131  Connect( m_redraw_trigger_timer.GetId(),
132  wxEVT_TIMER,
133  wxTimerEventHandler( EDA_3D_CANVAS::OnTimerTimeout_Redraw ),
134  NULL,
135  this );
136 
137  m_is_currently_painting.clear();
138 
139  m_3d_render_raytracing = new C3D_RENDER_RAYTRACING( m_boardAdapter, m_camera );
140  m_3d_render_ogl_legacy = new C3D_RENDER_OGL_LEGACY( m_boardAdapter, m_camera );
141 
142  wxASSERT( m_3d_render_raytracing != NULL );
143  wxASSERT( m_3d_render_ogl_legacy != NULL );
144 
145  auto busy_indicator_factory = []() { return std::make_unique<WX_BUSY_INDICATOR>(); };
146 
147  m_3d_render_raytracing->SetBusyIndicatorFactory( busy_indicator_factory );
148  m_3d_render_ogl_legacy->SetBusyIndicatorFactory( busy_indicator_factory );
149 
150  RenderEngineChanged();
151 
152  wxASSERT( aBoard != NULL );
153  m_boardAdapter.SetBoard( aBoard );
154 
155  m_boardAdapter.SetColorSettings( Pgm().GetSettingsManager().GetColorSettings() );
156 
157  wxASSERT( a3DCachePointer != NULL );
158  m_boardAdapter.Set3DCacheManager( a3DCachePointer );
159 
160  const wxEventType events[] =
161  {
162  // Binding both EVT_CHAR and EVT_CHAR_HOOK ensures that all key events,
163  // especially special key like arrow keys, are handled by the GAL event dispatcher,
164  // and not sent to GUI without filtering, because they have a default action (scroll)
165  // that must not be called.
166  wxEVT_LEFT_UP, wxEVT_LEFT_DOWN, wxEVT_LEFT_DCLICK,
167  wxEVT_RIGHT_UP, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_DCLICK,
168  wxEVT_MIDDLE_UP, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_DCLICK,
169  wxEVT_MOTION, wxEVT_MOUSEWHEEL, wxEVT_CHAR, wxEVT_CHAR_HOOK,
170 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
171  wxEVT_MAGNIFY,
172 #endif
173  wxEVT_MENU_OPEN, wxEVT_MENU_CLOSE, wxEVT_MENU_HIGHLIGHT
174  };
175 
176  for( wxEventType eventType : events )
177  Connect( eventType, wxEventHandler( EDA_3D_CANVAS::OnEvent ), NULL, m_eventDispatcher );
178 }
179 
180 
182 {
183  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::~EDA_3D_CANVAS" );
184 
185  delete m_accelerator3DShapes;
187 
188  releaseOpenGL();
189 }
190 
191 
193 {
194  if( m_glRC )
195  {
197 
198  delete m_3d_render_raytracing;
200 
201  delete m_3d_render_ogl_legacy;
203 
204  // This is just a copy of a pointer, can safely be set to NULL
205  m_3d_render = NULL;
206 
209  m_glRC = NULL;
210  }
211 }
212 
213 
214 void EDA_3D_CANVAS::OnCloseWindow( wxCloseEvent &event )
215 {
216  releaseOpenGL();
217 
218  event.Skip();
219 }
220 
221 
222 void EDA_3D_CANVAS::OnResize( wxSizeEvent &event )
223 {
224  this->Request_refresh();
225 }
226 
227 
229 {
230  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::initializeOpenGL" );
231 
232  const GLenum err = glewInit();
233 
234  if( GLEW_OK != err )
235  {
236  const wxString msgError = (const char*) glewGetErrorString( err );
237 
238  wxLogMessage( msgError );
239 
240  return false;
241  }
242  else
243  {
244  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::initializeOpenGL Using GLEW version %s",
245  FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) );
246  }
247 
248  wxString version = FROM_UTF8( (char *) glGetString( GL_VERSION ) );
249 
250  wxLogTrace( m_logTrace, "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( "." ) );
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, "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, "EDA_3D_CANVAS::%s OpenGL not supported.",
291  __WXFUNCTION__ );
292 
294  }
295  }
296 
299 
300  return true;
301 }
302 
303 
304 void EDA_3D_CANVAS::GetScreenshot( wxImage &aDstImage )
305 {
306  OGL_GetScreenshot( aDstImage );
307 }
308 
309 
310 void EDA_3D_CANVAS::ReloadRequest( BOARD *aBoard , S3D_CACHE *aCachePointer )
311 {
312  if( aCachePointer != NULL )
313  m_boardAdapter.Set3DCacheManager( aCachePointer );
314 
315  if( aBoard != NULL )
316  m_boardAdapter.SetBoard( aBoard );
317 
318  m_boardAdapter.SetColorSettings( Pgm().GetSettingsManager().GetColorSettings() );
319 
320  if( m_3d_render )
322 }
323 
324 
326 {
328 
329  if( m_3d_render )
331 
333  //m_mouse_was_moved = true;
334 
335  Request_refresh();
336 }
337 
338 
340 {
341  if( m_parentStatusBar )
342  {
343  wxString msg;
344 
345  msg.Printf( "dx %3.2f", m_camera.GetCameraPos().x );
346  m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::X_POS ) );
347 
348  msg.Printf( "dy %3.2f", m_camera.GetCameraPos().y );
349  m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::Y_POS ) );
350  }
351 }
352 
353 
354 void EDA_3D_CANVAS::OnPaint( wxPaintEvent& aEvent )
355 {
356  // Please have a look at:
357  // https://lists.launchpad.net/kicad-developers/msg25149.html
358  // wxPaintDC( this );
359  // aEvent.Skip( false );
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, "EDA_3D_CANVAS::DoRePaint !IsShown" );
373  m_is_currently_painting.clear();
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()->IsShown() )
384  return; // The parent board editor frame is no more alive
385 
386  wxString err_messages;
387 
388  // !TODO: implement error reporter
389  //WX_STRING_REPORTER errorReporter( &err_messages );
390  INFOBAR_REPORTER warningReporter( m_parentInfoBar );
391  STATUSBAR_REPORTER activityReporter( m_parentStatusBar,
393 
394  unsigned strtime = GetRunningMicroSecs();
395 
396  // "Makes the OpenGL state that is represented by the OpenGL rendering
397  // context context current, i.e. it will be used by all subsequent OpenGL calls.
398  // This function may only be called when the window is shown on screen"
399 
400  // Explicitly create a new rendering context instance for this canvas.
401  if( m_glRC == NULL )
403 
405 
406  // Set the OpenGL viewport according to the client size of this canvas.
407  // This is done here rather than in a wxSizeEvent handler because our
408  // OpenGL rendering context (and thus viewport setting) is used with
409  // multiple canvases: If we updated the viewport in the wxSizeEvent
410  // handler, changing the size of one canvas causes a viewport setting that
411  // is wrong when next another canvas is repainted.
412  wxSize clientSize = GetNativePixelSize();
413 
414  const bool windows_size_changed = m_camera.SetCurWindowSize( clientSize );
415 
416  // Initialize openGL if need
417  // /////////////////////////////////////////////////////////////////////////
419  {
420  if( !initializeOpenGL() )
421  {
423  m_is_currently_painting.clear();
424 
425  return;
426  }
427 
429  {
430  warningReporter.Report( _( "Your OpenGL version is not supported. Minimum required is 1.5" ),
432 
433  warningReporter.Finalize();
434  }
435  }
436 
438  {
439  glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
440  glClear( GL_COLOR_BUFFER_BIT );
441 
442  SwapBuffers();
443 
445  m_is_currently_painting.clear();
446 
447  return;
448  }
449 
450  // Don't attend to ray trace if OpenGL doesn't support it.
452  {
456  }
457 
458  // Check if a raytacing was requested and need to switch to raytracing mode
460  {
461  const bool was_camera_changed = m_camera.ParametersChanged();
462 
463  // It reverts back to OpenGL mode if it was requested a raytracing
464  // render of the current scene. AND the mouse / camera is moving
465  if( ( m_mouse_is_moving
467  || was_camera_changed
468  || windows_size_changed )
470  {
473  }
474  }
475 
476  float curtime_delta_s = 0.0f;
477 
478  if( m_camera_is_moving )
479  {
480  const unsigned curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
481  curtime_delta_s = (curtime_delta / 1e6) * m_camera_moving_speed;
482  m_camera.Interpolate( curtime_delta_s );
483 
484  if( curtime_delta_s > 1.0f )
485  {
486  m_render_pivot = false;
487  m_camera_is_moving = false;
488  m_mouse_was_moved = true;
489 
491  DisplayStatus();
492  }
493  else
494  {
495  Request_refresh();
496  }
497  }
498 
499  // It will return true if the render request a new redraw
500  bool requested_redraw = false;
501 
502  if( m_3d_render )
503  {
504  try
505  {
506  m_3d_render->SetCurWindowSize( clientSize );
507 
508  bool reloadRaytracingForIntersectionCalculations = false;
509 
512  {
513  reloadRaytracingForIntersectionCalculations = true;
514  }
515 
516  requested_redraw = m_3d_render->Redraw( m_mouse_was_moved || m_camera_is_moving,
517  &activityReporter, &warningReporter );
518 
519  if( reloadRaytracingForIntersectionCalculations )
520  m_3d_render_raytracing->Reload( nullptr, nullptr, true );
521  }
522  catch( std::runtime_error& err )
523  {
526  m_is_opengl_initialized = false;
528  m_is_currently_painting.clear();
529  return;
530  }
531  }
532 
533  if( m_render_pivot )
534  {
535  const float scale = glm::min( m_camera.ZoomGet(), 1.0f );
536  render_pivot( curtime_delta_s, scale * scale );
537  }
538 
539  // "Swaps the double-buffer of this window, making the back-buffer the
540  // front-buffer and vice versa, so that the output of the previous OpenGL
541  // commands is displayed on the window."
542  SwapBuffers();
543 
545 
546  if( !activityReporter.HasMessage() )
547  {
549  {
550  // Calculation time in milliseconds
551  const double calculation_time = (double)( GetRunningMicroSecs() - strtime) / 1e3;
552 
553  activityReporter.Report( wxString::Format( _( "Render time %.0f ms ( %.1f fps)" ),
554  calculation_time, 1000.0 / calculation_time ) );
555  }
556  }
557 
558  // This will reset the flag of camera parameters changed
560 
561  warningReporter.Finalize();
562 
563  if( !err_messages.IsEmpty() )
564  wxLogMessage( err_messages );
565 
566  if( (!m_camera_is_moving) && requested_redraw )
567  {
568  m_mouse_was_moved = false;
569  Request_refresh( false );
570  }
571 
572  m_is_currently_painting.clear();
573 }
574 
575 
577 {
578  m_eventDispatcher = aEventDispatcher;
579 
580  if( m_eventDispatcher )
581  {
582  m_parent->Connect( wxEVT_TOOL,
583  wxCommandEventHandler( TOOL_DISPATCHER::DispatchWxCommand ),
585  }
586  else
587  {
588  // While loop is used to be sure that all event handlers are removed.
589  while( m_parent->Disconnect( wxEVT_TOOL,
590  wxCommandEventHandler( TOOL_DISPATCHER::DispatchWxCommand ),
591  NULL, m_eventDispatcher ) );
592  }
593 }
594 
595 
596 void EDA_3D_CANVAS::OnEvent( wxEvent& aEvent )
597 {
598  if( !m_eventDispatcher )
599  aEvent.Skip();
600  else
602 
603  Refresh();
604 }
605 
606 
607 void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent &event )
608 {
609  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::OnEraseBackground" );
610  // Do nothing, to avoid flashing.
611 }
612 
613 
614 void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent &event )
615 {
616  bool mouseActivity = false;
617 
618  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::OnMouseWheel" );
619 
620  if( m_camera_is_moving )
621  return;
622 
623  float delta_move = m_delta_move_step_factor * m_camera.ZoomGet();
624 
626  delta_move *= 0.01f * event.GetWheelRotation();
627  else
628  if( event.GetWheelRotation() < 0 )
629  delta_move = -delta_move;
630 
631  // mousewheel_panning enabled:
632  // wheel -> pan;
633  // wheel + shift -> horizontal scrolling;
634  // wheel + ctrl -> zooming;
635  // mousewheel_panning disabled:
636  // wheel + shift -> vertical scrolling;
637  // wheel + ctrl -> horizontal scrolling;
638  // wheel -> zooming.
639 
640  if( m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) && !event.ControlDown() )
641  {
642  if( event.GetWheelAxis() == wxMOUSE_WHEEL_HORIZONTAL || event.ShiftDown() )
643  m_camera.Pan( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
644  else
645  m_camera.Pan( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
646 
647  mouseActivity = true;
648  }
649  else if( event.ShiftDown() && !m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) )
650  {
651  m_camera.Pan( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
652  mouseActivity = true;
653  }
654  else if( event.ControlDown() && !m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) )
655  {
656  m_camera.Pan( SFVEC3F( delta_move, 0.0f, 0.0f ) );
657  mouseActivity = true;
658  }
659  else
660  {
661  mouseActivity = m_camera.Zoom( event.GetWheelRotation() > 0 ? 1.1f : 1/1.1f );
662  }
663 
664  // If it results on a camera movement
665  if( mouseActivity )
666  {
667  DisplayStatus();
668  Request_refresh();
669 
670  m_mouse_is_moving = true;
671  m_mouse_was_moved = true;
672 
674  }
675 
676  // Update the cursor current mouse position on the camera
677  m_camera.SetCurMousePosition( event.GetPosition() );
678 }
679 
680 
681 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
682 void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event )
683 {
684  SetFocus();
685 
686  if( m_camera_is_moving )
687  return;
688 
689  //m_is_moving_mouse = true;
691 
692  float magnification = ( event.GetMagnification() + 1.0f );
693 
694  m_camera.Zoom( magnification );
695 
696  DisplayStatus();
697  Request_refresh();
698 }
699 #endif
700 
701 
702 void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent &event )
703 {
704  //wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnMouseMove" ) );
705 
706  if( m_camera_is_moving )
707  return;
708 
710 
711  if( event.Dragging() )
712  {
713  if( event.LeftIsDown() ) // Drag
714  m_camera.Drag( event.GetPosition() );
715  else if( event.MiddleIsDown() ) // Pan
716  m_camera.Pan( event.GetPosition() );
717 
718  m_mouse_is_moving = true;
719  m_mouse_was_moved = true;
720 
721  // orientation has changed, redraw mesh
722  DisplayStatus();
723  Request_refresh();
724  }
725 
726  const wxPoint eventPosition = event.GetPosition();
727  m_camera.SetCurMousePosition( eventPosition );
728 
729  if( !event.Dragging() &&
731  {
732  STATUSBAR_REPORTER activityReporter(
734 
735  RAY mouseRay = getRayAtCurrrentMousePosition();
736 
737  BOARD_ITEM *intersectedBoardItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
738 
739  if( intersectedBoardItem )
740  {
741  if( intersectedBoardItem != m_currentIntersectedBoardItem )
742  {
744  m_currentIntersectedBoardItem = intersectedBoardItem;
745 
746  Request_refresh();
747  }
748 
749  switch( intersectedBoardItem->Type() )
750  {
751  case PCB_PAD_T:
752  {
753  PAD* pad = dynamic_cast<PAD*>( intersectedBoardItem );
754 
755  if( pad && pad->IsOnCopperLayer() )
756  {
757  activityReporter.Report( wxString::Format( _( "Net %s\tNetClass %s\tPadName %s" ),
758  pad->GetNet()->GetNetname(),
759  pad->GetNet()->GetClassName(),
760  pad->GetName() ) );
761  }
762  }
763  break;
764 
765  case PCB_FOOTPRINT_T:
766  {
767  FOOTPRINT* footprint = dynamic_cast<FOOTPRINT *>( intersectedBoardItem );
768 
769  if( footprint )
770  activityReporter.Report( footprint->GetReference() );
771  }
772  break;
773 
774  case PCB_TRACE_T:
775  case PCB_VIA_T:
776  case PCB_ARC_T:
777  {
778  TRACK* track = dynamic_cast<TRACK *>( intersectedBoardItem );
779 
780  if( track )
781  {
782  activityReporter.Report( wxString::Format( _( "Net %s\tNetClass %s" ),
783  track->GetNet()->GetNetname(),
784  track->GetNet()->GetClassName() ) );
785  }
786  }
787  break;
788 
789  case PCB_ZONE_T:
790  {
791  ZONE* zone = dynamic_cast<ZONE*>( intersectedBoardItem );
792 
793  if( zone && zone->IsOnCopperLayer() )
794  {
795  activityReporter.Report( wxString::Format( _( "Net %s\tNetClass %s" ),
796  zone->GetNet()->GetNetname(),
797  zone->GetNet()->GetClassName() ) );
798  }
799  }
800  break;
801 
802  default:
803  break;
804  }
805  }
806  else
807  {
808  if( ( m_currentIntersectedBoardItem != nullptr ) &&
810  {
812  Request_refresh();
813 
814  activityReporter.Report( "" );
815  }
816 
818  }
819  }
820 }
821 
822 
823 void EDA_3D_CANVAS::OnLeftDown( wxMouseEvent &event )
824 {
825  SetFocus();
827 
828  if( !event.Dragging() && ( m_3d_render_raytracing != nullptr ) )
829  {
830  RAY mouseRay = getRayAtCurrrentMousePosition();
831 
832  BOARD_ITEM *intersectedBoardItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
833 
834  // !TODO: send a selection item to pcbnew, eg: via kiway?
835  }
836 }
837 
838 
839 void EDA_3D_CANVAS::OnLeftUp( wxMouseEvent &event )
840 {
841  if( m_camera_is_moving )
842  return;
843 
844  if( m_mouse_is_moving )
845  {
846  m_mouse_is_moving = false;
848  }
849 }
850 
851 
852 void EDA_3D_CANVAS::OnMiddleDown( wxMouseEvent &event )
853 {
854  SetFocus();
856 }
857 
858 
859 void EDA_3D_CANVAS::OnMiddleUp( wxMouseEvent &event )
860 {
861  if( m_camera_is_moving )
862  return;
863 
864  if( m_mouse_is_moving )
865  {
866  m_mouse_is_moving = false;
868  }
869  else
870  {
872  }
873 }
874 
875 
876 void EDA_3D_CANVAS::OnTimerTimeout_Editing( wxTimerEvent &event )
877 {
878  (void)event;
879 
880  m_mouse_is_moving = false;
881  m_mouse_was_moved = false;
882 
883  Request_refresh();
884 }
885 
886 
888 {
890 }
891 
892 
894 {
895  if( m_3d_render )
896  m_editing_timeout_timer.Start( m_3d_render->GetWaitForEditingTimeOut(), wxTIMER_ONE_SHOT );
897 }
898 
899 
900 void EDA_3D_CANVAS::OnTimerTimeout_Redraw( wxTimerEvent &event )
901 {
902  Request_refresh( true );
903 }
904 
905 
906 void EDA_3D_CANVAS::OnRefreshRequest( wxEvent& aEvent )
907 {
908  DoRePaint();
909 }
910 
911 
912 void EDA_3D_CANVAS::Request_refresh( bool aRedrawImmediately )
913 {
914  if( aRedrawImmediately )
915  {
916  // Just calling Refresh() does not work always
917  // Using an event to call DoRepaint ensure the repaint code will be executed,
918  // and PostEvent will take priority to other events like mouse movements, keys, etc.
919  // and is executed during the next idle time
920  wxCommandEvent redrawEvent( wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1 );
921  wxPostEvent( this, redrawEvent );
922  }
923  else
924  {
925  // Schedule a timed redraw
926  m_redraw_trigger_timer.Start( 10 , wxTIMER_ONE_SHOT );
927  }
928 }
929 
930 
931 void EDA_3D_CANVAS::request_start_moving_camera( float aMovingSpeed, bool aRenderPivot )
932 {
933  wxASSERT( aMovingSpeed > FLT_EPSILON );
934 
935  // Fast forward the animation if the animation is disabled
936  if( !m_animation_enabled )
937  {
938  m_camera.Interpolate( 1.0f );
939  DisplayStatus();
940  Request_refresh();
941  return;
942  }
943 
944  // Map speed multipler option to actual multiplier value
945  // [1,2,3,4,5] -> [0.25, 0.5, 1, 2, 4]
946  aMovingSpeed *= ( 1 << m_moving_speed_multiplier ) / 8.0f;
947 
948  m_render_pivot = aRenderPivot;
949  m_camera_moving_speed = aMovingSpeed;
950 
952 
953  DisplayStatus();
954  Request_refresh();
955 
956  m_camera_is_moving = true;
957 
959 }
960 
961 
963 {
964  RAY mouseRay = getRayAtCurrrentMousePosition();
965 
966  float hit_t;
967 
968  // Test it with the board bounding box
969  if( m_boardAdapter.GetBBox3DU().Intersect( mouseRay, &hit_t ) )
970  {
973  m_camera.SetLookAtPos_T1( mouseRay.at( hit_t ) );
975 
977  }
978 }
979 
980 
981 bool EDA_3D_CANVAS::SetView3D( int aKeycode )
982 {
983  if( m_camera_is_moving )
984  return false;
985 
986  const float delta_move = m_delta_move_step_factor * m_camera.ZoomGet();
987  const float arrow_moving_time_speed = 8.0f;
988  bool handled = false;
989 
990  switch( aKeycode )
991  {
992  case WXK_SPACE:
994  return true;
995 
996  case WXK_LEFT:
999  m_camera.Pan_T1( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
1000  request_start_moving_camera( arrow_moving_time_speed, false );
1001  return true;
1002 
1003  case WXK_RIGHT:
1006  m_camera.Pan_T1( SFVEC3F( +delta_move, 0.0f, 0.0f ) );
1007  request_start_moving_camera( arrow_moving_time_speed, false );
1008  return true;
1009 
1010  case WXK_UP:
1013  m_camera.Pan_T1( SFVEC3F( 0.0f, +delta_move, 0.0f ) );
1014  request_start_moving_camera( arrow_moving_time_speed, false );
1015  return true;
1016 
1017  case WXK_DOWN:
1020  m_camera.Pan_T1( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
1021  request_start_moving_camera( arrow_moving_time_speed, false );
1022  return true;
1023 
1024  case WXK_HOME:
1027  m_camera.Reset_T1();
1028  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 1/1.26f ), 1.26f ) );
1029  return true;
1030 
1031  case WXK_END:
1032  break;
1033 
1034  case WXK_TAB:
1037  m_camera.RotateZ_T1( glm::radians( 45.0f ) );
1039  handled = true;
1040  break;
1041 
1042  case WXK_F1:
1045 
1046  if( m_camera.Zoom_T1( 1.26f ) ) // 3 steps per doubling
1048 
1049  return true;
1050 
1051  case WXK_F2:
1054 
1055  if( m_camera.Zoom_T1( 1/1.26f ) ) // 3 steps per halving
1057 
1058  return true;
1059 
1060  case ID_VIEW3D_RESET:
1063  m_camera.Reset_T1();
1064  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 0.5f ), 1.125f ) );
1065  return true;
1066 
1067  case ID_VIEW3D_RIGHT:
1070  m_camera.Reset_T1();
1071  m_camera.RotateZ_T1( glm::radians( -90.0f ) );
1072  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1074  return true;
1075 
1076  case ID_VIEW3D_LEFT:
1079  m_camera.Reset_T1();
1080  m_camera.RotateZ_T1( glm::radians( 90.0f ) );
1081  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1083  return true;
1084 
1085  case ID_VIEW3D_FRONT:
1088  m_camera.Reset_T1();
1089  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1091  return true;
1092 
1093  case ID_VIEW3D_BACK:
1096  m_camera.Reset_T1();
1097  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1098  // The rotation angle should be 180.
1099  // We use 179.999 (180 - epsilon) to avoid a full 360 deg rotation when
1100  // using 180 deg if the previous rotated position was already 180 deg
1101  m_camera.RotateZ_T1( glm::radians( 179.999f ) );
1103  return true;
1104 
1105  case ID_VIEW3D_TOP:
1108  m_camera.Reset_T1();
1109  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 0.5f ), 1.125f ) );
1110  return true;
1111 
1112  case ID_VIEW3D_BOTTOM:
1115  m_camera.Reset_T1();
1116  m_camera.RotateY_T1( glm::radians( 179.999f ) ); // Rotation = 180 - epsilon
1117  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 0.5f ), 1.125f ) );
1118  return true;
1119 
1120  case ID_VIEW3D_FLIP:
1123  m_camera.RotateY_T1( glm::radians( 179.999f ) );
1125  return true;
1126 
1127  default:
1128  return false;
1129  }
1130 
1131  m_mouse_was_moved = true;
1132 
1134 
1135  DisplayStatus();
1136  Request_refresh();
1137 
1138  return handled;
1139 }
1140 
1141 
1143 {
1144  switch( m_boardAdapter.RenderEngineGet() )
1145  {
1148  default: m_3d_render = NULL; break;
1149  }
1150 
1151  if( m_3d_render )
1153 
1154  m_mouse_was_moved = false;
1155 
1156  Request_refresh();
1157 }
1158 
1159 
1161 {
1162  SFVEC3F rayOrigin;
1163  SFVEC3F rayDir;
1164 
1165  // Generate a ray origin and direction based on current mouser position and camera
1166  m_camera.MakeRayAtCurrrentMousePosition( rayOrigin, rayDir );
1167 
1168  RAY mouseRay;
1169  mouseRay.Init( rayOrigin, rayDir );
1170 
1171  return mouseRay;
1172 }
C3D_RENDER_RAYTRACING * m_3d_render_raytracing
STATUSBAR_REPORTER is a wrapper for reporting to a specific text location in a statusbar.
Definition: reporter.h:264
void SetLookAtPos_T1(const SFVEC3F &aLookAtPos)
Definition: ccamera.h:128
virtual wxSize GetNativePixelSize() const
void OGL_GetScreenshot(wxImage &aDstImage)
OGL_GetScreenshot - got the pixel data of current OpenGL image.
Definition: ogl_utils.cpp:36
bool GetFlag(DISPLAY3D_FLG aFlag) const
GetFlag - get a configuration status of a flag.
void OnMouseMove(wxMouseEvent &event)
void OnResize(wxSizeEvent &event)
void request_start_moving_camera(float aMovingSpeed=2.0f, bool aRenderPivot=true)
request_start_moving_camera - start a camera movement
void releaseOpenGL()
releaseOpenGL - free created targets and openGL context
static wxString FROM_UTF8(const char *cstring)
function FROM_UTF8 converts a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:109
virtual void SetT0_and_T1_current_T()
SetT0_and_T1_current_T - This will set T0 and T1 with the current values.
Definition: ccamera.cpp:559
bool m_mouse_is_moving
void OnRefreshRequest(wxEvent &aEvent)
void ResetXYpos_T1()
Definition: ccamera.cpp:416
unsigned m_strtime_camera_movement
wxDEFINE_EVENT(wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent)
WX_INFOBAR * m_parentInfoBar
BOARD_ITEM is a base class for any item which can be embedded within the BOARD container class,...
Definition: board_item.h:86
RAY getRayAtCurrrentMousePosition()
void stop_editingTimeOut_Timer()
stop_editingTimeOut_Timer - stop the editing time, so it will not timeout
NETINFO_ITEM * GetNet() const
Function GetNet Returns NET_INFO object for a given item.
void Init(const SFVEC3F &o, const SFVEC3F &d)
Definition: ray.cpp:41
wxStatusBar * m_parentStatusBar
virtual bool Redraw(bool aIsMoving, REPORTER *aStatusReporter=NULL, REPORTER *aWarningReporter=NULL)=0
Redraw - Ask to redraw the view.
Class CCAMERA is a virtual class used to derive CCAMERA objects from.
Definition: ccamera.h:79
wxGLContext * m_glRC
const CBBOX & GetBBox3DU() const noexcept
GetBBox3DU - Get the bbox of the pcb board.
void OnCloseWindow(wxCloseEvent &event)
wxGLCanvas wrapper for HiDPI/Retina support.
class ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
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.
BOARD_ITEM * m_currentIntersectedBoardItem
class PAD, a pad in a footprint
Definition: typeinfo.h:90
Definition: ray.h:67
void DisplayStatus()
DisplayStatus - Update the status bar with the position information.
C3D_RENDER_OGL_LEGACY * m_3d_render_ogl_legacy
S3D_CACHE.
Definition: 3d_cache.h:54
void Request_refresh(bool aRedrawImmediately=true)
Request_refresh - Schedule a refresh update of the canvas.
void ReloadRequest(BOARD *aBoard=NULL, S3D_CACHE *aCachePointer=NULL)
virtual int GetWaitForEditingTimeOut()=0
GetWaitForEditingTimeOut - Give the interface the time (in ms) that it should wait for editing or mov...
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:102
void UnlockCtx(wxGLContext *aContext)
Function UnlockCtx allows other canvases to bind an OpenGL context.
void RenderEngineSet(RENDER_ENGINE aRenderEngine) noexcept
RenderEngineSet.
SFVEC3F at(float t) const
Definition: ray.h:89
void DoRePaint()
The actual function to repaint the canvas.
void SetCurMousePosition(const wxPoint &aPosition)
It updates the current mouse position without make any new recalculations on camera.
Definition: ccamera.cpp:435
class TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
void SetBoard(BOARD *aBoard) noexcept
SetBoard - Set current board to be rendered.
BOARD_ADAPTER & m_boardAdapter
bool Zoom(float aFactor)
Definition: ccamera.cpp:478
void OnLeftDown(wxMouseEvent &event)
float m_camera_moving_speed
bool m_is_opengl_version_supported
static GL_CONTEXT_MANAGER & Get()
Function Get returns the GL_CONTEXT_MANAGER instance (singleton).
void OnLeftUp(wxMouseEvent &event)
void restart_editingTimeOut_Timer()
restart_editingTimeOut_Timer - reset the editing timer
void RenderRaytracingRequest()
RenderRaytracingRequest - Request to render the current view in Raytracing mode.
void SetEventDispatcher(TOOL_DISPATCHER *aEventDispatcher)
Function SetEventDispatcher() Sets a dispatcher that processes events and forwards them to tools.
float ZoomGet() const
Definition: ccamera.cpp:514
#define NULL
void OnMiddleDown(wxMouseEvent &event)
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
CCAMERA & m_camera
bool m_mouse_was_moved
void OnPaint(wxPaintEvent &aEvent)
Called by a wxPaintEvent event.
void OnEraseBackground(wxEraseEvent &event)
void OnMouseWheel(wxMouseEvent &event)
void RenderEngineChanged()
RenderEngineChanged - Notify that the render engine was changed.
virtual void DispatchWxEvent(wxEvent &aEvent)
Processes wxEvents (mostly UI events), translates them to TOOL_EVENTs, and makes tools handle those.
const wxString & GetName() const
Definition: pad.h:127
bool m_camera_is_moving
virtual void Interpolate(float t)
Interpolate - It will update the matrix to interpolate between T0 and T1 values.
Definition: ccamera.cpp:573
virtual void Drag(const wxPoint &aNewMousePosition)=0
Calculate a new mouse drag position.
EVT_MIDDLE_DOWN(mpWindow::OnMouseMiddleDown) EVT_MOUSEWHEEL(mpWindow
Definition: mathplot.cpp:1681
const wxString & GetNetname() const
Function GetNetname.
Definition: netinfo.h:231
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Function Report is a pure virtual function to override in the derived object.
Definition: reporter.cpp:146
RENDER_ENGINE RenderEngineGet() const noexcept
RenderEngineGet.
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Function Report is a pure virtual function to override in the derived object.
Definition: reporter.cpp:163
ZONE handles a list of polygons defining a copper zone.
Definition: zone.h:57
class ZONE, a copper pour area
Definition: typeinfo.h:106
void Finalize()
Update the infobar with the reported text.
Definition: reporter.cpp:179
bool SetCurWindowSize(const wxSize &aSize)
SetCurWindowSize - update the windows size of the camera.
Definition: ccamera.cpp:452
const wxString GetReference() const
Function GetReference.
Definition: footprint.h:440
void Reload(REPORTER *aStatusReporter, REPORTER *aWarningReporter, bool aOnlyLoadCopperAndShapes)
bool IsOnCopperLayer() const override
Definition: pad.h:211
void OnTimerTimeout_Redraw(wxTimerEvent &event)
bool Zoom_T1(float aFactor)
Definition: ccamera.cpp:497
virtual void Pan_T1(const SFVEC3F &aDeltaOffsetInc)=0
void RotateX_T1(float aAngleInRadians)
Definition: ccamera.cpp:541
SETTINGS_MANAGER * GetSettingsManager()
BOARD_ITEM * IntersectBoardItem(const RAY &aRay)
virtual void Reset_T1()
Definition: ccamera.cpp:103
bool m_animation_enabled
void LockCtx(wxGLContext *aContext, wxGLCanvas *aCanvas)
Function LockCtx sets a context as current and prevents other canvases from switching it.
void Set3DCacheManager(S3D_CACHE *aCachePointer) noexcept
Set3DCacheManager - Update the Cache manager pointer.
Definition: board_adapter.h:80
class FOOTPRINT, a footprint
Definition: typeinfo.h:89
bool m_is_opengl_initialized
bool IsReloadRequestPending() const
IsReloadRequestPending - Query if there is a pending reload request.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
C3D_RENDER_BASE * m_3d_render
void OnMiddleUp(wxMouseEvent &event)
The C3D_RENDER_OGL_LEGACY class render the board using openGL legacy mode.
TOOL_DISPATCHER * m_eventDispatcher
void GetScreenshot(wxImage &aDstImage)
Request a screenshot and output it to the aDstImage.
int m_moving_speed_multiplier
CGENERICACCELERATOR * m_accelerator3DShapes
const int scale
see class PGM_BASE
bool initializeOpenGL()
initializeOpenGL
void OnEvent(wxEvent &aEvent)
Used to forward events to the canvas from popups, etc.
bool m_opengl_supports_raytracing
Declaration of the eda_3d_viewer class.
void RotateY_T1(float aAngleInRadians)
Definition: ccamera.cpp:547
void MakeRayAtCurrrentMousePosition(SFVEC3F &aOutOrigin, SFVEC3F &aOutDirection) const
MakeRayAtCurrrentMousePosition - Make a ray based on the latest mouse position.
Definition: ccamera.cpp:379
void move_pivot_based_on_cur_mouse_position()
move_pivot_based_on_cur_mouse_position - This function hits a ray to the board and start a moviment
std::atomic_flag m_is_currently_painting
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:186
virtual void DispatchWxCommand(wxCommandEvent &aEvent)
Processes wxCommands (mostly menu related events) and runs appropriate actions (eg.
#define _(s)
Definition: 3d_actions.cpp:33
bool Intersect(const RAY &aRay, float *t) const
Function Intersect.
Definition: cbbox_ray.cpp:45
unsigned GetRunningMicroSecs()
Function GetRunningMicroSecs An alternate way to calculate an elapset time (in microsecondes) to clas...
virtual void Pan(const wxPoint &aNewMousePosition)=0
glm::vec3 SFVEC3F
Definition: xv3d_types.h:47
bool SetView3D(int aKeycode)
SetView3D - Helper function to call view commands.
bool IsOnCopperLayer() const override
Function IsOnCopperLayer.
Definition: zone.cpp:221
void SetCurrentIntersectedBoardItem(BOARD_ITEM *aCurrentIntersectedBoardItem)
void OnTimerTimeout_Editing(wxTimerEvent &event)
const SFVEC3F & GetCameraPos() const
Definition: ccamera.h:134
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
void DestroyCtx(wxGLContext *aContext)
Function DestroyCtx destroys a managed OpenGL context.
void SetColorSettings(COLOR_SETTINGS *aSettings) noexcept
wxGLContext * CreateCtx(wxGLCanvas *aCanvas, const wxGLContext *aOther=NULL)
Function CreateCtx creates a managed OpenGL context.
Definition: pad.h:59
virtual void SetCurWindowSize(const wxSize &aSize)=0
SetCurWindowSize - Before each render, the canvas will tell the render what is the size of its window...
static int SetSwapInterval(int aVal)
Attempts to set the OpenGL swap interval.
Definition: gl_utils.h:39
wxString GetClassName() const
Function GetClassName returns the class name.
Definition: netinfo.h:137
void ReloadRequest()
ReloadRequest - !TODO: this must be reviewed to add flags to improve specific render.
void RotateZ_T1(float aAngleInRadians)
Definition: ccamera.cpp:553
wxTimer m_redraw_trigger_timer
Class BOARD_ADAPTER 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_pivot - render the pivot cursor
This BVH implementation is based on the source code implementation from the book "Physically Based Re...
INFOBAR_REPORTER is a wrapper for reporting to a WX_INFOBAR UI element.
Definition: reporter.h:292
Definition: track.h:83
void SetInterpolateMode(CAMERA_INTERPOLATION aInterpolateMode)
Definition: ccamera.h:200
KICAD_T Type() const
Function Type()
Definition: eda_item.h:181
bool HasMessage() const override
Function HasMessage Returns true if the reporter client is non-empty.
Definition: reporter.cpp:154
bool ParametersChanged()
Function ParametersChanged.
Definition: ccamera.cpp:591
bool m_render_raytracing_was_requested