diff --git a/3d-viewer/CMakeLists.txt b/3d-viewer/CMakeLists.txt index d48dabadb3..87bfde723f 100644 --- a/3d-viewer/CMakeLists.txt +++ b/3d-viewer/CMakeLists.txt @@ -124,5 +124,5 @@ add_subdirectory( 3d_cache ) if( KICAD_USE_3DCONNEXION ) message( STATUS "Including 3Dconnexion SpaceMouse navigation support in 3d-viewer" ) add_subdirectory( 3d_navlib ) - target_link_libraries( 3d-viewer 3d-viewer_navlib) + target_link_libraries( 3d-viewer PRIVATE 3d-viewer_navlib) endif() diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 0e908c4bc7..510367b6d2 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -714,6 +714,12 @@ target_link_libraries( pcbnew_kiface ${PCBNEW_KIFACE_LIBRARIES} ) +if( KICAD_USE_3DCONNEXION ) + message( STATUS "Including 3Dconnexion SpaceMouse navigation support in PcbNew" ) + add_subdirectory( navlib ) + target_link_libraries( pcbnew_kiface pcbnew_navlib) +endif() + set_source_files_properties( pcbnew.cpp PROPERTIES # The KIFACE is in pcbnew.cpp, export it: COMPILE_DEFINITIONS "BUILD_KIWAY_DLL;COMPILING_DLL" diff --git a/pcbnew/navlib/CMakeLists.txt b/pcbnew/navlib/CMakeLists.txt new file mode 100644 index 0000000000..e52fd51fb0 --- /dev/null +++ b/pcbnew/navlib/CMakeLists.txt @@ -0,0 +1,27 @@ +if( KICAD_USE_3DCONNEXION ) + add_library(pcbnew_navlib STATIC + "nl_pcbnew_plugin.cpp" + "nl_pcbnew_plugin_impl.cpp" + ) + + # Find the 3DxWare SDK component 3DxWare::NlClient + # find_package(TDxWare_SDK 4.0 REQUIRED COMPONENTS 3DxWare::Navlib) + target_compile_definitions(pcbnew_navlib PRIVATE + $ + ) + target_compile_options(pcbnew_navlib PRIVATE + $ + ) + target_include_directories(pcbnew_navlib PRIVATE + $ + $ + ) + target_link_libraries(pcbnew_navlib + $ + 3DxWare::Navlib + ) +else() + add_library(pcbnew_navlib STATIC + "nl_pcbnew_plugin.cpp" + ) +endif(KICAD_USE_3DCONNEXION) diff --git a/pcbnew/navlib/nl_pcbnew_plugin.cpp b/pcbnew/navlib/nl_pcbnew_plugin.cpp new file mode 100644 index 0000000000..ab1d6def5d --- /dev/null +++ b/pcbnew/navlib/nl_pcbnew_plugin.cpp @@ -0,0 +1,52 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2021 3Dconnexion + * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "nl_pcbnew_plugin.h" +#if defined( KICAD_USE_3DCONNEXION ) + +#include "nl_pcbnew_plugin_impl.h" + +NL_PCBNEW_PLUGIN::NL_PCBNEW_PLUGIN( PCB_DRAW_PANEL_GAL* aViewport ) : + m_impl( new NL_PCBNEW_PLUGIN_IMPL( aViewport ) ) +{ +} + +NL_PCBNEW_PLUGIN::~NL_PCBNEW_PLUGIN() +{ + delete m_impl; +} + +void NL_PCBNEW_PLUGIN::SetFocus( bool focus ) +{ + m_impl->SetFocus( focus ); +} +#else +NL_PCBNEW_PLUGIN::NL_PCBNEW_PLUGIN( PCB_DRAW_PANEL_GAL* aViewport ) +{ +} + +void NL_PCBNEW_PLUGIN::SetFocus( bool focus ) +{ +} + +NL_PCBNEW_PLUGIN::~NL_PCBNEW_PLUGIN() +{ +} +#endif diff --git a/pcbnew/navlib/nl_pcbnew_plugin.h b/pcbnew/navlib/nl_pcbnew_plugin.h new file mode 100644 index 0000000000..289c521247 --- /dev/null +++ b/pcbnew/navlib/nl_pcbnew_plugin.h @@ -0,0 +1,60 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2021 3Dconnexion + * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +/** + * @file nl_pcbnew_plugin.h + * @brief Declaration of the NL_PCBNEW_PLUGIN class + */ + +#ifndef NL_PCBNEW_PLUGIN_H_ +#define NL_PCBNEW_PLUGIN_H_ + +// Forward declarations. +class PCB_DRAW_PANEL_GAL; +class NL_PCBNEW_PLUGIN_IMPL; + +/** + * The class that implements the public interface to the SpaceMouse plug-in. + */ +class NL_PCBNEW_PLUGIN +{ +public: + /** + * Initializes a new instance of the NL_3DVIEWER_PLUGIN. + * + * @param aViewport is the viewport to be navigated. + */ + NL_PCBNEW_PLUGIN( PCB_DRAW_PANEL_GAL* aViewport ); + + virtual ~NL_PCBNEW_PLUGIN(); + + /** + * Set the connection to the 3Dconnexion driver to the focus state so that + * 3DMouse data is routed to this connexion. + * + * @param aFocus is true to set the connexion active. + */ + void SetFocus( bool aFocus ); + +private: + NL_PCBNEW_PLUGIN_IMPL* m_impl; +}; + +#endif // NL_PCBNEW_PLUGIN_H_ diff --git a/pcbnew/navlib/nl_pcbnew_plugin_impl.cpp b/pcbnew/navlib/nl_pcbnew_plugin_impl.cpp new file mode 100644 index 0000000000..b3dafa42fe --- /dev/null +++ b/pcbnew/navlib/nl_pcbnew_plugin_impl.cpp @@ -0,0 +1,597 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2021 3Dconnexion + * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "nl_pcbnew_plugin_impl.h" + +// KiCAD includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// stdlib +#include +#include +#include +#include +#include +#include + +#include +#include + +/** + * Template to compare two floating point values for equality within a required epsilon. + * + * @param aFirst value to compare. + * @param aSecond value to compare. + * @param aEpsilon allowed error. + * @return true if the values considered equal within the specified epsilon, otherwise false. + */ +template +bool equals( T aFirst, T aSecond, T aEpsilon = static_cast( FLT_EPSILON ) ) +{ + T diff = fabs( aFirst - aSecond ); + if( diff < aEpsilon ) + { + return true; + } + aFirst = fabs( aFirst ); + aSecond = fabs( aSecond ); + T largest = aFirst > aSecond ? aFirst : aSecond; + if( diff <= largest * aEpsilon ) + { + return true; + } + return false; +} + +/** + * Template to compare two VECTOR2 values for equality within a required epsilon. + * + * @param aFirst value to compare. + * @param aSecond value to compare. + * @param aEpsilon allowed error. + * @return true if the values considered equal within the specified epsilon, otherwise false. + */ +template +bool equals( VECTOR2 const& aFirst, VECTOR2 const& aSecond, + T aEpsilon = static_cast( FLT_EPSILON ) ) +{ + if( !equals( aFirst.x, aSecond.x, aEpsilon ) ) + { + return false; + } + + return equals( aFirst.y, aSecond.y, aEpsilon ); +} + +/** + * Flag to enable the NL_PCBNEW_PLUGIN debug tracing. + * + * Use "KI_TRACE_NL_PCBNEW_PLUGIN" to enable. + * + * @ingroup trace_env_vars + */ +const wxChar* NL_PCBNEW_PLUGIN_IMPL::m_logTrace = wxT( "KI_TRACE_NL_PCBNEW_PLUGIN" ); + +NL_PCBNEW_PLUGIN_IMPL::NL_PCBNEW_PLUGIN_IMPL( PCB_DRAW_PANEL_GAL* aViewport ) : + CNavigation3D( false, false ), m_viewport2D( aViewport ), m_isMoving( false ) +{ + m_view = m_viewport2D->GetView(); + m_viewportWidth = m_view->GetBoundary().GetWidth(); + + PutProfileHint( "KiCAD PCB" ); + + // Use the default settings for the connexion to the 3DMouse navigation + // They are use a single-threaded threading model and row vectors. + EnableNavigation( true ); + + // Use the SpaceMouse internal timing source for the frame rate. + PutFrameTimingSource( TimingSource::SpaceMouse ); + + exportCommandsAndImages(); +} + +NL_PCBNEW_PLUGIN_IMPL::~NL_PCBNEW_PLUGIN_IMPL() +{ + EnableNavigation( false ); +} + +void NL_PCBNEW_PLUGIN_IMPL::SetFocus( bool aFocus ) +{ + wxLogTrace( m_logTrace, "NL_PCBNEW_PLUGIN_IMPL::SetFocus %d", aFocus ); + NAV_3D::Write( navlib::focus_k, aFocus ); +} + +// temporary store for the command categories +typedef std::map CATEGORY_STORE; + +/** + * Add a category to the store. + * + * The function adds category paths of the format "A.B" where B is a sub-category of A. + * + * @param aCategoryPath is the std::string representation of the category. + * @param aCategoryStore is the CATEGORY_STORE instance to add to. + * @return a CATEGORY_STORE::iterator where the category was added. + */ +static CATEGORY_STORE::iterator add_category( std::string aCategoryPath, + CATEGORY_STORE& aCategoryStore ) +{ + using TDx::SpaceMouse::CCategory; + + CATEGORY_STORE::iterator parent_iter = aCategoryStore.begin(); + std::string::size_type pos = aCategoryPath.find_last_of( '.' ); + + if( pos != std::string::npos ) + { + std::string parentPath = aCategoryPath.substr( 0, pos ); + parent_iter = aCategoryStore.find( parentPath ); + if( parent_iter == aCategoryStore.end() ) + { + parent_iter = add_category( parentPath, aCategoryStore ); + } + } + + std::string name = aCategoryPath.substr( pos + 1 ); + std::unique_ptr categoryNode = + std::make_unique( aCategoryPath.c_str(), name.c_str() ); + + CATEGORY_STORE::iterator iter = aCategoryStore.insert( + aCategoryStore.end(), CATEGORY_STORE::value_type( aCategoryPath, categoryNode.get() ) ); + + parent_iter->second->push_back( std::move( categoryNode ) ); + return iter; +} + +/** + * Export the invocable actions and images to the 3Dconnexion UI. + */ +void NL_PCBNEW_PLUGIN_IMPL::exportCommandsAndImages() +{ + wxLogTrace( m_logTrace, "NL_PCBNEW_PLUGIN_IMPL::exportCommandsAndImages" ); + + std::list actions = ACTION_MANAGER::GetActionList(); + + if( actions.size() == 0 ) + { + return; + } + + using TDx::SpaceMouse::CCommand; + using TDx::SpaceMouse::CCommandSet; + + // The root action set node + CCommandSet commandSet( "PCB_DRAW_PANEL_GAL", "PCB Viewer" ); + + // Activate the command set + NAV_3D::PutActiveCommands( commandSet.GetId() ); + + // temporary store for the categories + CATEGORY_STORE categoryStore; + + std::vector vImages; + + // add the action set to the category_store + CATEGORY_STORE::iterator iter = categoryStore.insert( + categoryStore.end(), CATEGORY_STORE::value_type( ".", &commandSet ) ); + + std::list::const_iterator it; + + for( it = actions.begin(); it != actions.end(); ++it ) + { + const TOOL_ACTION* action = *it; + std::string label = action->GetLabel().ToStdString(); + if( label.empty() ) + { + continue; + } + + std::string name = action->GetName(); + + // Do no export commands for the 3DViewer app. + if( name.rfind( "3DViewer.", 0 ) == 0 ) + { + continue; + } + + std::string strCategory = action->GetToolName(); + CATEGORY_STORE::iterator iter = categoryStore.find( strCategory ); + if( iter == categoryStore.end() ) + { + iter = add_category( std::move( strCategory ), categoryStore ); + } + + std::string description = action->GetDescription().ToStdString(); + + // Arbitrary 8-bit data stream + wxMemoryOutputStream imageStream; + if( action->GetIcon() != BITMAPS::INVALID_BITMAP ) + { + wxImage image = KiBitmap( action->GetIcon() ).ConvertToImage(); + image.SaveFile( imageStream, wxBitmapType::wxBITMAP_TYPE_PNG ); + image.Destroy(); + + if( imageStream.GetSize() ) + { + wxStreamBuffer* streamBuffer = imageStream.GetOutputStreamBuffer(); + TDx::CImage image = TDx::CImage::FromData( "", 0, name.c_str() ); + image.AssignImage( std::string( reinterpret_cast( + streamBuffer->GetBufferStart() ), + streamBuffer->GetBufferSize() ), + 0 ); + + wxLogTrace( m_logTrace, "Adding image for : %s", name ); + vImages.push_back( std::move( image ) ); + } + } + + wxLogTrace( m_logTrace, "Inserting command: %s, description: %s, in category: %s", name, + description, iter->first ); + + iter->second->push_back( + CCommand( std::move( name ), std::move( label ), std::move( description ) ) ); + } + + NAV_3D::AddCommandSet( commandSet ); + NAV_3D::AddImages( vImages ); +} + +long NL_PCBNEW_PLUGIN_IMPL::GetCameraMatrix( navlib::matrix_t& matrix ) const +{ + if( m_view == nullptr ) + { + return navlib::make_result_code( navlib::navlib_errc::no_data_available ); + } + + m_viewPosition = m_view->GetCenter(); + + double x = m_view->IsMirroredX() ? -1 : 1; + double y = m_view->IsMirroredY() ? 1 : -1; + + // x * y * z = 1 for a right-handed coordinate system. + double z = x * y; + + // Note: the connexion has been configured as row vectors, the coordinate system is defined in + // NL_PCBNEW_PLUGIN_IMPL::GetCoordinateSystem and the front view in NL_PCBNEW_PLUGIN_IMPL::GetFrontView. + matrix = { x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, m_viewPosition.x, m_viewPosition.y, 0, 1 }; + return 0; +} + +long NL_PCBNEW_PLUGIN_IMPL::GetPointerPosition( navlib::point_t& position ) const +{ + if( m_view == nullptr ) + { + return navlib::make_result_code( navlib::navlib_errc::no_data_available ); + } + + VECTOR2D mouse_pointer = m_viewport2D->GetViewControls()->GetMousePosition(); + + position.x = mouse_pointer.x; + position.y = mouse_pointer.y; + position.z = 0; + + return 0; +} + +long NL_PCBNEW_PLUGIN_IMPL::GetViewExtents( navlib::box_t& extents ) const +{ + if( m_view == nullptr ) + { + return navlib::make_result_code( navlib::navlib_errc::no_data_available ); + } + + double scale = m_viewport2D->GetGAL()->GetWorldScale(); + BOX2D box = m_view->GetViewport(); + + m_viewportWidth = box.GetWidth(); + + extents = navlib::box_t{ -box.GetWidth() / 2.0, + -box.GetHeight() / 2.0, + m_viewport2D->GetGAL()->GetMinDepth() / scale, + box.GetWidth() / 2.0, + box.GetHeight() / 2.0, + m_viewport2D->GetGAL()->GetMaxDepth() / scale }; + return 0; +} + +long NL_PCBNEW_PLUGIN_IMPL::GetIsViewPerspective( navlib::bool_t& perspective ) const +{ + perspective = false; + + return 0; +} + +long NL_PCBNEW_PLUGIN_IMPL::SetCameraMatrix( const navlib::matrix_t& matrix ) +{ + if( m_view == nullptr ) + { + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); + } + + long result = 0; + VECTOR2D viewPos( matrix.m4x4[3][0], matrix.m4x4[3][1] ); + + if( !equals( m_view->GetCenter(), m_viewPosition ) ) + { + m_view->SetCenter( viewPos + m_view->GetCenter() - m_viewPosition ); + result = navlib::make_result_code( navlib::navlib_errc::error ); + } + else + { + m_view->SetCenter( viewPos ); + } + + m_viewPosition = viewPos; + + return result; +} + +long NL_PCBNEW_PLUGIN_IMPL::SetViewExtents( const navlib::box_t& extents ) +{ + if( m_view == nullptr ) + { + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); + } + + long result = 0; + if( m_viewportWidth != m_view->GetViewport().GetWidth() ) + { + result = navlib::make_result_code( navlib::navlib_errc::error ); + } + + double width = m_viewportWidth; + m_viewportWidth = extents.max_x - extents.min_x; + + double scale = width / m_viewportWidth * m_view->GetScale(); + m_view->SetScale( scale, m_view->GetCenter() ); + + if( !equals( m_view->GetScale(), scale ) ) + { + result = navlib::make_result_code( navlib::navlib_errc::error ); + } + + return result; +} + +long NL_PCBNEW_PLUGIN_IMPL::SetViewFOV( double fov ) +{ + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); +} + +long NL_PCBNEW_PLUGIN_IMPL::SetViewFrustum( const navlib::frustum_t& frustum ) +{ + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); +} + +long NL_PCBNEW_PLUGIN_IMPL::GetModelExtents( navlib::box_t& extents ) const +{ + if( m_view == nullptr ) + { + return navlib::make_result_code( navlib::navlib_errc::no_data_available ); + } + + BOX2I box = static_cast( m_viewport2D->GetParent() )->GetDocumentExtents(); + box.Normalize(); + + double half_depth = 0.1 / m_viewport2D->GetGAL()->GetWorldScale(); + if( box.GetWidth() == 0 && box.GetHeight() == 0 ) + { + half_depth = 0; + } + + extents = { static_cast( box.GetOrigin().x ), + static_cast( box.GetOrigin().y ), + -half_depth, + static_cast( box.GetEnd().x ), + static_cast( box.GetEnd().y ), + half_depth }; + + return 0; +} + +long NL_PCBNEW_PLUGIN_IMPL::GetCoordinateSystem( navlib::matrix_t& matrix ) const +{ + // The coordinate system is defined as x to the right, y down and z into the screen. + matrix = { 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1 }; + return 0; +} + +long NL_PCBNEW_PLUGIN_IMPL::GetFrontView( navlib::matrix_t& matrix ) const +{ + matrix = { 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1 }; + return 0; +} + +long NL_PCBNEW_PLUGIN_IMPL::GetIsSelectionEmpty( navlib::bool_t& empty ) const +{ + empty = true; + return 0; +} + +long NL_PCBNEW_PLUGIN_IMPL::GetIsViewRotatable( navlib::bool_t& isRotatable ) const +{ + isRotatable = false; + return 0; +} + +long NL_PCBNEW_PLUGIN_IMPL::SetActiveCommand( std::string commandId ) +{ + if( commandId.empty() ) + { + return 0; + } + + std::list actions = ACTION_MANAGER::GetActionList(); + TOOL_ACTION* context = nullptr; + + for( std::list::const_iterator it = actions.begin(); it != actions.end(); it++ ) + { + TOOL_ACTION* action = *it; + std::string nm = action->GetName(); + + if( commandId == nm ) + { + context = action; + } + } + + if( context != nullptr ) + { + wxWindow* parent = m_viewport2D->GetParent(); + + // Only allow command execution if the window is enabled. i.e. there is not a modal dialog + // currently active. + if( parent->IsEnabled() ) + { + TOOL_MANAGER* tool_manager = static_cast( parent )->GetToolManager(); + + // Get the selection to use to test if the action is enabled + SELECTION& sel = tool_manager->GetToolHolder()->GetCurrentSelection(); + + bool runAction = true; + + if( const ACTION_CONDITIONS* aCond = + tool_manager->GetActionManager()->GetCondition( *context ) ) + { + runAction = aCond->enableCondition( sel ); + } + + if( runAction ) + { + tool_manager->RunAction( *context, true ); + } + } + else + { + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); + } + } + + return 0; +} + +long NL_PCBNEW_PLUGIN_IMPL::SetSettingsChanged( long change ) +{ + return 0; +} + +long NL_PCBNEW_PLUGIN_IMPL::SetMotionFlag( bool value ) +{ + m_isMoving = value; + + return 0; +} + +long NL_PCBNEW_PLUGIN_IMPL::SetTransaction( long value ) +{ + if( value == 0L ) + { + m_viewport2D->ForceRefresh(); + } + + return 0; +} + +long NL_PCBNEW_PLUGIN_IMPL::GetViewFOV( double& fov ) const +{ + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); +} + +long NL_PCBNEW_PLUGIN_IMPL::GetViewFrustum( navlib::frustum_t& frustum ) const +{ + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); +} + +long NL_PCBNEW_PLUGIN_IMPL::GetSelectionExtents( navlib::box_t& extents ) const +{ + return navlib::make_result_code( navlib::navlib_errc::no_data_available ); +} + +long NL_PCBNEW_PLUGIN_IMPL::GetSelectionTransform( navlib::matrix_t& transform ) const +{ + return navlib::make_result_code( navlib::navlib_errc::no_data_available ); +} + +long NL_PCBNEW_PLUGIN_IMPL::SetSelectionTransform( const navlib::matrix_t& matrix ) +{ + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); +} + +long NL_PCBNEW_PLUGIN_IMPL::GetPivotPosition( navlib::point_t& position ) const +{ + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); +} + +long NL_PCBNEW_PLUGIN_IMPL::IsUserPivot( navlib::bool_t& userPivot ) const +{ + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); +} + +long NL_PCBNEW_PLUGIN_IMPL::SetPivotPosition( const navlib::point_t& position ) +{ + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); +} + +long NL_PCBNEW_PLUGIN_IMPL::GetPivotVisible( navlib::bool_t& visible ) const +{ + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); +} + +long NL_PCBNEW_PLUGIN_IMPL::SetPivotVisible( bool visible ) +{ + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); +} + +long NL_PCBNEW_PLUGIN_IMPL::GetHitLookAt( navlib::point_t& position ) const +{ + return navlib::make_result_code( navlib::navlib_errc::no_data_available ); +} + +long NL_PCBNEW_PLUGIN_IMPL::SetHitAperture( double aperture ) +{ + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); +} + +long NL_PCBNEW_PLUGIN_IMPL::SetHitDirection( const navlib::vector_t& direction ) +{ + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); +} + +long NL_PCBNEW_PLUGIN_IMPL::SetHitLookFrom( const navlib::point_t& eye ) +{ + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); +} + +long NL_PCBNEW_PLUGIN_IMPL::SetHitSelectionOnly( bool onlySelection ) +{ + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); +} + +long NL_PCBNEW_PLUGIN_IMPL::SetCameraTarget( const navlib::point_t& position ) +{ + return navlib::make_result_code( navlib::navlib_errc::invalid_operation ); +} diff --git a/pcbnew/navlib/nl_pcbnew_plugin_impl.h b/pcbnew/navlib/nl_pcbnew_plugin_impl.h new file mode 100644 index 0000000000..0e47803b6e --- /dev/null +++ b/pcbnew/navlib/nl_pcbnew_plugin_impl.h @@ -0,0 +1,132 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2021 3Dconnexion + * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +/** + * @file nl_pcbnew_plugin_impl.h + * @brief Declaration of the NL_PCBNEW_PLUGIN_IMPL class + */ + +#ifndef NL_PCBNEW_PLUGIN_IMPL_H_ +#define NL_PCBNEW_PLUGIN_IMPL_H_ + +#if _WIN32 +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0603 +#endif +#endif + +// TDxWare SDK. +#include + +// wx +#include + +// KiCAD +#include + +// stdlib +#include + +// Forward declarations. +class PCB_DRAW_PANEL_GAL; +namespace KIGFX +{ +class PCB_VIEW; +} + +// Convenience typedef. +typedef TDx::SpaceMouse::Navigation3D::CNavigation3D NAV_3D; + +class NL_PCBNEW_PLUGIN_IMPL : public NAV_3D +{ +public: + /** + * Initializes a new instance of the NL_PCBNEW_PLUGIN_IMPL. + * + * @param aViewport is the viewport to be navigated. + */ + NL_PCBNEW_PLUGIN_IMPL( PCB_DRAW_PANEL_GAL* aViewport ); + + virtual ~NL_PCBNEW_PLUGIN_IMPL(); + + /** + * Set the connection to the 3Dconnexion driver to the focus state so that + * 3DMouse data is routed here. + * + * @param aFocus is true to set the connection active. + */ + void SetFocus( bool aFocus ); + +private: + void exportCommandsAndImages(); + + long GetCameraMatrix( navlib::matrix_t& aMatrix ) const override; + long GetPointerPosition( navlib::point_t& aPosition ) const override; + long GetViewExtents( navlib::box_t& aExtents ) const override; + long GetViewFOV( double& aFov ) const override; + long GetViewFrustum( navlib::frustum_t& aFrustum ) const override; + long GetIsViewPerspective( navlib::bool_t& aPerspective ) const override; + long SetCameraMatrix( const navlib::matrix_t& aMatrix ) override; + long SetViewExtents( const navlib::box_t& aExtents ) override; + long SetViewFOV( double aFov ) override; + long SetViewFrustum( const navlib::frustum_t& aFrustum ) override; + long GetModelExtents( navlib::box_t& aExtents ) const override; + long GetSelectionExtents( navlib::box_t& aExtents ) const override; + long GetSelectionTransform( navlib::matrix_t& aTransform ) const override; + long GetIsSelectionEmpty( navlib::bool_t& aEmpty ) const override; + long SetSelectionTransform( const navlib::matrix_t& aMatrix ) override; + long GetPivotPosition( navlib::point_t& aPosition ) const override; + long IsUserPivot( navlib::bool_t& aUserPivot ) const override; + long SetPivotPosition( const navlib::point_t& aPosition ) override; + long GetPivotVisible( navlib::bool_t& aVisible ) const override; + long SetPivotVisible( bool aVisible ) override; + long GetHitLookAt( navlib::point_t& aPosition ) const override; + long SetHitAperture( double aAperture ) override; + long SetHitDirection( const navlib::vector_t& aDirection ) override; + long SetHitLookFrom( const navlib::point_t& aPosition ) override; + long SetHitSelectionOnly( bool aSelectionOnly ) override; + long SetActiveCommand( std::string aCommandId ) override; + + long SetSettingsChanged( long aChangeNumber ) override; + long SetMotionFlag( bool aValue ) override; + long SetTransaction( long aValue ) override; + long SetCameraTarget( const navlib::point_t& aPosition ) override; + + long GetFrontView( navlib::matrix_t& aMatrix ) const override; + long GetCoordinateSystem( navlib::matrix_t& aMatrix ) const override; + long GetIsViewRotatable( navlib::bool_t& isRotatable ) const override; + +private: + PCB_DRAW_PANEL_GAL* m_viewport2D; + KIGFX::PCB_VIEW* m_view; + bool m_isMoving; + mutable double m_viewportWidth; + mutable VECTOR2D m_viewPosition; + + /** + * Trace mask used to enable or disable the trace output of this class. + * The debug output can be turned on by setting the WXTRACE environment variable to + * "KI_TRACE_NL_PCBNEW_PLUGIN". See the wxWidgets documentation on wxLogTrace for + * more information. + */ + static const wxChar* m_logTrace; +}; + +#endif // NL_PCBNEW_PLUGIN_IMPL_H_ diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index 2dc89b086d..a9e1b65f71 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -108,6 +108,9 @@ #include +#if defined( KICAD_USE_3DCONNEXION ) +#include +#endif using namespace std::placeholders; @@ -115,6 +118,7 @@ using namespace std::placeholders; BEGIN_EVENT_TABLE( PCB_EDIT_FRAME, PCB_BASE_FRAME ) EVT_SOCKET( ID_EDA_SOCKET_EVENT_SERV, PCB_EDIT_FRAME::OnSockRequestServer ) EVT_SOCKET( ID_EDA_SOCKET_EVENT, PCB_EDIT_FRAME::OnSockRequest ) + EVT_ACTIVATE( PCB_EDIT_FRAME::OnActivate ) EVT_CHOICE( ID_ON_ZOOM_SELECT, PCB_EDIT_FRAME::OnSelectZoom ) EVT_CHOICE( ID_ON_GRID_SELECT, PCB_EDIT_FRAME::OnSelectGrid ) @@ -178,7 +182,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : PCB_BASE_EDIT_FRAME( aKiway, aParent, FRAME_PCB_EDITOR, _( "PCB Editor" ), wxDefaultPosition, wxDefaultSize, KICAD_DEFAULT_DRAWFRAME_STYLE, PCB_EDIT_FRAME_NAME ), - m_exportNetlistAction( nullptr ), m_findDialog( nullptr ) + m_exportNetlistAction( nullptr ), m_findDialog( nullptr ), m_spaceMouse( nullptr ) { m_maximizeByDefault = true; m_showBorderAndTitleBlock = true; // true to display sheet references @@ -368,6 +372,10 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : // if( !appK2S.FileExists() ) // GetMenuBar()->FindItem( ID_GEN_EXPORT_FILE_STEP )->Enable( false ); +#if defined( KICAD_USE_3DCONNEXION ) + m_spaceMouse = new NL_PCBNEW_PLUGIN( canvas ); +#endif + // AUI doesn't refresh properly on wxMac after changes in eb7dc6dd, so force it to #ifdef __WXMAC__ if( Kiface().IsSingle() ) @@ -412,6 +420,11 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : PCB_EDIT_FRAME::~PCB_EDIT_FRAME() { +#if defined( KICAD_USE_3DCONNEXION ) + if( m_spaceMouse != nullptr ) + delete m_spaceMouse; +#endif + if( ADVANCED_CFG::GetCfg().m_ShowEventCounters ) { // Stop the timer during destruction early to avoid potential event race conditions (that do happen on windows) @@ -1240,6 +1253,17 @@ void PCB_EDIT_FRAME::OnDisplayOptionsChanged() m_appearancePanel->UpdateDisplayOptions(); } +void PCB_EDIT_FRAME::OnActivate( wxActivateEvent& aEvent ) +{ +#if defined( KICAD_USE_3DCONNEXION ) + if( m_spaceMouse != nullptr ) + { + m_spaceMouse->SetFocus( aEvent.GetActive() ); + } +#endif + + aEvent.Skip(); // required under wxMAC +} bool PCB_EDIT_FRAME::IsElementVisible( GAL_LAYER_ID aElement ) const { diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h index 0c19522082..3ed816734b 100644 --- a/pcbnew/pcb_edit_frame.h +++ b/pcbnew/pcb_edit_frame.h @@ -57,6 +57,7 @@ class FP_LIB_TABLE; class BOARD_NETLIST_UPDATER; class ACTION_MENU; class TOOL_ACTION; +class NL_PCBNEW_PLUGIN; enum LAST_PATH_TYPE : unsigned int; @@ -723,12 +724,12 @@ protected: */ void AddActionPluginTools(); - /** - * Execute action plugin's Run() method and updates undo buffer. + /** + * Execute action plugin's Run() method and updates undo buffer. * - * @param aActionPlugin action plugin - */ - void RunActionPlugin( ACTION_PLUGIN* aActionPlugin ); + * @param aActionPlugin action plugin + */ + void RunActionPlugin( ACTION_PLUGIN* aActionPlugin ); /** * Launched by the menu when an action is called. @@ -737,6 +738,13 @@ protected: */ void OnActionPluginMenu( wxCommandEvent& aEvent); + /** + * Sent when a window or application is being activated or deactivated. + * + * @param aEvent sent by wx + */ + void OnActivate( wxActivateEvent& aEvent ); + /** * Launched by the button when an action is called. * @@ -816,6 +824,8 @@ private: wxTimer m_redrawNetnamesTimer; wxTimer* m_eventCounterTimer; + + NL_PCBNEW_PLUGIN* m_spaceMouse; }; #endif // __PCB_EDIT_FRAME_H__ diff --git a/qa/pcbnew/CMakeLists.txt b/qa/pcbnew/CMakeLists.txt index c005885214..e311a229e6 100644 --- a/qa/pcbnew/CMakeLists.txt +++ b/qa/pcbnew/CMakeLists.txt @@ -116,4 +116,9 @@ if( WIN32 ) ) endif() +if( KICAD_USE_3DCONNEXION ) + message( STATUS "Including 3Dconnexion SpaceMouse navigation support in PcbNew" ) + target_link_libraries( qa_pcbnew pcbnew_navlib) +endif() + kicad_add_boost_test( qa_pcbnew qa_pcbnew ) diff --git a/qa/pcbnew_tools/CMakeLists.txt b/qa/pcbnew_tools/CMakeLists.txt index c30dad905c..09f1703edd 100644 --- a/qa/pcbnew_tools/CMakeLists.txt +++ b/qa/pcbnew_tools/CMakeLists.txt @@ -63,4 +63,9 @@ target_link_libraries( qa_pcbnew_tools ${PCBNEW_EXTRA_LIBS} # -lrt must follow Boost ) +if( KICAD_USE_3DCONNEXION ) + message( STATUS "Including 3Dconnexion SpaceMouse navigation support in PcbNew" ) + target_link_libraries( qa_pcbnew_tools pcbnew_navlib) +endif() + kicad_add_utils_executable( qa_pcbnew_tools )