7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-04-20 12:01:41 +00:00

ADDED: 3D Mouse support in Gerber Viewer and Drawing Sheet Editor

Fixes https://gitlab.com/kicad/code/kicad/-/issues/13306
This commit is contained in:
Kamil Galik 2024-09-21 08:22:19 +00:00 committed by Seth Hillbrand
parent 7187f1b705
commit 2068ed31a4
16 changed files with 1889 additions and 18 deletions

View File

@ -136,6 +136,13 @@ target_link_libraries( gerbview_kiface_objects
core
)
message( STATUS "Including 3Dconnexion SpaceMouse navigation support in gerbview" )
add_subdirectory( navlib )
target_link_libraries( gerbview_kiface_objects PUBLIC gerbview_navlib)
add_dependencies( gerbview_kiface_objects gerbview_navlib )
# the main gerbview program, in DSO form.
add_library( gerbview_kiface MODULE )

View File

@ -59,19 +59,21 @@
#include <dialog_draw_layers_settings.h>
#include <zoom_defines.h>
#include <navlib/nl_gerbview_plugin.h>
#include <wx/log.h>
GERBVIEW_FRAME::GERBVIEW_FRAME( KIWAY* aKiway, wxWindow* aParent )
: EDA_DRAW_FRAME( aKiway, aParent, FRAME_GERBER, wxT( "GerbView" ), wxDefaultPosition,
GERBVIEW_FRAME::GERBVIEW_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
EDA_DRAW_FRAME( aKiway, aParent, FRAME_GERBER, wxT( "GerbView" ), wxDefaultPosition,
wxDefaultSize, KICAD_DEFAULT_DRAWFRAME_STYLE, GERBVIEW_FRAME_NAME,
gerbIUScale ),
m_TextInfo( nullptr ),
m_zipFileHistory( DEFAULT_FILE_HISTORY_SIZE, ID_GERBVIEW_ZIP_FILE1,
ID_GERBVIEW_ZIP_FILE_LIST_CLEAR, _( "Clear Recent Zip Files" ) ),
m_drillFileHistory( DEFAULT_FILE_HISTORY_SIZE, ID_GERBVIEW_DRILL_FILE1,
ID_GERBVIEW_DRILL_FILE_LIST_CLEAR, _( "Clear Recent Drill Files" ) ),
m_jobFileHistory( DEFAULT_FILE_HISTORY_SIZE, ID_GERBVIEW_JOB_FILE1,
ID_GERBVIEW_JOB_FILE_LIST_CLEAR, _( "Clear Recent Job Files" ) ),
m_activeLayer( 0 )
m_TextInfo( nullptr ),
m_zipFileHistory( DEFAULT_FILE_HISTORY_SIZE, ID_GERBVIEW_ZIP_FILE1,
ID_GERBVIEW_ZIP_FILE_LIST_CLEAR, _( "Clear Recent Zip Files" ) ),
m_drillFileHistory( DEFAULT_FILE_HISTORY_SIZE, ID_GERBVIEW_DRILL_FILE1,
ID_GERBVIEW_DRILL_FILE_LIST_CLEAR, _( "Clear Recent Drill Files" ) ),
m_jobFileHistory( DEFAULT_FILE_HISTORY_SIZE, ID_GERBVIEW_JOB_FILE1,
ID_GERBVIEW_JOB_FILE_LIST_CLEAR, _( "Clear Recent Job Files" ) ),
m_activeLayer( 0 )
{
m_maximizeByDefault = true;
m_gerberLayout = nullptr;
@ -1070,6 +1072,18 @@ void GERBVIEW_FRAME::ActivateGalCanvas()
ReCreateOptToolbar();
ReCreateMenuBar();
try
{
if( !m_spaceMouse )
m_spaceMouse = std::make_unique<NL_GERBVIEW_PLUGIN>();
m_spaceMouse->SetCanvas( galCanvas );
}
catch( const std::system_error& e )
{
wxLogTrace( wxT( "KI_TRACE_NAVLIB" ), e.what() );
}
}
@ -1237,3 +1251,19 @@ void GERBVIEW_FRAME::ToggleLayerManager()
m_auimgr.GetPane( "LayersManager" ).Show( m_show_layer_manager_tools );
m_auimgr.Update();
}
void GERBVIEW_FRAME::handleActivateEvent( wxActivateEvent& aEvent )
{
EDA_DRAW_FRAME::handleActivateEvent(aEvent);
if( m_spaceMouse )
m_spaceMouse->SetFocus( aEvent.GetActive() );
}
void GERBVIEW_FRAME::handleIconizeEvent( wxIconizeEvent& aEvent )
{
EDA_DRAW_FRAME::handleIconizeEvent(aEvent);
if( m_spaceMouse )
m_spaceMouse->SetFocus( false );
}

View File

