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 <[email protected]>
5  * Copyright (C) 1992-2021 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, wxT( "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_opengl = new RENDER_3D_OPENGL( this, m_boardAdapter, m_camera );
131 
132  wxASSERT( m_3d_render_raytracing != nullptr );
133  wxASSERT( m_3d_render_opengl != 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_opengl->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, wxT( "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_opengl;
189  m_3d_render_opengl = 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, wxT( "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, wxT( "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, wxT( "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( wxT( "." ) ) );
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, wxT( "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, wxT( "EDA_3D_CANVAS::%s OpenGL not supported." ),
278  __WXFUNCTION__ );
279 
281  }
282  }
283 
286 
287  return true;
288 }
289 
290 
291 void EDA_3D_CANVAS::GetScreenshot( wxImage& aDstImage )
292 {
293  OglGetScreenshot( aDstImage );
294 }
295 
296 
297 void EDA_3D_CANVAS::ReloadRequest( BOARD* aBoard , S3D_CACHE* aCachePointer )
298 {
299  if( aCachePointer != nullptr )
300  m_boardAdapter.Set3dCacheManager( aCachePointer );
301 
302  if( aBoard != nullptr )
303  m_boardAdapter.SetBoard( aBoard );
304 
305  m_boardAdapter.SetColorSettings( Pgm().GetSettingsManager().GetColorSettings() );
306 
307  if( m_3d_render )
309 }
310 
311 
313 {
315 
316  if( m_3d_render )
318 
320 
321  Request_refresh();
322 }
323 
324 
326 {
327  if( m_parentStatusBar )
328  {
329  wxString msg;
330 
331  msg.Printf( wxT( "dx %3.2f" ), m_camera.GetCameraPos().x );
332  m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::X_POS ) );
333 
334  msg.Printf( wxT( "dy %3.2f" ), m_camera.GetCameraPos().y );
335  m_parentStatusBar->SetStatusText( msg, static_cast<int>( EDA_3D_VIEWER_STATUSBAR::Y_POS ) );
336  }
337 }
338 
339 
340 void EDA_3D_CANVAS::OnPaint( wxPaintEvent& aEvent )
341 {
342  // Please have a look at: https://lists.launchpad.net/kicad-developers/msg25149.html
343  DoRePaint();
344 }
345 
346 
348 {
349  if( m_is_currently_painting.test_and_set() )
350  return;
351 
352  // SwapBuffer requires the window to be shown before calling
353  if( !IsShownOnScreen() )
354  {
355  wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::DoRePaint !IsShown" ) );
356  m_is_currently_painting.clear();
357  return;
358  }
359 
360  // Because the board to draw is handled by the parent viewer frame,
361  // ensure this parent is still alive. When it is closed before the viewer
362  // frame, a paint event can be generated after the parent is closed,
363  // therefore with invalid board.
364  // This is dependent of the platform.
365  // Especially on OSX, but also on Windows, it frequently happens
366  if( !GetParent()->GetParent()->IsShown() )
367  return; // The parent board editor frame is no more alive
368 
369  wxString err_messages;
370 
371  // !TODO: implement error reporter
372  INFOBAR_REPORTER warningReporter( m_parentInfoBar );
373  STATUSBAR_REPORTER activityReporter( m_parentStatusBar,
375 
376  unsigned strtime = GetRunningMicroSecs();
377 
378  // "Makes the OpenGL state that is represented by the OpenGL rendering
379  // context context current, i.e. it will be used by all subsequent OpenGL calls.
380  // This function may only be called when the window is shown on screen"
381 
382  // Explicitly create a new rendering context instance for this canvas.
383  if( m_glRC == nullptr )
385 
387 
388  // Set the OpenGL viewport according to the client size of this canvas.
389  // This is done here rather than in a wxSizeEvent handler because our
390  // OpenGL rendering context (and thus viewport setting) is used with
391  // multiple canvases: If we updated the viewport in the wxSizeEvent
392  // handler, changing the size of one canvas causes a viewport setting that
393  // is wrong when next another canvas is repainted.
394  wxSize clientSize = GetNativePixelSize();
395 
396  const bool windows_size_changed = m_camera.SetCurWindowSize( clientSize );
397 
398  // Initialize openGL if need
400  {
401  if( !initializeOpenGL() )
402  {
404  m_is_currently_painting.clear();
405 
406  return;
407  }
408 
410  {
411  warningReporter.Report( _( "Your OpenGL version is not supported. Minimum required "
412  "is 1.5." ), RPT_SEVERITY_ERROR );
413 
414  warningReporter.Finalize();
415  }
416  }
417 
419  {
420  glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
421  glClear( GL_COLOR_BUFFER_BIT );
422 
423  SwapBuffers();
424 
426  m_is_currently_painting.clear();
427 
428  return;
429  }
430 
431  // Don't attend to ray trace if OpenGL doesn't support it.
433  {
437  }
438 
439  // Check if a raytacing was requested and need to switch to raytracing mode
441  {
442  const bool was_camera_changed = m_camera.ParametersChanged();
443 
444  // It reverts back to OpenGL mode if it was requested a raytracing
445  // render of the current scene. AND the mouse / camera is moving
446  if( ( m_mouse_is_moving || m_camera_is_moving || was_camera_changed
447  || windows_size_changed )
449  {
452  }
453  }
454 
455  float curtime_delta_s = 0.0f;
456 
457  if( m_camera_is_moving )
458  {
459  const unsigned curtime_delta = GetRunningMicroSecs() - m_strtime_camera_movement;
460  curtime_delta_s = (curtime_delta / 1e6) * m_camera_moving_speed;
461  m_camera.Interpolate( curtime_delta_s );
462 
463  if( curtime_delta_s > 1.0f )
464  {
465  m_render_pivot = false;
466  m_camera_is_moving = false;
467  m_mouse_was_moved = true;
468 
470  DisplayStatus();
471  }
472  else
473  {
474  Request_refresh();
475  }
476  }
477 
478  // It will return true if the render request a new redraw
479  bool requested_redraw = false;
480 
481  if( m_3d_render )
482  {
483  try
484  {
485  m_3d_render->SetCurWindowSize( clientSize );
486 
487  bool reloadRaytracingForCalculations = false;
488 
491  {
492  reloadRaytracingForCalculations = true;
493  }
494 
495  requested_redraw = m_3d_render->Redraw( m_mouse_was_moved || m_camera_is_moving,
496  &activityReporter, &warningReporter );
497 
498  // Raytracer renderer is responsible for some features also used by the OpenGL
499  // renderer.
500  // FIXME: Presumably because raytracing renderer reload is called only after current
501  // renderer redraw, the old zoom value stays for a single frame. This is ugly, but only
502  // cosmetic, so I'm not fixing that for now: I don't know how to do this without
503  // reloading twice (maybe it's not too bad of an idea?) or doing a complicated
504  // refactor.
505  if( reloadRaytracingForCalculations )
506  m_3d_render_raytracing->Reload( nullptr, nullptr, true );
507  }
508  catch( std::runtime_error& )
509  {
512  m_is_opengl_initialized = false;
514  m_is_currently_painting.clear();
515  return;
516  }
517  }
518 
519  if( m_render_pivot )
520  {
521  const float scale = glm::min( m_camera.GetZoom(), 1.0f );
522  render_pivot( curtime_delta_s, scale );
523  }
524 
525  // "Swaps the double-buffer of this window, making the back-buffer the
526  // front-buffer and vice versa, so that the output of the previous OpenGL
527  // commands is displayed on the window."
528  SwapBuffers();
529 
531 
532  if( !activityReporter.HasMessage() )
533  {
535  {
536  // Calculation time in milliseconds
537  const double calculation_time = (double)( GetRunningMicroSecs() - strtime) / 1e3;
538 
539  activityReporter.Report( wxString::Format( _( "Last render time %.0f ms" ),
540  calculation_time ) );
541  }
542  }
543 
544  // This will reset the flag of camera parameters changed
546 
547  warningReporter.Finalize();
548 
549  if( !err_messages.IsEmpty() )
550  wxLogMessage( err_messages );
551 
552  if( ( !m_camera_is_moving ) && requested_redraw )
553  {
554  m_mouse_was_moved = false;
555  Request_refresh( false );
556  }
557 
558  m_is_currently_painting.clear();
559 }
560 
561 
563 {
564  m_eventDispatcher = aEventDispatcher;
565 }
566 
567 
568 void EDA_3D_CANVAS::OnEvent( wxEvent& aEvent )
569 {
570  if( !m_eventDispatcher )
571  aEvent.Skip();
572  else
574 
575  Refresh();
576 }
577 
578 
579 void EDA_3D_CANVAS::OnEraseBackground( wxEraseEvent& event )
580 {
581  wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnEraseBackground" ) );
582  // Do nothing, to avoid flashing.
583 }
584 
585 
586 void EDA_3D_CANVAS::OnMouseWheel( wxMouseEvent& event )
587 {
588  bool mouseActivity = false;
589 
590  wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnMouseWheel" ) );
591 
592  if( m_camera_is_moving )
593  return;
594 
595  float delta_move = m_delta_move_step_factor * m_camera.GetZoom();
596 
598  delta_move *= 0.01f * event.GetWheelRotation();
599  else
600  if( event.GetWheelRotation() < 0 )
601  delta_move = -delta_move;
602 
603  // mousewheel_panning enabled:
604  // wheel -> pan;
605  // wheel + shift -> horizontal scrolling;
606  // wheel + ctrl -> zooming;
607  // mousewheel_panning disabled:
608  // wheel + shift -> vertical scrolling;
609  // wheel + ctrl -> horizontal scrolling;
610  // wheel -> zooming.
611 
612  if( m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) && !event.ControlDown() )
613  {
614  if( event.GetWheelAxis() == wxMOUSE_WHEEL_HORIZONTAL || event.ShiftDown() )
615  m_camera.Pan( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
616  else
617  m_camera.Pan( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
618 
619  mouseActivity = true;
620  }
621  else if( event.ShiftDown() && !m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) )
622  {
623  m_camera.Pan( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
624  mouseActivity = true;
625  }
626  else if( event.ControlDown() && !m_boardAdapter.GetFlag( FL_MOUSEWHEEL_PANNING ) )
627  {
628  m_camera.Pan( SFVEC3F( delta_move, 0.0f, 0.0f ) );
629  mouseActivity = true;
630  }
631  else
632  {
633  mouseActivity = m_camera.Zoom( event.GetWheelRotation() > 0 ? 1.1f : 1/1.1f );
634  }
635 
636  // If it results on a camera movement
637  if( mouseActivity )
638  {
639  DisplayStatus();
640  Request_refresh();
641 
642  m_mouse_is_moving = true;
643  m_mouse_was_moved = true;
644 
646  }
647 
648  // Update the cursor current mouse position on the camera
649  m_camera.SetCurMousePosition( GetNativePosition( event.GetPosition() ) );
650 }
651 
652 
653 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
654 void EDA_3D_CANVAS::OnMagnify( wxMouseEvent& event )
655 {
656  SetFocus();
657 
658  if( m_camera_is_moving )
659  return;
660 
661  //m_is_moving_mouse = true;
663 
664  float magnification = ( event.GetMagnification() + 1.0f );
665 
666  m_camera.Zoom( magnification );
667 
668  DisplayStatus();
669  Request_refresh();
670 }
671 #endif
672 
673 
674 void EDA_3D_CANVAS::OnMouseMove( wxMouseEvent& event )
675 {
676  //wxLogTrace( m_logTrace, wxT( "EDA_3D_CANVAS::OnMouseMove" ) );
677 
678  if( m_camera_is_moving )
679  return;
680 
681  const wxSize& nativeWinSize = GetNativePixelSize();
682  const wxPoint& nativePosition = GetNativePosition( event.GetPosition() );
683 
684  m_camera.SetCurWindowSize( nativeWinSize );
685 
686  if( event.Dragging() )
687  {
688  if( event.LeftIsDown() ) // Drag
689  m_camera.Drag( nativePosition );
690  else if( event.MiddleIsDown() ) // Pan
691  m_camera.Pan( nativePosition );
692 
693  m_mouse_is_moving = true;
694  m_mouse_was_moved = true;
695 
696  // orientation has changed, redraw mesh
697  DisplayStatus();
698  Request_refresh();
699  }
700 
701  m_camera.SetCurMousePosition( nativePosition );
702 
703  if( !event.Dragging() && m_boardAdapter.GetRenderEngine() == RENDER_ENGINE::OPENGL )
704  {
706  static_cast<int>( EDA_3D_VIEWER_STATUSBAR::HOVERED_ITEM ) );
707 
708  RAY mouseRay = getRayAtCurrrentMousePosition();
709 
710  BOARD_ITEM *rollOverItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
711 
712  if( rollOverItem )
713  {
714  if( rollOverItem != m_currentRollOverItem )
715  {
717  m_currentRollOverItem = rollOverItem;
718 
719  Request_refresh();
720  }
721 
722  switch( rollOverItem->Type() )
723  {
724  case PCB_PAD_T:
725  {
726  PAD* pad = dynamic_cast<PAD*>( rollOverItem );
727 
728  if( pad && pad->IsOnCopperLayer() )
729  {
730  reporter.Report( wxString::Format( _( "Net %s\tNetClass %s\tPadName %s" ),
731  pad->GetNet()->GetNetname(),
732  pad->GetNet()->GetNetClassName(),
733  pad->GetNumber() ) );
734  }
735  }
736  break;
737 
738  case PCB_FOOTPRINT_T:
739  {
740  FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( rollOverItem );
741 
742  if( footprint )
743  reporter.Report( footprint->GetReference() );
744  }
745  break;
746 
747  case PCB_TRACE_T:
748  case PCB_VIA_T:
749  case PCB_ARC_T:
750  {
751  PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( rollOverItem );
752 
753  if( track )
754  {
755  reporter.Report( wxString::Format( _( "Net %s\tNetClass %s" ),
756  track->GetNet()->GetNetname(),
757  track->GetNet()->GetNetClassName() ) );
758  }
759  }
760  break;
761 
762  case PCB_ZONE_T:
763  {
764  ZONE* zone = dynamic_cast<ZONE*>( rollOverItem );
765 
766  if( zone && zone->IsOnCopperLayer() )
767  {
768  reporter.Report( wxString::Format( _( "Net %s\tNetClass %s" ),
769  zone->GetNet()->GetNetname(),
770  zone->GetNet()->GetNetClassName() ) );
771  }
772  }
773  break;
774 
775  default:
776  break;
777  }
778  }
779  else
780  {
782  {
784  Request_refresh();
785 
786  reporter.Report( wxEmptyString );
787  }
788 
789  m_currentRollOverItem = nullptr;
790  }
791  }
792 }
793 
794 
795 void EDA_3D_CANVAS::OnLeftDown( wxMouseEvent& event )
796 {
797  SetFocus();
799 
800  if( !event.Dragging() && ( m_3d_render_raytracing != nullptr ) )
801  {
802  RAY mouseRay = getRayAtCurrrentMousePosition();
803 
804  BOARD_ITEM *intersectedBoardItem = m_3d_render_raytracing->IntersectBoardItem( mouseRay );
805 
806  // !TODO: send a selection item to pcbnew, eg: via kiway?
807  }
808 }
809 
810 
811 void EDA_3D_CANVAS::OnLeftUp( wxMouseEvent& event )
812 {
813  if( m_camera_is_moving )
814  return;
815 
816  if( m_mouse_is_moving )
817  {
818  m_mouse_is_moving = false;
820  }
821 }
822 
823 
824 void EDA_3D_CANVAS::OnMiddleDown( wxMouseEvent& event )
825 {
826  SetFocus();
828 }
829 
830 
831 void EDA_3D_CANVAS::OnMiddleUp( wxMouseEvent& event )
832 {
833  if( m_camera_is_moving )
834  return;
835 
836  if( m_mouse_is_moving )
837  {
838  m_mouse_is_moving = false;
840  }
841  else
842  {
844  }
845 }
846 
847 
848 void EDA_3D_CANVAS::OnTimerTimeout_Editing( wxTimerEvent& /* event */ )
849 {
850  m_mouse_is_moving = false;
851  m_mouse_was_moved = false;
852 
853  Request_refresh();
854 }
855 
856 
858 {
860 }
861 
862 
864 {
865  if( m_3d_render )
866  m_editing_timeout_timer.Start( m_3d_render->GetWaitForEditingTimeOut(), wxTIMER_ONE_SHOT );
867 }
868 
869 
870 void EDA_3D_CANVAS::OnTimerTimeout_Redraw( wxTimerEvent& event )
871 {
872  Request_refresh( true );
873 }
874 
875 
876 void EDA_3D_CANVAS::OnRefreshRequest( wxEvent& aEvent )
877 {
878  DoRePaint();
879 }
880 
881 
882 void EDA_3D_CANVAS::Request_refresh( bool aRedrawImmediately )
883 {
884  if( aRedrawImmediately )
885  {
886  // Just calling Refresh() does not work always
887  // Using an event to call DoRepaint ensure the repaint code will be executed,
888  // and PostEvent will take priority to other events like mouse movements, keys, etc.
889  // and is executed during the next idle time
890  wxCommandEvent redrawEvent( wxEVT_REFRESH_CUSTOM_COMMAND, ID_CUSTOM_EVENT_1 );
891  wxPostEvent( this, redrawEvent );
892  }
893  else
894  {
895  // Schedule a timed redraw
896  m_redraw_trigger_timer.Start( 10 , wxTIMER_ONE_SHOT );
897  }
898 }
899 
900 
901 void EDA_3D_CANVAS::request_start_moving_camera( float aMovingSpeed, bool aRenderPivot )
902 {
903  wxASSERT( aMovingSpeed > FLT_EPSILON );
904 
905  // Fast forward the animation if the animation is disabled
906  if( !m_animation_enabled )
907  {
908  m_camera.Interpolate( 1.0f );
909  DisplayStatus();
910  Request_refresh();
911  return;
912  }
913 
914  // Map speed multiplier option to actual multiplier value
915  // [1,2,3,4,5] -> [0.25, 0.5, 1, 2, 4]
916  aMovingSpeed *= ( 1 << m_moving_speed_multiplier ) / 8.0f;
917 
918  m_render_pivot = aRenderPivot;
919  m_camera_moving_speed = aMovingSpeed;
920 
922 
923  DisplayStatus();
924  Request_refresh();
925 
926  m_camera_is_moving = true;
927 
929 }
930 
931 
933 {
934  RAY mouseRay = getRayAtCurrrentMousePosition();
935 
936  float hit_t;
937 
938  // Test it with the board bounding box
939  if( m_boardAdapter.GetBBox().Intersect( mouseRay, &hit_t ) )
940  {
943  m_camera.SetLookAtPos_T1( mouseRay.at( hit_t ) );
945 
947  }
948 }
949 
950 
951 bool EDA_3D_CANVAS::SetView3D( int aKeycode )
952 {
953  if( m_camera_is_moving )
954  return false;
955 
956  const float delta_move = m_delta_move_step_factor * m_camera.GetZoom();
957  const float arrow_moving_time_speed = 8.0f;
958  bool handled = false;
959 
960  switch( aKeycode )
961  {
962  case WXK_SPACE:
964  return true;
965 
966  case WXK_LEFT:
969  m_camera.Pan_T1( SFVEC3F( -delta_move, 0.0f, 0.0f ) );
970  request_start_moving_camera( arrow_moving_time_speed, false );
971  return true;
972 
973  case WXK_RIGHT:
976  m_camera.Pan_T1( SFVEC3F( +delta_move, 0.0f, 0.0f ) );
977  request_start_moving_camera( arrow_moving_time_speed, false );
978  return true;
979 
980  case WXK_UP:
983  m_camera.Pan_T1( SFVEC3F( 0.0f, +delta_move, 0.0f ) );
984  request_start_moving_camera( arrow_moving_time_speed, false );
985  return true;
986 
987  case WXK_DOWN:
990  m_camera.Pan_T1( SFVEC3F( 0.0f, -delta_move, 0.0f ) );
991  request_start_moving_camera( arrow_moving_time_speed, false );
992  return true;
993 
994  case WXK_HOME:
997  m_camera.Reset_T1();
998  request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 1 / 1.26f ), 1.26f ) );
999  return true;
1000 
1001  case WXK_END:
1002  break;
1003 
1004  case WXK_TAB:
1007  m_camera.RotateZ_T1( glm::radians( 45.0f ) );
1009  handled = true;
1010  break;
1011 
1012  case WXK_F1:
1015 
1016  if( m_camera.Zoom_T1( 1.26f ) ) // 3 steps per doubling
1018 
1019  return true;
1020 
1021  case WXK_F2:
1024 
1025  if( m_camera.Zoom_T1( 1/1.26f ) ) // 3 steps per halving
1027 
1028  return true;
1029 
1030  case ID_VIEW3D_RESET:
1033  m_camera.Reset_T1();
1034  request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
1035  return true;
1036 
1037  case ID_VIEW3D_RIGHT:
1040  m_camera.Reset_T1();
1041  m_camera.RotateZ_T1( glm::radians( -90.0f ) );
1042  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1044  return true;
1045 
1046  case ID_VIEW3D_LEFT:
1049  m_camera.Reset_T1();
1050  m_camera.RotateZ_T1( glm::radians( 90.0f ) );
1051  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1053  return true;
1054 
1055  case ID_VIEW3D_FRONT:
1058  m_camera.Reset_T1();
1059  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1061  return true;
1062 
1063  case ID_VIEW3D_BACK:
1066  m_camera.Reset_T1();
1067  m_camera.RotateX_T1( glm::radians( -90.0f ) );
1068 
1069  // The rotation angle should be 180.
1070  // We use 179.999 (180 - epsilon) to avoid a full 360 deg rotation when
1071  // using 180 deg if the previous rotated position was already 180 deg
1072  m_camera.RotateZ_T1( glm::radians( 179.999f ) );
1074  return true;
1075 
1076  case ID_VIEW3D_TOP:
1079  m_camera.Reset_T1();
1080  request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
1081  return true;
1082 
1083  case ID_VIEW3D_BOTTOM:
1086  m_camera.Reset_T1();
1087  m_camera.RotateY_T1( glm::radians( 179.999f ) ); // Rotation = 180 - epsilon
1088  request_start_moving_camera( glm::min( glm::max( m_camera.GetZoom(), 0.5f ), 1.125f ) );
1089  return true;
1090 
1091  case ID_VIEW3D_FLIP:
1094  m_camera.RotateY_T1( glm::radians( 179.999f ) );
1096  return true;
1097 
1098  default:
1099  return false;
1100  }
1101 
1102  m_mouse_was_moved = true;
1103 
1105 
1106  DisplayStatus();
1107  Request_refresh();
1108 
1109  return handled;
1110 }
1111 
1112 
1114 {
1115  switch( m_boardAdapter.GetRenderEngine() )
1116  {
1119  default: m_3d_render = nullptr; break;
1120  }
1121 
1122  if( m_3d_render )
1124 
1125  m_mouse_was_moved = false;
1126 
1127  Request_refresh();
1128 }
1129 
1130 
1132 {
1133  SFVEC3F rayOrigin;
1134  SFVEC3F rayDir;
1135 
1136  // Generate a ray origin and direction based on current mouser position and camera
1137  m_camera.MakeRayAtCurrrentMousePosition( rayOrigin, rayDir );
1138 
1139  RAY mouseRay;
1140  mouseRay.Init( rayOrigin, rayDir );
1141 
1142  return mouseRay;
1143 }
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:123
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:552
bool IsReloadRequestPending() const
Query if there is a pending reload request.
void OnMouseMove(wxMouseEvent &event)
Object to render the board using openGL.
bool Zoom_T1(float aFactor)
Definition: camera.cpp:497
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:49
RAY getRayAtCurrrentMousePosition()
wxString GetNetClassName() const
Definition: netinfo.h:115
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:35
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:546
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:62
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:84
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.
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
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:439
RENDER_3D_BASE * m_3d_render
float GetZoom() const
Definition: camera.h:177
EVT_MIDDLE_DOWN(mpWindow::OnMouseMiddleDown) EVT_MOUSEWHEEL(mpWindow
Definition: mathplot.cpp:1586
const wxString & GetNetname() const
Definition: netinfo.h:126
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:463
#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:103
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:379
void OnTimerTimeout_Redraw(wxTimerEvent &event)
SETTINGS_MANAGER * GetSettingsManager()
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:482
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:213
void OnMiddleUp(wxMouseEvent &event)
wxPoint GetNativePosition(const wxPoint &aPoint) const
Convert the given point from client coordinates to native pixel coordinates.
bool SetCurWindowSize(const wxSize &aSize)
Update the windows size of the camera.
Definition: camera.cpp:456
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:558
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:572
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:191
unsigned GetRunningMicroSecs()
An alternate way to calculate an elapsed 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:228
RENDER_3D_OPENGL * m_3d_render_opengl
const SFVEC3F & GetCameraPos() const
Definition: camera.h:130
void OnTimerTimeout_Editing(wxTimerEvent &event)
void ReloadRequest(BOARD *aBoard=nullptr, S3D_CACHE *aCachePointer=nullptr)
void SetCurrentRollOverItem(BOARD_ITEM *aRollOverItem)
bool ParametersChanged()
Definition: camera.cpp:590
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:420
Definition: pad.h:57
static int SetSwapInterval(int aVal)
Attempts to set the OpenGL swap interval.
Definition: gl_utils.h:42
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:112
void RotateX_T1(float aAngleInRadians)
Definition: camera.cpp:540
bool HasMessage() const override
Returns true if the reporter client is non-empty.
Definition: reporter.cpp:195
bool m_render_raytracing_was_requested