You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

466 lines
15 KiB

5 years ago
5 years ago
5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2016 Mario Luzeiro <mrluzeiro@ua.pt>
  5. * Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
  6. * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
  7. * Copyright (C) 2015-2020 KiCad Developers, see AUTHORS.txt for contributors.
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #include "panel_prev_3d.h"
  27. #include <3d_canvas/eda_3d_canvas.h>
  28. #include <tool/tool_manager.h>
  29. #include <tool/tool_dispatcher.h>
  30. #include <tools/3d_actions.h>
  31. #include <tools/3d_controller.h>
  32. #include <base_units.h>
  33. #include <bitmaps.h>
  34. #include <board.h>
  35. #include <common_ogl/cogl_att_list.h>
  36. #include <gal/dpi_scaling.h>
  37. #include <pgm_base.h>
  38. #include <project.h>
  39. #include <settings/common_settings.h>
  40. #include <widgets/infobar.h>
  41. PANEL_PREV_3D::PANEL_PREV_3D( wxWindow* aParent, PCB_BASE_FRAME* aFrame, FOOTPRINT* aFootprint,
  42. std::vector<FP_3DMODEL>* aParentModelList ) :
  43. PANEL_PREV_3D_BASE( aParent, wxID_ANY ),
  44. m_previewPane( nullptr ),
  45. m_infobar( nullptr ),
  46. m_boardAdapter(),
  47. m_currentCamera( m_trackBallCamera ),
  48. m_trackBallCamera( RANGE_SCALE_3D )
  49. {
  50. m_userUnits = aFrame->GetUserUnits();
  51. m_dummyBoard = new BOARD();
  52. // This board will only be used to hold a footprint for viewing
  53. m_dummyBoard->SetBoardUse( BOARD_USE::FPHOLDER );
  54. m_selected = -1;
  55. // Set the bitmap of 3D view buttons:
  56. m_bpvTop->SetBitmap( KiBitmap( axis3d_top_xpm ) );
  57. m_bpvFront->SetBitmap( KiBitmap( axis3d_front_xpm ) );
  58. m_bpvBack->SetBitmap( KiBitmap( axis3d_back_xpm ) );
  59. m_bpvLeft->SetBitmap( KiBitmap( axis3d_left_xpm ) );
  60. m_bpvRight->SetBitmap( KiBitmap( axis3d_right_xpm ) );
  61. m_bpvBottom->SetBitmap( KiBitmap( axis3d_bottom_xpm ) );
  62. m_bpvISO->SetBitmap( KiBitmap( ortho_xpm ) );
  63. m_bpUpdate->SetBitmap( KiBitmap( reload_xpm ) );
  64. // Set the min and max values of spin buttons (mandatory on Linux)
  65. // They are not used, so they are set to min and max 32 bits int values
  66. // (the min and max values supported by a wxSpinButton)
  67. // It avoids blocking the up or down arrows when reaching this limit after
  68. // a few clicks.
  69. wxSpinButton* spinButtonList[] =
  70. {
  71. m_spinXscale, m_spinYscale, m_spinZscale,
  72. m_spinXrot, m_spinYrot, m_spinZrot,
  73. m_spinXoffset,m_spinYoffset, m_spinZoffset
  74. };
  75. for( wxSpinButton* button : spinButtonList )
  76. button->SetRange(INT_MIN, INT_MAX );
  77. m_parentModelList = aParentModelList;
  78. m_dummyFootprint = new FOOTPRINT( *aFootprint );
  79. m_dummyBoard->Add( m_dummyFootprint );
  80. // Create the infobar
  81. m_infobar = new WX_INFOBAR( this );
  82. // Create the 3D canvas
  83. m_previewPane = new EDA_3D_CANVAS( this, COGL_ATT_LIST::GetAttributesList( ANTIALIASING_MODE::AA_8X ),
  84. m_dummyBoard, m_boardAdapter, m_currentCamera,
  85. aFrame->Prj().Get3DCacheManager() );
  86. loadCommonSettings();
  87. m_boardAdapter.SetFlag( FL_USE_SELECTION, false );
  88. // Create the manager
  89. m_toolManager = new TOOL_MANAGER;
  90. m_toolManager->SetEnvironment( m_dummyBoard, nullptr, nullptr, nullptr, this );
  91. m_actions = new EDA_3D_ACTIONS();
  92. m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager, m_actions );
  93. m_previewPane->SetEventDispatcher( m_toolDispatcher );
  94. // Register tools
  95. m_toolManager->RegisterTool( new EDA_3D_CONTROLLER );
  96. m_toolManager->InitTools();
  97. // Run the viewer control tool, it is supposed to be always active
  98. m_toolManager->InvokeTool( "3DViewer.Control" );
  99. m_SizerPanelView->Add( m_infobar, 0, wxEXPAND, 0 );
  100. m_SizerPanelView->Add( m_previewPane, 1, wxEXPAND, 5 );
  101. // Tell the canvas about the infobar
  102. if( m_infobar )
  103. m_previewPane->SetInfoBar( m_infobar );
  104. for( wxEventType eventType : { wxEVT_MENU_OPEN, wxEVT_MENU_CLOSE, wxEVT_MENU_HIGHLIGHT } )
  105. Connect( eventType, wxMenuEventHandler( PANEL_PREV_3D::OnMenuEvent ), NULL, this );
  106. #ifdef __WXOSX__
  107. // Call layout once to get the proper button sizes after the bitmaps have been set
  108. Layout();
  109. // The rounded-button style used has a small border on the left/right sides.
  110. // This is automatically fixed in wx for buttons with a bitmap < 20, but not
  111. // when the bitmap is set to be 26x26.
  112. wxSize borderFix = wxSize(4, 4);
  113. m_bpvTop->SetMinSize( m_bpvTop->GetSize() + borderFix );
  114. m_bpvFront->SetMinSize( m_bpvFront->GetSize() + borderFix );
  115. m_bpvBack->SetMinSize( m_bpvBack->GetSize() + borderFix );
  116. m_bpvLeft->SetMinSize( m_bpvLeft->GetSize() + borderFix );
  117. m_bpvRight->SetMinSize( m_bpvRight->GetSize() + borderFix );
  118. m_bpvBottom->SetMinSize( m_bpvBottom->GetSize() + borderFix );
  119. m_bpvISO->SetMinSize( m_bpvISO->GetSize() + borderFix );
  120. m_bpUpdate->SetMinSize( m_bpUpdate->GetSize() + borderFix );
  121. #endif
  122. }
  123. PANEL_PREV_3D::~PANEL_PREV_3D()
  124. {
  125. delete m_dummyBoard;
  126. delete m_previewPane;
  127. }
  128. void PANEL_PREV_3D::OnMenuEvent( wxMenuEvent& aEvent )
  129. {
  130. if( !m_toolDispatcher )
  131. aEvent.Skip();
  132. else
  133. m_toolDispatcher->DispatchWxEvent( aEvent );
  134. }
  135. void PANEL_PREV_3D::loadCommonSettings()
  136. {
  137. wxCHECK_RET( m_previewPane, "Cannot load settings to null canvas" );
  138. COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
  139. const DPI_SCALING dpi{ settings, this };
  140. m_previewPane->SetScaleFactor( dpi.GetScaleFactor() );
  141. // TODO(JE) use all control options
  142. m_boardAdapter.SetFlag( FL_MOUSEWHEEL_PANNING, settings->m_Input.scroll_modifier_zoom != 0 );
  143. }
  144. /**
  145. * @brief rotationFromString
  146. * Ensure -MAX_ROTATION <= rotation <= MAX_ROTATION
  147. * aRotation will be normalized between -MAX_ROTATION and MAX_ROTATION
  148. */
  149. static double rotationFromString( const wxString& aValue )
  150. {
  151. double rotation = DoubleValueFromString( EDA_UNITS::DEGREES, aValue ) / 10.0;
  152. if( rotation > MAX_ROTATION )
  153. {
  154. int n = KiROUND( rotation / MAX_ROTATION );
  155. rotation -= MAX_ROTATION * n;
  156. }
  157. else if( rotation < -MAX_ROTATION )
  158. {
  159. int n = KiROUND( -rotation / MAX_ROTATION );
  160. rotation += MAX_ROTATION * n;
  161. }
  162. return rotation;
  163. }
  164. wxString PANEL_PREV_3D::formatScaleValue( double aValue )
  165. {
  166. return wxString::Format( "%.4f", aValue );
  167. }
  168. wxString PANEL_PREV_3D::formatRotationValue( double aValue )
  169. {
  170. return wxString::Format( "%.2f %s", aValue, GetAbbreviatedUnitsLabel( EDA_UNITS::DEGREES ) );
  171. }
  172. wxString PANEL_PREV_3D::formatOffsetValue( double aValue )
  173. {
  174. // Convert from internal units (mm) to user units
  175. if( m_userUnits == EDA_UNITS::INCHES )
  176. aValue /= 25.4f;
  177. return wxString::Format( "%.4f %s", aValue, GetAbbreviatedUnitsLabel( m_userUnits ) );
  178. }
  179. void PANEL_PREV_3D::SetSelectedModel( int idx )
  180. {
  181. if( m_parentModelList && idx >= 0 && idx < (int) m_parentModelList->size() )
  182. {
  183. m_selected = idx;
  184. const FP_3DMODEL& modelInfo = m_parentModelList->at( (unsigned) m_selected );
  185. // Use ChangeValue() instead of SetValue(). It's not the user making the change, so we
  186. // don't want to generate wxEVT_GRID_CELL_CHANGED events.
  187. xscale->ChangeValue( formatScaleValue( modelInfo.m_Scale.x ) );
  188. yscale->ChangeValue( formatScaleValue( modelInfo.m_Scale.y ) );
  189. zscale->ChangeValue( formatScaleValue( modelInfo.m_Scale.z ) );
  190. xrot->ChangeValue( formatRotationValue( modelInfo.m_Rotation.x ) );
  191. yrot->ChangeValue( formatRotationValue( modelInfo.m_Rotation.y ) );
  192. zrot->ChangeValue( formatRotationValue( modelInfo.m_Rotation.z ) );
  193. xoff->ChangeValue( formatOffsetValue( modelInfo.m_Offset.x ) );
  194. yoff->ChangeValue( formatOffsetValue( modelInfo.m_Offset.y ) );
  195. zoff->ChangeValue( formatOffsetValue( modelInfo.m_Offset.z ) );
  196. m_opacity->SetValue( modelInfo.m_Opacity * 100.0 );
  197. }
  198. else
  199. {
  200. m_selected = -1;
  201. xscale->ChangeValue( wxEmptyString );
  202. yscale->ChangeValue( wxEmptyString );
  203. zscale->ChangeValue( wxEmptyString );
  204. xrot->ChangeValue( wxEmptyString );
  205. yrot->ChangeValue( wxEmptyString );
  206. zrot->ChangeValue( wxEmptyString );
  207. xoff->ChangeValue( wxEmptyString );
  208. yoff->ChangeValue( wxEmptyString );
  209. zoff->ChangeValue( wxEmptyString );
  210. m_opacity->SetValue( 100 );
  211. }
  212. }
  213. void PANEL_PREV_3D::updateOrientation( wxCommandEvent &event )
  214. {
  215. if( m_parentModelList && m_selected >= 0 && m_selected < (int) m_parentModelList->size() )
  216. {
  217. // Write settings back to the parent
  218. FP_3DMODEL* modelInfo = &m_parentModelList->at( (unsigned) m_selected );
  219. modelInfo->m_Scale.x = DoubleValueFromString( EDA_UNITS::UNSCALED, xscale->GetValue() );
  220. modelInfo->m_Scale.y = DoubleValueFromString( EDA_UNITS::UNSCALED, yscale->GetValue() );
  221. modelInfo->m_Scale.z = DoubleValueFromString( EDA_UNITS::UNSCALED, zscale->GetValue() );
  222. modelInfo->m_Rotation.x = rotationFromString( xrot->GetValue() );
  223. modelInfo->m_Rotation.y = rotationFromString( yrot->GetValue() );
  224. modelInfo->m_Rotation.z = rotationFromString( zrot->GetValue() );
  225. modelInfo->m_Offset.x = DoubleValueFromString( m_userUnits, xoff->GetValue() ) / IU_PER_MM;
  226. modelInfo->m_Offset.y = DoubleValueFromString( m_userUnits, yoff->GetValue() ) / IU_PER_MM;
  227. modelInfo->m_Offset.z = DoubleValueFromString( m_userUnits, zoff->GetValue() ) / IU_PER_MM;
  228. // Update the dummy footprint for the preview
  229. UpdateDummyFootprint( false );
  230. }
  231. }
  232. void PANEL_PREV_3D::onOpacitySlider( wxCommandEvent& event )
  233. {
  234. if( m_parentModelList && m_selected >= 0 && m_selected < (int) m_parentModelList->size() )
  235. {
  236. // Write settings back to the parent
  237. FP_3DMODEL* modelInfo = &m_parentModelList->at( (unsigned) m_selected );
  238. modelInfo->m_Opacity = m_opacity->GetValue() / 100.0;
  239. // Update the dummy footprint for the preview
  240. UpdateDummyFootprint( false );
  241. }
  242. }
  243. void PANEL_PREV_3D::doIncrementScale( wxSpinEvent& event, double aSign )
  244. {
  245. wxSpinButton* spinCtrl = (wxSpinButton*) event.GetEventObject();
  246. wxTextCtrl * textCtrl = xscale;
  247. if( spinCtrl == m_spinYscale )
  248. textCtrl = yscale;
  249. else if( spinCtrl == m_spinZscale )
  250. textCtrl = zscale;
  251. double curr_value = DoubleValueFromString( EDA_UNITS::UNSCALED, textCtrl->GetValue() );
  252. curr_value += ( SCALE_INCREMENT * aSign );
  253. curr_value = std::max( 1/MAX_SCALE, curr_value );
  254. curr_value = std::min( curr_value, MAX_SCALE );
  255. textCtrl->SetValue( formatScaleValue( curr_value ) );
  256. }
  257. void PANEL_PREV_3D::doIncrementRotation( wxSpinEvent& aEvent, double aSign )
  258. {
  259. wxSpinButton* spinCtrl = (wxSpinButton*) aEvent.GetEventObject();
  260. wxTextCtrl* textCtrl = xrot;
  261. if( spinCtrl == m_spinYrot )
  262. textCtrl = yrot;
  263. else if( spinCtrl == m_spinZrot )
  264. textCtrl = zrot;
  265. double curr_value = DoubleValueFromString( EDA_UNITS::DEGREES, textCtrl->GetValue() ) / 10.0;
  266. curr_value += ( ROTATION_INCREMENT * aSign );
  267. curr_value = std::max( -MAX_ROTATION, curr_value );
  268. curr_value = std::min( curr_value, MAX_ROTATION );
  269. textCtrl->SetValue( formatRotationValue( curr_value ) );
  270. }
  271. void PANEL_PREV_3D::doIncrementOffset( wxSpinEvent& event, double aSign )
  272. {
  273. wxSpinButton* spinCtrl = (wxSpinButton*) event.GetEventObject();
  274. wxTextCtrl * textCtrl = xoff;
  275. if( spinCtrl == m_spinYoffset )
  276. textCtrl = yoff;
  277. else if( spinCtrl == m_spinZoffset )
  278. textCtrl = zoff;
  279. double step = OFFSET_INCREMENT_MM;
  280. if( m_userUnits == EDA_UNITS::INCHES )
  281. step = OFFSET_INCREMENT_MIL/1000.0;
  282. double curr_value = DoubleValueFromString( m_userUnits, textCtrl->GetValue() ) / IU_PER_MM;
  283. curr_value += ( step * aSign );
  284. curr_value = std::max( -MAX_OFFSET, curr_value );
  285. curr_value = std::min( curr_value, MAX_OFFSET );
  286. textCtrl->SetValue( formatOffsetValue( curr_value ) );
  287. }
  288. void PANEL_PREV_3D::onMouseWheelScale( wxMouseEvent& event )
  289. {
  290. wxTextCtrl* textCtrl = (wxTextCtrl*) event.GetEventObject();
  291. double step = SCALE_INCREMENT;
  292. if( event.ShiftDown( ) )
  293. step = SCALE_INCREMENT_FINE;
  294. if( event.GetWheelRotation() >= 0 )
  295. step = -step;
  296. double curr_value = DoubleValueFromString( EDA_UNITS::UNSCALED, textCtrl->GetValue() );
  297. curr_value += step;
  298. curr_value = std::max( 1/MAX_SCALE, curr_value );
  299. curr_value = std::min( curr_value, MAX_SCALE );
  300. textCtrl->SetValue( formatScaleValue( curr_value ) );
  301. }
  302. void PANEL_PREV_3D::onMouseWheelRot( wxMouseEvent& event )
  303. {
  304. wxTextCtrl* textCtrl = (wxTextCtrl*) event.GetEventObject();
  305. double step = ROTATION_INCREMENT_WHEEL;
  306. if( event.ShiftDown( ) )
  307. step = ROTATION_INCREMENT_WHEEL_FINE;
  308. if( event.GetWheelRotation() >= 0 )
  309. step = -step;
  310. double curr_value = DoubleValueFromString( EDA_UNITS::DEGREES, textCtrl->GetValue() ) / 10.0;
  311. curr_value += step;
  312. curr_value = std::max( -MAX_ROTATION, curr_value );
  313. curr_value = std::min( curr_value, MAX_ROTATION );
  314. textCtrl->SetValue( formatRotationValue( curr_value ) );
  315. }
  316. void PANEL_PREV_3D::onMouseWheelOffset( wxMouseEvent& event )
  317. {
  318. wxTextCtrl* textCtrl = (wxTextCtrl*) event.GetEventObject();
  319. double step = OFFSET_INCREMENT_MM;
  320. if( event.ShiftDown( ) )
  321. step = OFFSET_INCREMENT_MM_FINE;
  322. if( m_userUnits == EDA_UNITS::INCHES )
  323. {
  324. step = OFFSET_INCREMENT_MIL/1000.0;
  325. if( event.ShiftDown( ) )
  326. step = OFFSET_INCREMENT_MIL_FINE/1000.0;
  327. }
  328. if( event.GetWheelRotation() >= 0 )
  329. step = -step;
  330. double curr_value = DoubleValueFromString( m_userUnits, textCtrl->GetValue() ) / IU_PER_MM;
  331. curr_value += step;
  332. curr_value = std::max( -MAX_OFFSET, curr_value );
  333. curr_value = std::min( curr_value, MAX_OFFSET );
  334. textCtrl->SetValue( formatOffsetValue( curr_value ) );
  335. }
  336. void PANEL_PREV_3D::UpdateDummyFootprint( bool aReloadRequired )
  337. {
  338. m_dummyFootprint->Models().clear();
  339. for( FP_3DMODEL& model : *m_parentModelList)
  340. {
  341. if( model.m_Show )
  342. m_dummyFootprint->Models().push_back( model );
  343. }
  344. if( aReloadRequired )
  345. m_previewPane->ReloadRequest();
  346. m_previewPane->Request_refresh();
  347. }