@ -30,6 +30,8 @@
#include <page_info.h>
#include <gbr_display_options.h>
#include <memory>
#define NO_AVAILABLE_LAYERS UNDEFINED_LAYER
class DCODE_SELECTION_BOX;
@ -43,6 +45,7 @@ class LSET;
class REPORTER;
class SELECTION;
class wxStaticText;
class NL_GERBVIEW_PLUGIN;
/**
@ -456,6 +459,9 @@ protected:
void setupUIConditions() override;
void doReCreateMenuBar() override;
void handleActivateEvent( wxActivateEvent& aEvent ) override;
void handleIconizeEvent( wxIconizeEvent& aEvent ) override;
private:
void updateComponentListSelectBox();
void updateNetnameListSelectBox();
@ -525,6 +531,8 @@ private:
// relative to the m_SelAperAttributesBox
wxStaticText* m_dcodeText; // a message on the auxiliary toolbar,
// relative to the m_DCodeSelector
std::unique_ptr<NL_GERBVIEW_PLUGIN> m_spaceMouse;
};
#endif /* WX_GERBER_STRUCT_H */

View File

@ -0,0 +1,25 @@
add_library(gerbview_navlib STATIC
"nl_gerbview_plugin.cpp"
"nl_gerbview_plugin_impl.cpp"
)
# gerbview_navlib depends on make_lexer outputs in common
add_dependencies( gerbview_navlib common )
# Find the 3DxWare SDK component 3DxWare::NlClient
# find_package(TDxWare_SDK 4.0 REQUIRED COMPONENTS 3DxWare::Navlib)
target_compile_definitions(gerbview_navlib PRIVATE
$<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_COMPILE_DEFINITIONS>
)
target_compile_options(gerbview_navlib PRIVATE
$<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_COMPILE_OPTIONS>
)
target_include_directories(gerbview_navlib PRIVATE
$<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:gerbview_kiface_objects,INCLUDE_DIRECTORIES>
)
target_link_libraries(gerbview_navlib
$<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_LINK_LIBRARIES>
3DxWare::Navlib
)

View File

@ -0,0 +1,52 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 3Dconnexion
* Copyright (C) 2022 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_gerbview_plugin.h"
#include "nl_gerbview_plugin_impl.h"
#include <advanced_config.h>
#include <kiplatform/drivers.h>
NL_GERBVIEW_PLUGIN::NL_GERBVIEW_PLUGIN()
{
if( ADVANCED_CFG::GetCfg().m_Use3DConnexionDriver
&& KIPLATFORM::DRIVERS::Valid3DConnexionDriverVersion() )
{
m_impl = std::make_unique<NL_GERBVIEW_PLUGIN_IMPL>();
}
}
NL_GERBVIEW_PLUGIN::~NL_GERBVIEW_PLUGIN() = default;
void NL_GERBVIEW_PLUGIN::SetFocus( bool focus )
{
if( m_impl )
m_impl->SetFocus( focus );
}
void NL_GERBVIEW_PLUGIN::SetCanvas( EDA_DRAW_PANEL_GAL* aViewport )
{
if( m_impl )
m_impl->SetCanvas( aViewport );
}

View File

