* PCB_EDIT_FRAME: Implement SpaceMouse navigation and command export for 3D-viewer.

Added full support for using a 3Dconnexion device in PCB_EDIT_FRAME. Commands are exported and can be assigned to 3D mouse buttons. Any limitations to the functionality are limitations of the installed 3Dconnexion driver for the device and OS.

Use build option KICAD_USE_3DCONNEXION (default = ON) to control whether the SpaceMouse support is compiled into the solution.
This commit is contained in:
markus-bonk 2021-06-25 09:52:23 +02:00 committed by Wayne Stambaugh
parent aec7802fcf
commit 87d7bdbd5e
11 changed files with 925 additions and 7 deletions

View File

@ -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()

View File

@ -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"

View File

@ -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_PROPERTY:3DxWare::Navlib,INTERFACE_COMPILE_DEFINITIONS>
)
target_compile_options(pcbnew_navlib PRIVATE
$<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_COMPILE_OPTIONS>
)
target_include_directories(pcbnew_navlib PRIVATE
$<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:pcbnew_kiface_objects,INCLUDE_DIRECTORIES>
)
target_link_libraries(pcbnew_navlib
$<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_LINK_LIBRARIES>
3DxWare::Navlib
)
else()
add_library(pcbnew_navlib STATIC
"nl_pcbnew_plugin.cpp"
)
endif(KICAD_USE_3DCONNEXION)

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/**
* @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_

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "nl_pcbnew_plugin_impl.h"
// KiCAD includes
#include <board.h>
#include <pcb_base_frame.h>
#include <bitmaps.h>
#include <class_draw_panel_gal.h>
#include <view/view.h>
#include <view/wx_view_controls.h>
#include <tool/action_manager.h>
#include <tool/tool_action.h>
#include <tool/tool_manager.h>
// stdlib
#include <list>
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include <cfloat>
#include <wx/log.h>
#include <wx/mstream.h>
/**
* 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 <class T>
bool equals( T aFirst, T aSecond, T aEpsilon = static_cast<T>( 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<T> 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 <class T>
bool equals( VECTOR2<T> const& aFirst, VECTOR2<T> const& aSecond,
T aEpsilon = static_cast<T>( 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<std::string, TDx::CCommandTreeNode*> 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<CCategory> categoryNode =
std::make_unique<CCategory>( 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<TOOL_ACTION*> 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<TDx::CImage> vImages;
// add the action set to the category_store
CATEGORY_STORE::iterator iter = categoryStore.insert(
categoryStore.end(), CATEGORY_STORE::value_type( ".", &commandSet ) );
std::list<TOOL_ACTION*>::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<const char*>(
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<PCB_BASE_FRAME*>( 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<double>( box.GetOrigin().x ),
static_cast<double>( box.GetOrigin().y ),
-half_depth,
static_cast<double>( box.GetEnd().x ),
static_cast<double>( 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<TOOL_ACTION*> actions = ACTION_MANAGER::GetActionList();
TOOL_ACTION* context = nullptr;
for( std::list<TOOL_ACTION*>::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<PCB_BASE_FRAME*>( 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 );
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/**
* @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 <SpaceMouse/CNavigation3D.hpp>
// wx
#include <wx/chartype.h>
// KiCAD
#include <math/vector2d.h>
// stdlib
#include <string>
// 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_

View File

@ -108,6 +108,9 @@
#include <wx/filedlg.h>
#if defined( KICAD_USE_3DCONNEXION )
#include <navlib/nl_pcbnew_plugin.h>
#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
{

View File

@ -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__

View File

@ -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 )

View File

@ -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 )