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.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 <menus_helpers.h>
41 #include <pgm_base.h>
43 #include <tool/tool_dispatcher.h>
44 
46 
47 
55 const wxChar* EDA_3D_CANVAS::m_logTrace = wxT( "KI_TRACE_EDA_3D_CANVAS" );
56 
58 
59 
60 // A custom event, used to call DoRePaint during an idle time
61 wxDEFINE_EVENT( wxEVT_REFRESH_CUSTOM_COMMAND, wxEvent);
62 
63 
64 BEGIN_EVENT_TABLE( EDA_3D_CANVAS, wxGLCanvas )
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 )
72  EVT_MOUSEWHEEL( EDA_3D_CANVAS::OnMouseWheel )
73  EVT_MOTION( EDA_3D_CANVAS::OnMouseMove )
74 
75 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
76  EVT_MAGNIFY( EDA_3D_CANVAS::OnMagnify )
77 #endif
78 
79  // other events
80  EVT_ERASE_BACKGROUND( EDA_3D_CANVAS::OnEraseBackground )
81  //EVT_REFRESH_CUSTOM_COMMAND( ID_CUSTOM_EVENT_1, EDA_3D_CANVAS::OnRefreshRequest )
82  EVT_CUSTOM(wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1, EDA_3D_CANVAS::OnRefreshRequest )
83 
84  EVT_CLOSE( EDA_3D_CANVAS::OnCloseWindow )
85  EVT_SIZE( EDA_3D_CANVAS::OnResize )
86 END_EVENT_TABLE()
87 
88 
89 EDA_3D_CANVAS::EDA_3D_CANVAS( wxWindow* aParent, const int* aAttribList, BOARD* aBoard,
90  BOARD_ADAPTER& aBoardAdapter, CAMERA& aCamera,
91  S3D_CACHE* a3DCachePointer )
92  : HIDPI_GL_CANVAS( aParent, wxID_ANY, aAttribList, wxDefaultPosition, wxDefaultSize,
93  wxFULL_REPAINT_ON_RESIZE ),
94  m_eventDispatcher( nullptr ),
95  m_parentStatusBar( nullptr ),
96  m_parentInfoBar( nullptr ),
97  m_glRC( nullptr ),
98  m_is_opengl_initialized( false ),
99  m_is_opengl_version_supported( true ),
100  m_mouse_is_moving( false ),
101  m_mouse_was_moved( false ),
102  m_camera_is_moving( false ),
103  m_render_pivot( false ),
104  m_camera_moving_speed( 1.0f ),
105  m_strtime_camera_movement( 0 ),
106  m_animation_enabled( true ),
107  m_moving_speed_multiplier( 3 ),
108  m_boardAdapter( aBoardAdapter ),
109  m_camera( aCamera ),
110  m_3d_render( nullptr ),
111  m_opengl_supports_raytracing( true ),
112  m_render_raytracing_was_requested( false ),
113  m_accelerator3DShapes( nullptr ),
114  m_currentIntersectedBoardItem( nullptr )
115 {
116  wxLogTrace( m_logTrace, "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( m_boardAdapter, m_camera );
129  m_3d_render_ogl_legacy = new RENDER_3D_LEGACY( m_boardAdapter, m_camera );
130 
131  wxASSERT( m_3d_render_raytracing != nullptr );
132  wxASSERT( m_3d_render_ogl_legacy != nullptr );
133 
134  auto busy_indicator_factory = []() { return std::make_unique<WX_BUSY_INDICATOR>(); };
135 
136  m_3d_render_raytracing->SetBusyIndicatorFactory( busy_indicator_factory );
137  m_3d_render_ogl_legacy->SetBusyIndicatorFactory( busy_indicator_factory );
138 
139  RenderEngineChanged();
140 
141  wxASSERT( aBoard != nullptr );
142  m_boardAdapter.SetBoard( aBoard );
143 
144  m_boardAdapter.SetColorSettings( Pgm().GetSettingsManager().GetColorSettings() );
145 
146  wxASSERT( a3DCachePointer != nullptr );
147  m_boardAdapter.Set3dCacheManager( a3DCachePointer );
148 
149  const wxEventType events[] =
150  {
151  // Binding both EVT_CHAR and EVT_CHAR_HOOK ensures that all key events,
152  // especially special key like arrow keys, are handled by the GAL event dispatcher,
153  // and not sent to GUI without filtering, because they have a default action (scroll)
154  // that must not be called.
155  wxEVT_LEFT_UP, wxEVT_LEFT_DOWN, wxEVT_LEFT_DCLICK,
156  wxEVT_RIGHT_UP, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_DCLICK,
157  wxEVT_MIDDLE_UP, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_DCLICK,
158  wxEVT_MOTION, wxEVT_MOUSEWHEEL, wxEVT_CHAR, wxEVT_CHAR_HOOK,
159 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
160  wxEVT_MAGNIFY,
161 #endif
162  wxEVT_MENU_OPEN, wxEVT_MENU_CLOSE, wxEVT_MENU_HIGHLIGHT
163  };
164 
165  for( wxEventType eventType : events )
166  Connect( eventType, wxEventHandler( EDA_3D_CANVAS::OnEvent ), nullptr, m_eventDispatcher );
167 }
168 
169 
171 {
172  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::~EDA_3D_CANVAS" );
173 
174  delete m_accelerator3DShapes;
175  m_accelerator3DShapes = nullptr;
176 
177  releaseOpenGL();
178 }
179 
180 
182 {
183  if( m_glRC )
184  {
186 
187  delete m_3d_render_raytracing;
188  m_3d_render_raytracing = nullptr;
189 
190  delete m_3d_render_ogl_legacy;
191  m_3d_render_ogl_legacy = nullptr;
192 
193  // This is just a copy of a pointer, can safely be set to NULL.
194  m_3d_render = nullptr;
195 
198  m_glRC = nullptr;
199  }
200 }
201 
202 
203 void EDA_3D_CANVAS::OnCloseWindow( wxCloseEvent& event )
204 {
205  releaseOpenGL();
206 
207  event.Skip();
208 }
209 
210 
211 void EDA_3D_CANVAS::OnResize( wxSizeEvent& event )
212 {
213  this->Request_refresh();
214 }
215 
216 
218 {
219  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::initializeOpenGL" );
220 
221  const GLenum err = glewInit();
222 
223  if( GLEW_OK != err )
224  {
225  const wxString msgError = (const char*) glewGetErrorString( err );
226 
227  wxLogMessage( msgError );
228 
229  return false;
230  }
231  else
232  {
233  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::initializeOpenGL Using GLEW version %s",
234  FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) );
235  }
236 
237  wxString version = FROM_UTF8( (char *) glGetString( GL_VERSION ) );
238 
239  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::%s OpenGL version string %s.",
240  __WXFUNCTION__, version );
241 
242  // Extract OpenGL version from string. This method is used because prior to OpenGL 2,
243  // getting the OpenGL major and minor version as integers didn't exist.
244  wxString tmp;
245 
246  wxStringTokenizer tokenizer( version );
247 
248  if( tokenizer.HasMoreTokens() )
249  {
250  long major = 0;
251  long minor = 0;
252 
253  tmp = tokenizer.GetNextToken();
254 
255  tokenizer.SetString( tmp, wxString( "." ) );
256 
257  if( tokenizer.HasMoreTokens() )
258  tokenizer.GetNextToken().ToLong( &major );
259 
260  if( tokenizer.HasMoreTokens() )
261  tokenizer.GetNextToken().ToLong( &minor );
262 
263  if( major < 2 || ( ( major == 2 ) && ( minor < 1 ) ) )
264  {
265  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::%s OpenGL ray tracing not supported.",
266  __WXFUNCTION__ );
267 
268  if( GetParent() )
269  {
270  wxCommandEvent evt( wxEVT_MENU, ID_DISABLE_RAY_TRACING );
271  GetParent()->ProcessWindowEvent( evt );
272  }
273 
275  }
276 
277  if( ( major == 1 ) && ( minor < 5 ) )
278  {
279  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::%s OpenGL not supported.", __WXFUNCTION__ );
280 
282  }
283  }
284 
287 
288  return true;
289 }
290 
291 
292 void EDA_3D_CANVAS::GetScreenshot( wxImage& aDstImage )
293 {
294  OglGetScreenshot( aDstImage );
295 }
296 
297 
298 void EDA_3D_CANVAS::ReloadRequest( BOARD* aBoard , S3D_CACHE* aCachePointer )
299 {
300  if( aCachePointer != nullptr )
301  m_boardAdapter.Set3dCacheManager( aCachePointer );
302 
303  if( aBoard != nullptr )
304  m_boardAdapter.SetBoard( aBoard );
305 
306  m_boardAdapter.SetColorSettings( Pgm().GetSettingsManager().GetColorSettings() );
307 
308  if( m_3d_render )
310 }
311 
312 
314 {
316 
317  if( m_3d_render )
319 
321 
322  Request_refresh();
323 }
324 
325 
327 {
328  if( m_parentStatusBar )
329  {
330  wxString msg;
331 
332  msg.Printf( "dx %3.2f", m_camera.GetCameraPos().x );
333  m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::X_POS ) );
334 
335  msg.Printf( "dy %3.2f", m_camera.GetCameraPos().y );
336  m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::Y_POS ) );
337  }
338 }
339 
340 
341 void EDA_3D_CANVAS::OnPaint( wxPaintEvent& aEvent )
342 {
343  // Please have a look at: https://lists.launchpad.net/kicad-developers/msg25149.html
344  DoRePaint();
345 }
346 
347 
349 {
350  if( m_is_currently_painting.test_and_set() )
351  return;
352 
353  // SwapBuffer requires the window to be shown before calling
354  if( !IsShownOnScreen() )
355  {
356  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::DoRePaint !IsShown" );
357  m_is_currently_painting.clear();
358  return;
359  }
360 
361  // Because the board to draw is handled by the parent viewer frame,
362  // ensure this parent is still alive. When it is closed before the viewer
363  // frame, a paint event can be generated after the parent is closed,
364  // therefore with invalid board.
365  // This is dependent of the platform.
366  // Especially on OSX, but also on Windows, it frequently happens
367  if( !GetParent()->GetParent()->IsShown() )
368  return; // The parent board editor frame is no more alive
369 
370  wxString err_messages;
371 
372  // !TODO: implement error reporter
373  INFOBAR_REPORTER warningReporter( m_parentInfoBar );
374  STATUSBAR_REPORTER activityReporter( m_parentStatusBar,
376 
377  unsigned strtime = GetRunningMicroSecs();
378 
379  // "Makes the OpenGL state that is represented by the OpenGL rendering
380  // context context current, i.e. it will be used by all subsequent OpenGL calls.
381  // This function may only be called when the window is shown on screen"
382 
383  // Explicitly create a new rendering context instance for this canvas.
384  if( m_glRC == nullptr )
386 
388 
389  // Set the OpenGL viewport according to the client size of this canvas.
390  // This is done here rather than in a wxSizeEvent handler because our
391  // OpenGL rendering context (and thus viewport setting) is used with
392  // multiple canvases: If we updated the viewport in the wxSizeEvent
393  // handler, changing the size of one canvas causes a viewport setting that
394  // is wrong when next another canvas is repainted.
395  wxSize clientSize = GetNativePixelSize();
396 
397  const bool windows_size_changed = m_camera.SetCurWindowSize( clientSize );
398 
399  // Initialize openGL if need
401  {
402  if( !initializeOpenGL() )
403  {
405  m_is_currently_painting.clear();
406 
407  return;
408  }
409 
411  {
412  warningReporter.Report( _( "Your OpenGL version is not supported. Minimum required "
413  "is 1.5" ), RPT_SEVERITY_ERROR );
414 
415  warningReporter.Finalize();
416  }
417  }
418 
420  {
421  glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
422  glClear( GL_COLOR_BUFFER_BIT );
423 
424  SwapBuffers();
425 
427  m_is_currently_painting.clear();
428 
429  return;
430  }
431 
432  // Don't attend to ray trace if OpenGL doesn't support it.
434  {
438  }
439 
440  // Check if a raytacing was requested and need to switch to raytracing mode
442  {
443  const bool was_camera_changed = m_camera.ParametersChanged();
444 
445  // It reverts back to OpenGL mode if it was requested a raytracing
446  // render of the current scene. AND the mouse / camera is moving
447  if( ( m_mouse_is_moving || m_camera_is_moving || was_camera_changed
448  || windows_size_changed )
450  {
453  }
454  }
455 
456  float curtime_delta_s = 0.0f;
457 
458  if( m_camera_is_moving )
459  {
460  const unsigned curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
461  curtime_delta_s = (curtime_delta / 1e6) * m_camera_moving_speed;
462  m_camera.Interpolate( curtime_delta_s );
463 
464  if( curtime_delta_s > 1.0f )
465  {
466  m_render_pivot = false;
467  m_camera_is_moving = false;
468  m_mouse_was_moved = true;
469 
471  DisplayStatus();
472  }
473  else
474  {
475  Request_refresh();
476  }
477  }
478 
479  // It will return true if the render request a new redraw
480  bool requested_redraw = false;
481 
482  if( m_3d_render )
483  {
484  try
485  {
486  m_3d_render->SetCurWindowSize( clientSize );
487 
488  bool reloadRaytracingForIntersectionCalculations = false;
489 
492  {
493  reloadRaytracingForIntersectionCalculations = true;
494  }
495 
496  requested_redraw = m_3d_render->Redraw( m_mouse_was_moved || m_camera_is_moving,
497  &activityReporter, &warningReporter );
498 
499  if( reloadRaytracingForIntersectionCalculations )
500  m_3d_render_raytracing->Reload( nullptr, nullptr, true );
501  }
502  catch( std::runtime_error& )
503  {
506  m_is_opengl_initialized = false;
508  m_is_currently_painting.clear();
509  return;
510  }
511  }
512 
513  if( m_render_pivot )
514  {
515  const float scale = glm::min( m_camera.ZoomGet(), 1.0f );
516  render_pivot( curtime_delta_s, scale * scale );
517  }
518 
519  // "Swaps the double-buffer of this window, making the back-buffer the
520  // front-buffer and vice versa, so that the output of the previous OpenGL
521  // commands is displayed on the window."
522  SwapBuffers();
523 
525 
526  if( !activityReporter.HasMessage() )
527  {
529  {
530  // Calculation time in milliseconds
531  const double calculation_time = (double)( GetRunningMicroSecs() - strtime) / 1e3;
532 
533  activityReporter.Report( wxString::Format( _( "Last render time %.0f ms" ),
534  calculation_time ) );
535  }
536  }
537 
538  // This will reset the flag of camera parameters changed
540 
541  warningReporter.Finalize();
542 
543  if( !err_messages.IsEmpty() )
544  wxLogMessage( err_messages );
545 
546  if( ( !m_camera_is_moving ) && requested_redraw )
547  {
548  m_mouse_was_moved = false;
549  Request_refresh( false );
550  }
551 
552  m_is_currently_painting.clear();
553 }
554 
555 
557 {
558  m_eventDispatcher = aEventDispatcher;
559 
560  if( m_eventDispatcher )
561  {
562  m_parent->Connect( wxEVT_TOOL,
563  wxCommandEventHandler( TOOL_DISPATCHER::DispatchWxCommand ),
564  nullptr, m_eventDispatcher );
565  }
566  else
567  {
568  // While loop is used to be sure that all event handlers are removed.
569  while( m_parent->Disconnect( wxEVT_TOOL,
570  wxCommandEventHandler( TOOL_DISPATCHER::DispatchWxCommand ),
571  nullptr, m_eventDispatcher ) );
572  }
573 }
574 
575 
576 void EDA_3D_CANVAS::OnEvent( wxEvent& aEvent )
577 {
578  if( !m_eventDispatcher )
579  aEvent.Skip();
580  else
582 
583  Refresh();
584 }
585 
586 
587 void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent& event )
588 {
589  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::OnEraseBackground" );
590  // Do nothing, to avoid flashing.
591 }
592 
593 
594 void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent& event )
595 {
596  bool mouseActivity = false;
597 
598  wxLogTrace( m_logTrace, "EDA_3D_CANVAS::OnMouseWheel" );
599 
600  if( m_camera_is_moving )
601  return;
602 
603  float delta_move = m_delta_move_step_factor * m_camera.ZoomGet();
604 
606  delta_move *= 0.01f * event.GetWheelRotation();
607  else
608  if( event.GetWheelRotation() < 0 )
609  delta_move = -delta_move;
610 
611  // mousewheel_panning enabled:
612  // wheel -> pan;
613  // wheel + shift -> horizontal scrolling;
614  // wheel + ctrl -> zooming;
615  // mousewheel_panning disabled:
616  // wheel + shift -> vertical scrolling;
617  // wheel + ctrl -> horizontal scrolling;
618  // wheel -> zooming.
619 
620  if( m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) && !event.ControlDown() )
621  {
622  if( event.GetWheelAxis() == wxMOUSE_WHEEL_HORIZONTAL || event.ShiftDown() )
623  m_camera.Pan( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
624  else
625  m_camera.Pan( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
626 
627  mouseActivity = true;
628  }
629  else if( event.ShiftDown() && !m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) )
630  {
631  m_camera.Pan( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
632  mouseActivity = true;
633  }
634  else if( event.ControlDown() && !m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) )
635  {
636  m_camera.Pan( SFVEC3F( delta_move, 0.0f, 0.0f ) );
637  mouseActivity = true;
638  }
639  else
640  {
641  mouseActivity = m_camera.Zoom( event.GetWheelRotation() > 0 ? 1.1f : 1/1.1f );
642  }
643 
644  // If it results on a camera movement
645  if( mouseActivity )
646  {
647  DisplayStatus();
648  Request_refresh();
649 
650  m_mouse_is_moving = true;
651  m_mouse_was_moved = true;
652 
654  }
655 
656  // Update the cursor current mouse position on the camera
657  m_camera.SetCurMousePosition( event.GetPosition() );
658 }
659 
660 
661 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
662 void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event )
663 {
664  SetFocus();
665 
666  if( m_camera_is_moving )
667  return;
668 
669  //m_is_moving_mouse = true;
671 
672  float magnification = ( event.GetMagnification() + 1.0f );
673 
674  m_camera.Zoom( magnification );
675 
676  DisplayStatus();
677  Request_refresh();
678 }
679 #endif
680 
681 
682 void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent& event )
683 {
684  //wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnMouseMove" ) );
685 
686  if( m_camera_is_moving )
687  return;
688 
690 
691  if( event.Dragging() )
692  {
693  if( event.LeftIsDown() ) // Drag
694  m_camera.Drag( event.GetPosition() );
695  else if( event.MiddleIsDown() ) // Pan
696  m_camera.Pan( event.GetPosition() );
697 
698  m_mouse_is_moving = true;
699  m_mouse_was_moved = true;
700 
701  // orientation has changed, redraw mesh
702  DisplayStatus();
703  Request_refresh();
704  }
705 
706  const wxPoint eventPosition = event.GetPosition();
707  m_camera.SetCurMousePosition( eventPosition );
708 
709  if( !event.Dragging() &&
711  {
713  static_cast<int>( EDA_3D_VIEWER_STATUSBAR::HOVERED_ITEM ) );
714 
715  RAY mouseRay = getRayAtCurrrentMousePosition();
716 
717  BOARD_ITEM *intersectedBoardItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
718 
719  if( intersectedBoardItem )
720  {
721  if( intersectedBoardItem != m_currentIntersectedBoardItem )
722  {
724  m_currentIntersectedBoardItem = intersectedBoardItem;
725 
726  Request_refresh();
727  }
728 
729  switch( intersectedBoardItem->Type() )
730  {
731  case PCB_PAD_T:
732  {
733  PAD* pad = dynamic_cast<PAD*>( intersectedBoardItem );
734 
735  if( pad && pad->IsOnCopperLayer() )
736  {
737  reporter.Report( wxString::Format( _( "Net %s\tNetClass %s\tPadName %s" ),
738  pad->GetNet()->GetNetname(),
739  pad->GetNet()->GetNetClassName(),
740  pad->GetName() ) );
741  }
742  }
743  break;
744 
745  case PCB_FOOTPRINT_T:
746  {
747  FOOTPRINT* footprint = dynamic_cast<FOOTPRINT *>( intersectedBoardItem );
748 
749  if( footprint )
750  reporter.Report( footprint->GetReference() );
751  }
752  break;
753 
754  case PCB_TRACE_T:
755  case PCB_VIA_T:
756  case PCB_ARC_T:
757  {
758  TRACK* track = dynamic_cast<TRACK *>( intersectedBoardItem );
759 
760  if( track )
761  {
762  reporter.Report( wxString::Format( _( "Net %s\tNetClass %s" ),
763  track->GetNet()->GetNetname(),
764  track->GetNet()->GetNetClassName() ) );
765  }
766  }
767  break;
768 
769  case PCB_ZONE_T:
770  {
771  ZONE* zone = dynamic_cast<ZONE*>( intersectedBoardItem );
772 
773  if( zone && zone->IsOnCopperLayer() )
774  {
775  reporter.Report( wxString::Format( _( "Net %s\tNetClass %s" ),
776  zone->GetNet()->GetNetname(),
777  zone->GetNet()->GetNetClassName() ) );
778  }
779  }
780  break;
781 
782  default:
783  break;
784  }
785  }
786  else
787  {
788  if( ( m_currentIntersectedBoardItem != nullptr ) &&
790  {
792  Request_refresh();
793 
794  reporter.Report( "" );
795  }
796 
798  }
799  }
800 }
801 
802 
803 void EDA_3D_CANVAS::OnLeftDown( wxMouseEvent& event )
804 {
805  SetFocus();
807 
808  if( !event.Dragging() && ( m_3d_render_raytracing != nullptr ) )
809  {
810  RAY mouseRay = getRayAtCurrrentMousePosition();
811 
812  BOARD_ITEM *intersectedBoardItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
813 
814  // !TODO: send a selection item to pcbnew, eg: via kiway?
815  }
816 }
817 
818 
819 void EDA_3D_CANVAS::OnLeftUp( wxMouseEvent& event )
820 {
821  if( m_camera_is_moving )
822  return;
823 
824  if( m_mouse_is_moving )
825  {
826  m_mouse_is_moving = false;
828  }
829 }
830 
831 
832 void EDA_3D_CANVAS::OnMiddleDown( wxMouseEvent& event )
833 {
834  SetFocus();
836 }
837 
838 
839 void EDA_3D_CANVAS::OnMiddleUp( 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  else
850  {
852  }
853 }
854 
855 
856 void EDA_3D_CANVAS::OnTimerTimeout_Editing( wxTimerEvent& event )
857 {
858  (void)event;
859 
860  m_mouse_is_moving = false;
861  m_mouse_was_moved = false;
862 
863  Request_refresh();
864 }
865 
866 
868 {
870 }
871 
872 
874 {
875  if( m_3d_render )
876  m_editing_timeout_timer.Start( m_3d_render->GetWaitForEditingTimeOut(), wxTIMER_ONE_SHOT );
877 }
878 
879 
880 void EDA_3D_CANVAS::OnTimerTimeout_Redraw( wxTimerEvent& event )
881 {
882  Request_refresh( true );
883 }
884 
885 
886 void EDA_3D_CANVAS::OnRefreshRequest( wxEvent& aEvent )
887 {
888  DoRePaint();
889 }
890 
891 
892 void EDA_3D_CANVAS::Request_refresh( bool aRedrawImmediately )
893 {
894  if( aRedrawImmediately )
895  {
896  // Just calling Refresh() does not work always
897  // Using an event to call DoRepaint ensure the repaint code will be executed,
898  // and PostEvent will take priority to other events like mouse movements, keys, etc.
899  // and is executed during the next idle time
900  wxCommandEvent redrawEvent( wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1 );
901  wxPostEvent( this, redrawEvent );
902  }
903  else
904  {
905  // Schedule a timed redraw
906  m_redraw_trigger_timer.Start( 10 , wxTIMER_ONE_SHOT );
907  }
908 }
909 
910 
911 void EDA_3D_CANVAS::request_start_moving_camera( float aMovingSpeed, bool aRenderPivot )
912 {
913  wxASSERT( aMovingSpeed > FLT_EPSILON );
914 
915  // Fast forward the animation if the animation is disabled
916  if( !m_animation_enabled )
917  {
918  m_camera.Interpolate( 1.0f );
919  DisplayStatus();
920  Request_refresh();
921  return;
922  }
923 
924  // Map speed multiplier option to actual multiplier value
925  // [1,2,3,4,5] -> [0.25, 0.5, 1, 2, 4]
926  aMovingSpeed *= ( 1 << m_moving_speed_multiplier ) / 8.0f;
927 
928  m_render_pivot = aRenderPivot;
929  m_camera_moving_speed = aMovingSpeed;
930 
932 
933  DisplayStatus();
934  Request_refresh();
935 
936  m_camera_is_moving = true;
937 
939 }
940 
941 
943 {
944  RAY mouseRay = getRayAtCurrrentMousePosition();
945 
946  float hit_t;
947 
948  // Test it with the board bounding box
949  if( m_boardAdapter.GetBBox().Intersect( mouseRay, &hit_t ) )
950  {
953  m_camera.SetLookAtPos_T1( mouseRay.at( hit_t ) );
955 
957  }
958 }
959 
960 
961 bool EDA_3D_CANVAS::SetView3D( int aKeycode )
962 {
963  if( m_camera_is_moving )
964  return false;
965 
966  const float delta_move = m_delta_move_step_factor * m_camera.ZoomGet();
967  const float arrow_moving_time_speed = 8.0f;
968  bool handled = false;
969 
970  switch( aKeycode )
971  {
972  case WXK_SPACE:
974  return true;
975 
976  case WXK_LEFT:
979  m_camera.Pan_T1( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
980  request_start_moving_camera( arrow_moving_time_speed, false );
981  return true;
982 
983  case WXK_RIGHT:
986  m_camera.Pan_T1( SFVEC3F( +delta_move, 0.0f, 0.0f ) );
987  request_start_moving_camera( arrow_moving_time_speed, false );
988  return true;
989 
990  case WXK_UP:
993  m_camera.Pan_T1( SFVEC3F( 0.0f, +delta_move, 0.0f ) );
994  request_start_moving_camera( arrow_moving_time_speed, false );
995  return true;
996 
997  case WXK_DOWN:
1000  m_camera.Pan_T1( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
1001  request_start_moving_camera( arrow_moving_time_speed, false );
1002  return true;
1003 
1004  case WXK_HOME:
1007  m_camera.Reset_T1();
1008  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 1/1.26f ), 1.26f ) );
1009  return true;
1010 
1011  case WXK_END:
1012  break;
1013 
1014  case WXK_TAB:
1017  m_camera.RotateZ_T1( glm::radians( 45.0f ) );
1019  handled = true;
1020  break;
1021 
1022  case WXK_F1:
1025 
1026  if( m_camera.Zoom_T1( 1.26f ) ) // 3 steps per doubling
1028 
1029  return true;
1030 
1031  case WXK_F2:
1034 
1035  if( m_camera.Zoom_T1( 1/1.26f ) ) // 3 steps per halving
1037 
1038  return true;
1039 
1040  case ID_VIEW3D_RESET:
1043  m_camera.Reset_T1();
1044  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 0.5f ), 1.125f ) );
1045  return true;
1046 
1047  case ID_VIEW3D_RIGHT:
1050  m_camera.Reset_T1();
1051  m_camera.RotateZ_T1( glm::radians( -90.0f ) );
1052  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1054  return true;
1055 
1056  case ID_VIEW3D_LEFT:
1059  m_camera.Reset_T1();
1060  m_camera.RotateZ_T1( glm::radians( 90.0f ) );
1061  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1063  return true;
1064 
1065  case ID_VIEW3D_FRONT:
1068  m_camera.Reset_T1();
1069  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1071  return true;
1072 
1073  case ID_VIEW3D_BACK:
1076  m_camera.Reset_T1();
1077  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1078 
1079  // The rotation angle should be 180.
1080  // We use 179.999 (180 - epsilon) to avoid a full 360 deg rotation when
1081  // using 180 deg if the previous rotated position was already 180 deg
1082  m_camera.RotateZ_T1( glm::radians( 179.999f ) );
1084  return true;
1085 
1086  case ID_VIEW3D_TOP:
1089  m_camera.Reset_T1();
1090  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 0.5f ), 1.125f ) );
1091  return true;
1092 
1093  case ID_VIEW3D_BOTTOM:
1096  m_camera.Reset_T1();
1097  m_camera.RotateY_T1( glm::radians( 179.999f ) ); // Rotation = 180 - epsilon
1098  request_start_moving_camera( glm::min( glm::max( m_camera.ZoomGet(), 0.5f ), 1.125f ) );
1099  return true;
1100 
1101  case ID_VIEW3D_FLIP:
1104  m_camera.RotateY_T1( glm::radians( 179.999f ) );
1106  return true;
1107 
1108  default:
1109  return false;
1110  }
1111 
1112  m_mouse_was_moved = true;
1113 
1115 
1116  DisplayStatus();
1117  Request_refresh();
1118 
1119  return handled;
1120 }
1121 
1122 
1124 {
1125  switch( m_boardAdapter.GetRenderEngine() )
1126  {
1129  default: m_3d_render = nullptr; break;
1130  }
1131 
1132  if( m_3d_render )
1134 
1135  m_mouse_was_moved = false;
1136 
1137  Request_refresh();
1138 }
1139 
1140 
1142 {
1143  SFVEC3F rayOrigin;
1144  SFVEC3F rayDir;
1145 
1146  // Generate a ray origin and direction based on current mouser position and camera
1147  m_camera.MakeRayAtCurrrentMousePosition( rayOrigin, rayDir );
1148 
1149  RAY mouseRay;
1150  mouseRay.Init( rayOrigin, rayDir );
1151 
1152  return mouseRay;
1153 }
A wrapper for reporting to a specific text location in a statusbar.
Definition: reporter.h:262
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:543
bool IsReloadRequestPending() const
Query if there is a pending reload request.
void OnMouseMove(wxMouseEvent &event)
bool Zoom_T1(float aFactor)
Definition: camera.cpp:484
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:82
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:537
void OnCloseWindow(wxCloseEvent &event)
wxGLCanvas wrapper for HiDPI/Retina support.
class 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.
BOARD_ITEM * m_currentIntersectedBoardItem
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 TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
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:79
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.
void SetCurrentIntersectedBoardItem(BOARD_ITEM *aCurrentIntersectedBoardItem)
const wxString & GetName() const
Definition: pad.h:133
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:418
RENDER_3D_BASE * m_3d_render
EVT_MIDDLE_DOWN(mpWindow::OnMouseMiddleDown) EVT_MOUSEWHEEL(mpWindow
Definition: mathplot.cpp:1683
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:146
void Reload(REPORTER *aStatusReporter, REPORTER *aWarningReporter, bool aOnlyLoadCopperAndShapes)
const wxString & GetReference() const
Definition: footprint.h:423
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition: reporter.cpp:163
virtual void Reset_T1()
Definition: camera.cpp:101
ZONE handles a list of polygons defining a copper zone.
Definition: zone.h:57
class ZONE, a copper pour area
Definition: typeinfo.h:105
void Finalize()
Update the infobar with the reported text.
Definition: reporter.cpp:179
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:364
bool IsOnCopperLayer() const override
Definition: pad.h:217
void OnTimerTimeout_Redraw(wxTimerEvent &event)
SETTINGS_MANAGER * GetSettingsManager()
float ZoomGet() const
Definition: camera.cpp:504
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:461
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:197
void OnMiddleUp(wxMouseEvent &event)
bool SetCurWindowSize(const wxSize &aSize)
Update the windows size of the camera.
Definition: camera.cpp:435
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:549
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
Declaration of the eda_3d_viewer class.
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:563
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
virtual void DispatchWxCommand(wxCommandEvent &aEvent)
Process wxCommands (mostly menu related events) and runs appropriate actions (eg.
#define _(s)
Definition: 3d_actions.cpp:33
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
Function IsOnCopperLayer.
Definition: zone.cpp:221
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:581
class 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:399
Definition: pad.h:60
static int SetSwapInterval(int aVal)
Attempts to set the OpenGL swap interval.
Definition: gl_utils.h:40
void OglGetScreenshot(wxImage &aDstImage)
Get the pixel data of current OpenGL image.
Definition: ogl_utils.cpp:36
CAMERA & m_camera
wxTimer m_redraw_trigger_timer
Helper class to handle information needed to display 3D board.
Definition: board_adapter.h:67
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:291
Definition: track.h:83
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:162
void RotateX_T1(float aAngleInRadians)
Definition: camera.cpp:531
bool HasMessage() const override
Returns true if the reporter client is non-empty.
Definition: reporter.cpp:154
bool m_render_raytracing_was_requested