@ -0,0 +1,69 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 3Dconnexion
* Copyright (C) 2022 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_gerbview_plugin.h
* @brief Declaration of the NL_GERBVIEW_PLUGIN class
*/
#ifndef NL_GERBVIEW_PLUGIN_H_
#define NL_GERBVIEW_PLUGIN_H_
#include <memory>
// Forward declarations.
class EDA_DRAW_PANEL_GAL;
class NL_GERBVIEW_PLUGIN_IMPL;
/**
* The class that implements the public interface to the SpaceMouse plug-in.
*/
class NL_GERBVIEW_PLUGIN
{
public:
/**
* Initializes a new instance of the NL_GERBVIEW_PLUGIN.
*/
NL_GERBVIEW_PLUGIN();
virtual ~NL_GERBVIEW_PLUGIN();
/**
* Sets the viewport controlled by the SpaceMouse.
*
* @param aViewport is the viewport to be navigated.
*/
void SetCanvas( EDA_DRAW_PANEL_GAL* aViewport );
/**
* 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:
std::unique_ptr<NL_GERBVIEW_PLUGIN_IMPL> m_impl;
};
#endif // NL_GERBVIEW_PLUGIN_H_

View File

@ -0,0 +1,598 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 3Dconnexion
* Copyright (C) 2022 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_gerbview_plugin_impl.h"
// KiCAD includes
#include <gal/graphics_abstraction_layer.h>
#include <gerbview_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>
/**
* Flag to enable the NL_GERBVIEW_PLUGIN debug tracing.
*
* Use "KI_TRACE_NL_GERBVIEW_PLUGIN" to enable.
*
* @ingroup trace_env_vars
*/
const wxChar* NL_GERBVIEW_PLUGIN_IMPL::m_logTrace = wxT( "KI_TRACE_NL_GERBVIEW_PLUGIN" );
NL_GERBVIEW_PLUGIN_IMPL::NL_GERBVIEW_PLUGIN_IMPL() : CNavigation3D( false, false )
{
PutProfileHint( "KiCAD Gerbview" );
}
NL_GERBVIEW_PLUGIN_IMPL::~NL_GERBVIEW_PLUGIN_IMPL()
{
std::error_code m_errCode;
EnableNavigation( false, m_errCode );
if( m_errCode.value() != 0 )
{
wxLogTrace( wxT( "KI_TRACE_NAVLIB" ),
wxT( "Error occured when calling EnableNavigation. Error code: %d" ),
m_errCode.value() );
}
}
void NL_GERBVIEW_PLUGIN_IMPL::SetCanvas( EDA_DRAW_PANEL_GAL* aViewport )
{
m_viewport2D = aViewport;
if( m_viewport2D == nullptr )
{
return;
}
m_view = m_viewport2D->GetView();
if( m_view == nullptr )
{
return;
}
m_viewportWidth = m_view->GetBoundary().GetWidth();
if( !IsEnabled() )
{
// 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();
}
}
void NL_GERBVIEW_PLUGIN_IMPL::SetFocus( bool aFocus )
{
wxLogTrace( m_logTrace, wxT( "NL_GERBVIEW_PLUGIN_IMPL::SetFocus %d" ), aFocus );
NAV_3D::Write( navlib::focus_k, aFocus );
}
// temporary store for the command categories
using CATEGORY_STORE = std::map<std::string, TDx::CCommandTreeNode*, std::less<>>;
/**
* 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.
*/
static void add_category( const std::string& aCategoryPath, CATEGORY_STORE& aCategoryStore )
{
using TDx::SpaceMouse::CCategory;
auto 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 );
if( !aCategoryStore.contains( parentPath ) )
{
add_category( parentPath, aCategoryStore );
parent_iter = aCategoryStore.find( parentPath );
}
}
std::string name = aCategoryPath.substr( pos + 1 );
auto categoryNode = std::make_unique<CCategory>( aCategoryPath.c_str(), name.c_str() );
aCategoryStore.try_emplace( aCategoryStore.end(), aCategoryPath, categoryNode.get() );
parent_iter->second->push_back( std::move( categoryNode ) );
}
/**
* add_category wrapper.
*
* Function checks if path exists in the category and adds it if it doesn't.
*
* @param aCategoryPath is the std::string representation of the category.
* @param aCategoryStore is the CATEGORY_STORE instance to add to.
*/
static void try_add_category( const std::string& aCategoryPath, CATEGORY_STORE& aCategoryStore )
{
if( !aCategoryStore.contains( aCategoryPath ) )
{
add_category( aCategoryPath, aCategoryStore );
}
}
void NL_GERBVIEW_PLUGIN_IMPL::exportCommandsAndImages()
{
wxLogTrace( m_logTrace, wxT( "NL_GERBVIEW_PLUGIN_IMPL::exportCommandsAndImages" ) );
std::list<TOOL_ACTION*> actions = ACTION_MANAGER::GetActionList();
if( actions.empty() )
return;
using TDx::SpaceMouse::CCommand;
using TDx::SpaceMouse::CCommandSet;
// The root action set node
CCommandSet commandSet( "GERBER_EDITOR", "Gerber Viewer" );
// Activate the command set
NAV_3D::PutActiveCommands( commandSet.GetId() );
// temporary store for the categories initialized with action set
CATEGORY_STORE categoryStore{ CATEGORY_STORE::value_type( ".", &commandSet ) };
std::vector<TDx::CImage> vImages;
for( const auto action : actions )
{
std::string label = action->GetMenuLabel().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();
std::string description = action->GetDescription().ToStdString();
try_add_category( strCategory, categoryStore );
CATEGORY_STORE::iterator iter = categoryStore.find( strCategory );
// 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() )
{
const wxStreamBuffer* streamBuffer = imageStream.GetOutputStreamBuffer();
TDx::CImage tdxImage = TDx::CImage::FromData( "", 0, name.c_str() );
tdxImage.AssignImage(
std::string( std::bit_cast<const char*>( streamBuffer->GetBufferStart() ),
streamBuffer->GetBufferSize() ),
0 );
wxLogTrace( m_logTrace, wxT( "Adding image for : %s" ), name );
vImages.push_back( std::move( tdxImage ) );
}
}
wxLogTrace( m_logTrace, wxT( "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_GERBVIEW_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_GERBVIEW_PLUGIN_IMPL::GetCoordinateSystem and the front view in NL_GERBVIEW_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_GERBVIEW_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_GERBVIEW_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.min_x = -box.GetWidth() / 2.0;
extents.min_y = -box.GetHeight() / 2.0;
extents.min_z = m_viewport2D->GetGAL()->GetMinDepth() / scale;
extents.max_x = box.GetWidth() / 2.0;
extents.max_y = box.GetHeight() / 2.0;
extents.max_z = m_viewport2D->GetGAL()->GetMaxDepth() / scale;
return 0;
}
long NL_GERBVIEW_PLUGIN_IMPL::GetIsViewPerspective( navlib::bool_t& perspective ) const
{
perspective = false;
return 0;
}
long NL_GERBVIEW_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,
static_cast<VECTOR2D::coord_type>( FLT_EPSILON ) ) )
{
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_GERBVIEW_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, static_cast<double>( FLT_EPSILON ) ) )
result = navlib::make_result_code( navlib::navlib_errc::error );
return result;
}
long NL_GERBVIEW_PLUGIN_IMPL::SetViewFOV( double fov )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_GERBVIEW_PLUGIN_IMPL::SetViewFrustum( const navlib::frustum_t& frustum )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_GERBVIEW_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<GERBVIEW_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.min_x = static_cast<double>( box.GetOrigin().x );
extents.min_y = static_cast<double>( box.GetOrigin().y );
extents.min_z = -half_depth;
extents.max_x = static_cast<double>( box.GetEnd().x );
extents.max_y = static_cast<double>( box.GetEnd().y );
extents.max_z = half_depth;
return 0;
}
long NL_GERBVIEW_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_GERBVIEW_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_GERBVIEW_PLUGIN_IMPL::GetIsSelectionEmpty( navlib::bool_t& empty ) const
{
empty = true;
return 0;
}
long NL_GERBVIEW_PLUGIN_IMPL::GetIsViewRotatable( navlib::bool_t& isRotatable ) const
{
isRotatable = false;
return 0;
}
long NL_GERBVIEW_PLUGIN_IMPL::SetActiveCommand( std::string commandId )
{
if( commandId.empty() )
return 0;
if(m_viewport2D == nullptr)
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
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 == nullptr || !parent->IsEnabled() )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
TOOL_MANAGER* tool_manager = dynamic_cast<TOOLS_HOLDER*>( parent )->GetToolManager();
// Only allow for command execution if the tool manager is accessible.
if( tool_manager == nullptr )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
for( std::list<TOOL_ACTION*> actions = ACTION_MANAGER::GetActionList();
const auto action : actions )
{
if( action == nullptr )
{
continue;
}
if( commandId == action->GetName() )
{
// Get the selection to use to test if the action is enabled
const SELECTION& sel = tool_manager->GetToolHolder()->GetCurrentSelection();
const ACTION_CONDITIONS* aCond =
tool_manager->GetActionManager()->GetCondition( *action );
if( aCond == nullptr )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
aCond->enableCondition( sel );
tool_manager->RunAction( *action );
break;
}
}
return 0;
}
long NL_GERBVIEW_PLUGIN_IMPL::SetSettingsChanged( long change )
{
return 0;
}
long NL_GERBVIEW_PLUGIN_IMPL::SetMotionFlag( bool value )
{
m_isMoving = value;
return 0;
}
long NL_GERBVIEW_PLUGIN_IMPL::SetTransaction( long value )
{
if( value == 0L )
m_viewport2D->ForceRefresh();
return 0;
}
long NL_GERBVIEW_PLUGIN_IMPL::GetViewFOV( double& fov ) const
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_GERBVIEW_PLUGIN_IMPL::GetViewFrustum( navlib::frustum_t& frustum ) const
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_GERBVIEW_PLUGIN_IMPL::GetSelectionExtents( navlib::box_t& extents ) const
{
return navlib::make_result_code( navlib::navlib_errc::no_data_available );
}
long NL_GERBVIEW_PLUGIN_IMPL::GetSelectionTransform( navlib::matrix_t& transform ) const
{
return navlib::make_result_code( navlib::navlib_errc::no_data_available );
}
long NL_GERBVIEW_PLUGIN_IMPL::SetSelectionTransform( const navlib::matrix_t& matrix )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_GERBVIEW_PLUGIN_IMPL::GetPivotPosition( navlib::point_t& position ) const
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_GERBVIEW_PLUGIN_IMPL::IsUserPivot( navlib::bool_t& userPivot ) const
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_GERBVIEW_PLUGIN_IMPL::SetPivotPosition( const navlib::point_t& position )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_GERBVIEW_PLUGIN_IMPL::GetPivotVisible( navlib::bool_t& visible ) const
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_GERBVIEW_PLUGIN_IMPL::SetPivotVisible( bool visible )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_GERBVIEW_PLUGIN_IMPL::GetHitLookAt( navlib::point_t& position ) const
{
return navlib::make_result_code( navlib::navlib_errc::no_data_available );
}
long NL_GERBVIEW_PLUGIN_IMPL::SetHitAperture( double aperture )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_GERBVIEW_PLUGIN_IMPL::SetHitDirection( const navlib::vector_t& direction )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_GERBVIEW_PLUGIN_IMPL::SetHitLookFrom( const navlib::point_t& eye )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_GERBVIEW_PLUGIN_IMPL::SetHitSelectionOnly( bool onlySelection )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_GERBVIEW_PLUGIN_IMPL::SetCameraTarget( const navlib::point_t& position )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}

View File

@ -0,0 +1,147 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 3Dconnexion
* Copyright (C) 2022 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_gerbview_plugin_impl.h
* @brief Declaration of the NL_GERBVIEW_PLUGIN_IMPL class
*/
#ifndef NL_GERBVIEW_PLUGIN_IMPL_H_
#define NL_GERBVIEW_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 EDA_DRAW_PANEL_GAL;
namespace KIGFX
{
class VIEW;
}
// Convenience typedef.
typedef TDx::SpaceMouse::Navigation3D::CNavigation3D NAV_3D;
/**
* The class that implements the accessors and mutators required for
* 3D navigation in an PCB_DRAW_PANEL_GAL using a SpaceMouse.
*/
class NL_GERBVIEW_PLUGIN_IMPL : public NAV_3D
{
public:
/**
* Initializes a new instance of the NL_GERBVIEW_PLUGIN_IMPL.
*/
NL_GERBVIEW_PLUGIN_IMPL();
virtual ~NL_GERBVIEW_PLUGIN_IMPL();
/**
* Sets the viewport controlled by the SpaceMouse.
*
* @param aViewport is the viewport to be navigated.
*/
void SetCanvas( EDA_DRAW_PANEL_GAL* aViewport );
/**
* 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:
/**
* Export the invocable actions and images to the 3Dconnexion UI.
*/
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:
EDA_DRAW_PANEL_GAL* m_viewport2D = nullptr;
KIGFX::VIEW* m_view = nullptr;
bool m_isMoving = false;
mutable double m_viewportWidth = 0.0;
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_GERBVIEW_PLUGIN". See the wxWidgets documentation on wxLogTrace for
* more information.
*/
static const wxChar* m_logTrace;
};
#endif // NL_GERBVIEW_PLUGIN_IMPL

View File

@ -120,6 +120,13 @@ target_link_options( pl_editor_kiface PRIVATE
# if building pl_editor, then also build pl_editor_kiface if out of date.
add_dependencies( pl_editor pl_editor_kiface )
message( STATUS "Including 3Dconnexion SpaceMouse navigation support in pagelayout editor" )
add_subdirectory( navlib )
target_link_libraries( pl_editor_kiface pl_editor_navlib)
add_dependencies( pl_editor_kiface pl_editor_navlib)
# these 2 binaries are a matched set, keep them together:
if( APPLE )
set_target_properties( pl_editor PROPERTIES

View File

@ -0,0 +1,25 @@
add_library(pl_editor_navlib STATIC
"nl_pl_editor_plugin.cpp"
"nl_pl_editor_plugin_impl.cpp"
)
# pl_editor_navlib depends on make_lexer outputs in common
add_dependencies( pl_editor_navlib common )
# Find the 3DxWare SDK component 3DxWare::NlClient
# find_package(TDxWare_SDK 4.0 REQUIRED COMPONENTS 3DxWare::Navlib)
target_compile_definitions(pl_editor_navlib PRIVATE
$<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_COMPILE_DEFINITIONS>
)
target_compile_options(pl_editor_navlib PRIVATE
$<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_COMPILE_OPTIONS>
)
target_include_directories(pl_editor_navlib PRIVATE
$<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:pl_editor_kiface,INCLUDE_DIRECTORIES>
)
target_link_libraries(pl_editor_navlib
$<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_LINK_LIBRARIES>
3DxWare::Navlib
)

View File

@ -0,0 +1,52 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 3Dconnexion
* Copyright (C) 2022 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_pl_editor_plugin.h"
#include "nl_pl_editor_plugin_impl.h"
#include <advanced_config.h>
#include <kiplatform/drivers.h>
NL_PL_EDITOR_PLUGIN::NL_PL_EDITOR_PLUGIN()
{
if( ADVANCED_CFG::GetCfg().m_Use3DConnexionDriver
&& KIPLATFORM::DRIVERS::Valid3DConnexionDriverVersion() )
{
m_impl = std::make_unique<NL_PL_EDITOR_PLUGIN_IMPL>();
}
}
NL_PL_EDITOR_PLUGIN::~NL_PL_EDITOR_PLUGIN() = default;
void NL_PL_EDITOR_PLUGIN::SetFocus( bool focus )
{
if( m_impl )
m_impl->SetFocus( focus );
}
void NL_PL_EDITOR_PLUGIN::SetCanvas( EDA_DRAW_PANEL_GAL* aViewport )
{
if( m_impl )
m_impl->SetCanvas( aViewport );
}

View File

@ -0,0 +1,69 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 3Dconnexion
* Copyright (C) 2022 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_gerbview_plugin.h
* @brief Declaration of the NL_PL_EDITOR_PLUGIN class
*/
#ifndef NL_GERBVIEW_PLUGIN_H_
#define NL_GERBVIEW_PLUGIN_H_
#include <memory>
// Forward declarations.
class EDA_DRAW_PANEL_GAL;
class NL_PL_EDITOR_PLUGIN_IMPL;
/**
* The class that implements the public interface to the SpaceMouse plug-in.
*/
class NL_PL_EDITOR_PLUGIN
{
public:
/**
* Initializes a new instance of the NL_PL_EDITOR_PLUGIN.
*/
NL_PL_EDITOR_PLUGIN();
virtual ~NL_PL_EDITOR_PLUGIN();
/**
* Sets the viewport controlled by the SpaceMouse.
*
* @param aViewport is the viewport to be navigated.
*/
void SetCanvas( EDA_DRAW_PANEL_GAL* aViewport );
/**
* 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:
std::unique_ptr<NL_PL_EDITOR_PLUGIN_IMPL> m_impl;
};
#endif // NL_GERBVIEW_PLUGIN_H_

View File

@ -0,0 +1,598 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 3Dconnexion
* Copyright (C) 2022 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_pl_editor_plugin_impl.h"
// KiCAD includes
#include <gal/graphics_abstraction_layer.h>
#include <pl_editor_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>
/**
* Flag to enable the NL_PL_EDITOR_PLUGIN debug tracing.
*
* Use "KI_TRACE_NL_PL_EDITOR_PLUGIN" to enable.
*
* @ingroup trace_env_vars
*/
const wxChar* NL_PL_EDITOR_PLUGIN_IMPL::m_logTrace = wxT( "KI_TRACE_NL_PL_EDITOR_PLUGIN" );
NL_PL_EDITOR_PLUGIN_IMPL::NL_PL_EDITOR_PLUGIN_IMPL() : CNavigation3D( false, false )
{
PutProfileHint( "KiCAD Datasheet Editor" );
}
NL_PL_EDITOR_PLUGIN_IMPL::~NL_PL_EDITOR_PLUGIN_IMPL()
{
std::error_code m_errCode;
EnableNavigation( false, m_errCode );
if( m_errCode.value() != 0 )
{
wxLogTrace( wxT( "KI_TRACE_NAVLIB" ),
wxT( "Error occured when calling EnableNavigation. Error code: %d" ),
m_errCode.value() );
}
}
void NL_PL_EDITOR_PLUGIN_IMPL::SetCanvas( EDA_DRAW_PANEL_GAL* aViewport )
{
m_viewport2D = aViewport;
if( m_viewport2D == nullptr )
{
return;
}
m_view = m_viewport2D->GetView();
if( m_view == nullptr )
{
return;
}
m_viewportWidth = m_view->GetBoundary().GetWidth();
if( !IsEnabled() )
{
// 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();
}
}
void NL_PL_EDITOR_PLUGIN_IMPL::SetFocus( bool aFocus )
{
wxLogTrace( m_logTrace, wxT( "NL_PL_EDITOR_PLUGIN_IMPL::SetFocus %d" ), aFocus );
NAV_3D::Write( navlib::focus_k, aFocus );
}
// temporary store for the command categories
using CATEGORY_STORE = std::map<std::string, TDx::CCommandTreeNode*, std::less<>>;
/**
* 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.
*/
static void add_category( const std::string& aCategoryPath, CATEGORY_STORE& aCategoryStore )
{
using TDx::SpaceMouse::CCategory;
auto 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 );
if( !aCategoryStore.contains( parentPath ) )
{
add_category( parentPath, aCategoryStore );
parent_iter = aCategoryStore.find( parentPath );
}
}
std::string name = aCategoryPath.substr( pos + 1 );
auto categoryNode = std::make_unique<CCategory>( aCategoryPath.c_str(), name.c_str() );
aCategoryStore.try_emplace( aCategoryStore.end(), aCategoryPath, categoryNode.get() );
parent_iter->second->push_back( std::move( categoryNode ) );
}
/**
* add_category wrapper.
*
* Function checks if path exists in the category and adds it if it doesn't.
*
* @param aCategoryPath is the std::string representation of the category.
* @param aCategoryStore is the CATEGORY_STORE instance to add to.
*/
static void try_add_category( const std::string& aCategoryPath, CATEGORY_STORE& aCategoryStore )
{
if( !aCategoryStore.contains( aCategoryPath ) )
{
add_category( aCategoryPath, aCategoryStore );
}
}
void NL_PL_EDITOR_PLUGIN_IMPL::exportCommandsAndImages()
{
wxLogTrace( m_logTrace, wxT( "NL_PL_EDITOR_PLUGIN_IMPL::exportCommandsAndImages" ) );
std::list<TOOL_ACTION*> actions = ACTION_MANAGER::GetActionList();
if( actions.empty() )
return;
using TDx::SpaceMouse::CCommand;
using TDx::SpaceMouse::CCommandSet;
// The root action set node
CCommandSet commandSet( "PL_EDITOR", "Drawing Sheet Editor" );
// Activate the command set
NAV_3D::PutActiveCommands( commandSet.GetId() );
// temporary store for the categories initialized with action set
CATEGORY_STORE categoryStore{ CATEGORY_STORE::value_type( ".", &commandSet ) };
std::vector<TDx::CImage> vImages;
for( const auto action : actions )
{
std::string label = action->GetMenuLabel().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();
std::string description = action->GetDescription().ToStdString();
try_add_category( strCategory, categoryStore );
CATEGORY_STORE::iterator iter = categoryStore.find( strCategory );
// 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() )
{
const wxStreamBuffer* streamBuffer = imageStream.GetOutputStreamBuffer();
TDx::CImage tdxImage = TDx::CImage::FromData( "", 0, name.c_str() );
tdxImage.AssignImage(
std::string( std::bit_cast<const char*>( streamBuffer->GetBufferStart() ),
streamBuffer->GetBufferSize() ),
0 );
wxLogTrace( m_logTrace, wxT( "Adding image for : %s" ), name );
vImages.push_back( std::move( tdxImage ) );
}
}
wxLogTrace( m_logTrace, wxT( "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_PL_EDITOR_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_PL_EDITOR_PLUGIN_IMPL::GetCoordinateSystem and the front view in NL_PL_EDITOR_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_PL_EDITOR_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_PL_EDITOR_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.min_x = -box.GetWidth() / 2.0;
extents.min_y = -box.GetHeight() / 2.0;
extents.min_z = m_viewport2D->GetGAL()->GetMinDepth() / scale;
extents.max_x = box.GetWidth() / 2.0;
extents.max_y = box.GetHeight() / 2.0;
extents.max_z = m_viewport2D->GetGAL()->GetMaxDepth() / scale;
return 0;
}
long NL_PL_EDITOR_PLUGIN_IMPL::GetIsViewPerspective( navlib::bool_t& perspective ) const
{
perspective = false;
return 0;
}
long NL_PL_EDITOR_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,
static_cast<VECTOR2D::coord_type>( FLT_EPSILON ) ) )
{
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_PL_EDITOR_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, static_cast<double>( FLT_EPSILON ) ) )
result = navlib::make_result_code( navlib::navlib_errc::error );
return result;
}
long NL_PL_EDITOR_PLUGIN_IMPL::SetViewFOV( double fov )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_PL_EDITOR_PLUGIN_IMPL::SetViewFrustum( const navlib::frustum_t& frustum )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_PL_EDITOR_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<PL_EDITOR_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.min_x = static_cast<double>( box.GetOrigin().x );
extents.min_y = static_cast<double>( box.GetOrigin().y );
extents.min_z = -half_depth;
extents.max_x = static_cast<double>( box.GetEnd().x );
extents.max_y = static_cast<double>( box.GetEnd().y );
extents.max_z = half_depth;
return 0;
}
long NL_PL_EDITOR_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_PL_EDITOR_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_PL_EDITOR_PLUGIN_IMPL::GetIsSelectionEmpty( navlib::bool_t& empty ) const
{
empty = true;
return 0;
}
long NL_PL_EDITOR_PLUGIN_IMPL::GetIsViewRotatable( navlib::bool_t& isRotatable ) const
{
isRotatable = false;
return 0;
}
long NL_PL_EDITOR_PLUGIN_IMPL::SetActiveCommand( std::string commandId )
{
if( commandId.empty() )
return 0;
if(m_viewport2D == nullptr)
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
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 == nullptr || !parent->IsEnabled() )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
TOOL_MANAGER* tool_manager = dynamic_cast<TOOLS_HOLDER*>( parent )->GetToolManager();
// Only allow for command execution if the tool manager is accessible.
if( tool_manager == nullptr )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
for( std::list<TOOL_ACTION*> actions = ACTION_MANAGER::GetActionList();
const auto action : actions )
{
if( action == nullptr )
{
continue;
}
if( commandId == action->GetName() )
{
// Get the selection to use to test if the action is enabled
const SELECTION& sel = tool_manager->GetToolHolder()->GetCurrentSelection();
const ACTION_CONDITIONS* aCond =
tool_manager->GetActionManager()->GetCondition( *action );
if( aCond == nullptr )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
aCond->enableCondition( sel );
tool_manager->RunAction( *action );
break;
}
}
return 0;
}
long NL_PL_EDITOR_PLUGIN_IMPL::SetSettingsChanged( long change )
{
return 0;
}
long NL_PL_EDITOR_PLUGIN_IMPL::SetMotionFlag( bool value )
{
m_isMoving = value;
return 0;
}
long NL_PL_EDITOR_PLUGIN_IMPL::SetTransaction( long value )
{
if( value == 0L )
m_viewport2D->ForceRefresh();
return 0;
}
long NL_PL_EDITOR_PLUGIN_IMPL::GetViewFOV( double& fov ) const
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_PL_EDITOR_PLUGIN_IMPL::GetViewFrustum( navlib::frustum_t& frustum ) const
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_PL_EDITOR_PLUGIN_IMPL::GetSelectionExtents( navlib::box_t& extents ) const
{
return navlib::make_result_code( navlib::navlib_errc::no_data_available );
}
long NL_PL_EDITOR_PLUGIN_IMPL::GetSelectionTransform( navlib::matrix_t& transform ) const
{
return navlib::make_result_code( navlib::navlib_errc::no_data_available );
}
long NL_PL_EDITOR_PLUGIN_IMPL::SetSelectionTransform( const navlib::matrix_t& matrix )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_PL_EDITOR_PLUGIN_IMPL::GetPivotPosition( navlib::point_t& position ) const
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_PL_EDITOR_PLUGIN_IMPL::IsUserPivot( navlib::bool_t& userPivot ) const
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_PL_EDITOR_PLUGIN_IMPL::SetPivotPosition( const navlib::point_t& position )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_PL_EDITOR_PLUGIN_IMPL::GetPivotVisible( navlib::bool_t& visible ) const
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_PL_EDITOR_PLUGIN_IMPL::SetPivotVisible( bool visible )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_PL_EDITOR_PLUGIN_IMPL::GetHitLookAt( navlib::point_t& position ) const
{
return navlib::make_result_code( navlib::navlib_errc::no_data_available );
}
long NL_PL_EDITOR_PLUGIN_IMPL::SetHitAperture( double aperture )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_PL_EDITOR_PLUGIN_IMPL::SetHitDirection( const navlib::vector_t& direction )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_PL_EDITOR_PLUGIN_IMPL::SetHitLookFrom( const navlib::point_t& eye )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_PL_EDITOR_PLUGIN_IMPL::SetHitSelectionOnly( bool onlySelection )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}
long NL_PL_EDITOR_PLUGIN_IMPL::SetCameraTarget( const navlib::point_t& position )
{
return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
}

View File

@ -0,0 +1,147 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 3Dconnexion
* Copyright (C) 2022 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_gerbview_plugin_impl.h
* @brief Declaration of the NL_PL_EDITOR_PLUGIN_IMPL class
*/
#ifndef NL_GERBVIEW_PLUGIN_IMPL_H_
#define NL_GERBVIEW_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 EDA_DRAW_PANEL_GAL;
namespace KIGFX
{
class VIEW;
}
// Convenience typedef.
typedef TDx::SpaceMouse::Navigation3D::CNavigation3D NAV_3D;
/**
* The class that implements the accessors and mutators required for
* 3D navigation in an PCB_DRAW_PANEL_GAL using a SpaceMouse.
*/
class NL_PL_EDITOR_PLUGIN_IMPL : public NAV_3D
{
public:
/**
* Initializes a new instance of the NL_PL_EDITOR_PLUGIN_IMPL.
*/
NL_PL_EDITOR_PLUGIN_IMPL();
virtual ~NL_PL_EDITOR_PLUGIN_IMPL();
/**
* Sets the viewport controlled by the SpaceMouse.
*
* @param aViewport is the viewport to be navigated.
*/
void SetCanvas( EDA_DRAW_PANEL_GAL* aViewport );
/**
* 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:
/**
* Export the invocable actions and images to the 3Dconnexion UI.
*/
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:
EDA_DRAW_PANEL_GAL* m_viewport2D = nullptr;
KIGFX::VIEW* m_view = nullptr;
bool m_isMoving = false;
mutable double m_viewportWidth = 0.0;
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_GERBVIEW_PLUGIN". See the wxWidgets documentation on wxLogTrace for
* more information.
*/
static const wxChar* m_logTrace;
};
#endif // NL_PL_EDITOR_PLUGIN_IMPL

View File

@ -63,6 +63,9 @@
#include <wx/print.h>
#include <wx/treebook.h>
#include <wx/msgdlg.h>
#include <wx/log.h>
#include <navlib/nl_pl_editor_plugin.h>
BEGIN_EVENT_TABLE( PL_EDITOR_FRAME, EDA_DRAW_FRAME )
@ -83,14 +86,11 @@ END_EVENT_TABLE()
PL_EDITOR_FRAME::PL_EDITOR_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
EDA_DRAW_FRAME( aKiway, aParent, FRAME_PL_EDITOR, wxT( "PlEditorFrame" ),
wxDefaultPosition, wxDefaultSize,
KICAD_DEFAULT_DRAWFRAME_STYLE, PL_EDITOR_FRAME_NAME, drawSheetIUScale ),
m_propertiesPagelayout( nullptr ),
m_propertiesFrameWidth( 200 ),
m_originSelectBox( nullptr ),
m_originSelectChoice( 0 ),
m_pageSelectBox( nullptr ),
EDA_DRAW_FRAME( aKiway, aParent, FRAME_PL_EDITOR, wxT( "PlEditorFrame" ), wxDefaultPosition,
wxDefaultSize, KICAD_DEFAULT_DRAWFRAME_STYLE, PL_EDITOR_FRAME_NAME,
drawSheetIUScale ),
m_propertiesPagelayout( nullptr ), m_propertiesFrameWidth( 200 ),
m_originSelectBox( nullptr ), m_originSelectChoice( 0 ), m_pageSelectBox( nullptr ),
m_mruImagePath( wxEmptyString )
{
m_maximizeByDefault = true;
@ -230,6 +230,17 @@ PL_EDITOR_FRAME::PL_EDITOR_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
// Ensure the controls on the toolbars all are correctly sized
UpdateToolbarControlSizes();
} );
try
{
if( !m_spaceMouse )
m_spaceMouse = std::make_unique<NL_PL_EDITOR_PLUGIN>();
m_spaceMouse->SetCanvas( GetCanvas() );
}
catch( const std::system_error& e )
{
wxLogTrace( wxT( "KI_TRACE_NAVLIB" ), e.what() );
}
}
@ -979,3 +990,20 @@ void PL_EDITOR_FRAME::UpdateMsgPanelInfo()
SetMsgPanel( msgItems );
}
#endif
void PL_EDITOR_FRAME::handleActivateEvent( wxActivateEvent& aEvent )
{
EDA_DRAW_FRAME::handleActivateEvent(aEvent);
if( m_spaceMouse )
m_spaceMouse->SetFocus( aEvent.GetActive() );
}
void PL_EDITOR_FRAME::handleIconizeEvent( wxIconizeEvent& aEvent )
{
EDA_DRAW_FRAME::handleIconizeEvent(aEvent);
if( m_spaceMouse )
m_spaceMouse->SetFocus( false );
}

View File

@ -32,10 +32,13 @@
#include "pl_editor_layout.h"
#include "pl_draw_panel_gal.h"
#include <memory>
class PL_DRAW_PANEL_GAL;
class PROPERTIES_FRAME;
class DS_DATA_ITEM;
class wxChoice;
class NL_PL_EDITOR_PLUGIN;
/**
@ -265,6 +268,10 @@ protected:
DECLARE_EVENT_TABLE();
private:
void handleActivateEvent( wxActivateEvent& aEvent ) override;
void handleIconizeEvent( wxIconizeEvent& aEvent ) override;
protected:
/// The last filename chosen to be proposed to the user
PROPERTIES_FRAME* m_propertiesPagelayout;
@ -281,6 +288,8 @@ private:
wxString m_mruImagePath; // Most recently used path for placing a new image
// only on page 1, not on page 1
VECTOR2I m_grid_origin;
std::unique_ptr<NL_PL_EDITOR_PLUGIN> m_spaceMouse;
};
#endif /* _PL_EDITOR_FRAME_H */