KiCad PCB EDA Suite
Loading...
Searching...
No Matches
nl_pl_editor_plugin_impl.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2024 3Dconnexion
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
22
23// KiCAD includes
25#include <pl_editor_frame.h>
26#include <bitmaps.h>
28#include <navlib_safe_init.h>
29#include <view/view.h>
31#include <tool/action_manager.h>
32#include <tool/tool_action.h>
33#include <tool/tool_manager.h>
34
35// stdlib
36#include <list>
37#include <map>
38#include <memory>
39#include <utility>
40#include <vector>
41#include <cfloat>
42
43#include <wx/log.h>
44#include <wx/mstream.h>
45
46
54const wxChar* NL_PL_EDITOR_PLUGIN_IMPL::m_logTrace = wxT( "KI_TRACE_NL_PL_EDITOR_PLUGIN" );
55
56
58{
59 PutProfileHint( "KiCAD Datasheet Editor" );
60}
61
62
64{
65 if( IsEnabled() )
66 {
67 std::error_code errCode;
68 EnableNavigation( false, errCode );
69
70 if( errCode.value() != 0 )
71 {
72 wxLogTrace( wxT( "KI_TRACE_NAVLIB" ),
73 wxT( "Error occured when calling EnableNavigation. Error code: %d" ),
74 errCode.value() );
75 }
76 }
77}
78
79
81{
82 m_viewport2D = aViewport;
83
84 if( m_viewport2D == nullptr )
85 return;
86
87 m_view = m_viewport2D->GetView();
88
89 if( m_view == nullptr )
90 return;
91
92 m_viewportWidth = m_view->GetBoundary().GetWidth();
93
94 if( !IsEnabled() )
95 {
96 SafeNavlibInit( [this]()
97 {
98 EnableNavigation( true );
99 PutFrameTimingSource( TimingSource::SpaceMouse );
101 } );
102 }
103}
104
105
107{
108 wxLogTrace( m_logTrace, wxT( "NL_PL_EDITOR_PLUGIN_IMPL::SetFocus %d" ), aFocus );
109 NAV_3D::Write( navlib::focus_k, aFocus );
110}
111
112// temporary store for the command categories
113using CATEGORY_STORE = std::map<std::string, TDx::CCommandTreeNode*, std::less<>>;
114
115
124static void add_category( const std::string& aCategoryPath, CATEGORY_STORE& aCategoryStore )
125{
126 using TDx::SpaceMouse::CCategory;
127
128 auto parent_iter = aCategoryStore.begin();
129 std::string::size_type pos = aCategoryPath.find_last_of( '.' );
130
131 if( pos != std::string::npos )
132 {
133 std::string parentPath = aCategoryPath.substr( 0, pos );
134
135 if( !aCategoryStore.contains( parentPath ) )
136 {
137 add_category( parentPath, aCategoryStore );
138 parent_iter = aCategoryStore.find( parentPath );
139 }
140 }
141
142 std::string name = aCategoryPath.substr( pos + 1 );
143 auto categoryNode = std::make_unique<CCategory>( aCategoryPath.c_str(), name.c_str() );
144
145 aCategoryStore.try_emplace( aCategoryStore.end(), aCategoryPath, categoryNode.get() );
146
147 if( parent_iter != aCategoryStore.end() )
148 parent_iter->second->push_back( std::move( categoryNode ) );
149}
150
151
160static void try_add_category( const std::string& aCategoryPath, CATEGORY_STORE& aCategoryStore )
161{
162 if( !aCategoryStore.contains( aCategoryPath ) )
163 {
164 add_category( aCategoryPath, aCategoryStore );
165 }
166}
167
168
170{
171 wxLogTrace( m_logTrace, wxT( "NL_PL_EDITOR_PLUGIN_IMPL::exportCommandsAndImages" ) );
172
173 std::list<TOOL_ACTION*> actions = ACTION_MANAGER::GetActionList();
174
175 if( actions.empty() )
176 return;
177
178 using TDx::SpaceMouse::CCommand;
179 using TDx::SpaceMouse::CCommandSet;
180
181 // The root action set node
182 CCommandSet commandSet( "PL_EDITOR", "Drawing Sheet Editor" );
183
184 // Activate the command set
185 NAV_3D::PutActiveCommands( commandSet.GetId() );
186
187 // temporary store for the categories initialized with action set
188 CATEGORY_STORE categoryStore{ CATEGORY_STORE::value_type( ".", &commandSet ) };
189
190 std::vector<TDx::CImage> vImages;
191
192 for( const auto action : actions )
193 {
194 std::string label = action->GetMenuLabel().ToStdString();
195
196 if( label.empty() )
197 continue;
198
199 std::string name = action->GetName();
200
201 // Do no export commands for the 3DViewer app.
202
203 if( name.rfind( "3DViewer.", 0 ) == 0 )
204 continue;
205
206 std::string strCategory = action->GetToolName();
207 std::string description = action->GetDescription().ToStdString();
208
209 try_add_category( strCategory, categoryStore );
210 CATEGORY_STORE::iterator iter = categoryStore.find( strCategory );
211
212 wxCHECK2( iter != categoryStore.end(), continue );
213
214 // Arbitrary 8-bit data stream
215 wxMemoryOutputStream imageStream;
216
217 if( action->GetIcon() != BITMAPS::INVALID_BITMAP )
218 {
219 wxImage image = KiBitmap( action->GetIcon() ).ConvertToImage();
220 image.SaveFile( imageStream, wxBitmapType::wxBITMAP_TYPE_PNG );
221 image.Destroy();
222
223 if( imageStream.GetSize() )
224 {
225 const wxStreamBuffer* streamBuffer = imageStream.GetOutputStreamBuffer();
226 TDx::CImage tdxImage = TDx::CImage::FromData( "", 0, name.c_str() );
227 tdxImage.AssignImage(
228 std::string( std::bit_cast<const char*>( streamBuffer->GetBufferStart() ),
229 streamBuffer->GetBufferSize() ),
230 0 );
231
232 wxLogTrace( m_logTrace, wxT( "Adding image for : %s" ), name );
233 vImages.push_back( std::move( tdxImage ) );
234 }
235 }
236
237 wxLogTrace( m_logTrace, wxT( "Inserting command: %s, description: %s, in category: %s" ),
238 name, description, iter->first );
239
240 iter->second->push_back( CCommand( std::move( name ), std::move( label ),
241 std::move( description ) ) );
242 }
243
244 NAV_3D::AddCommandSet( commandSet );
245 NAV_3D::AddImages( vImages );
246}
247
248
249long NL_PL_EDITOR_PLUGIN_IMPL::GetCameraMatrix( navlib::matrix_t& matrix ) const
250{
251 if( m_view == nullptr )
252 return navlib::make_result_code( navlib::navlib_errc::no_data_available );
253
254 m_viewPosition = m_view->GetCenter();
255
256 double x = m_view->IsMirroredX() ? -1 : 1;
257 double y = m_view->IsMirroredY() ? 1 : -1;
258
259 // x * y * z = 1 for a right-handed coordinate system.
260 double z = x * y;
261
262 // Note: the connexion has been configured as row vectors, the coordinate system is defined in
263 // NL_PL_EDITOR_PLUGIN_IMPL::GetCoordinateSystem and the front view in NL_PL_EDITOR_PLUGIN_IMPL::GetFrontView.
264 matrix = { { { x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, m_viewPosition.x, m_viewPosition.y, 0,
265 1 } } };
266 return 0;
267}
268
269
270long NL_PL_EDITOR_PLUGIN_IMPL::GetPointerPosition( navlib::point_t& position ) const
271{
272 if( m_view == nullptr )
273 return navlib::make_result_code( navlib::navlib_errc::no_data_available );
274
275 VECTOR2D mouse_pointer = m_viewport2D->GetViewControls()->GetMousePosition();
276
277 position.x = mouse_pointer.x;
278 position.y = mouse_pointer.y;
279 position.z = 0;
280
281 return 0;
282}
283
284
285long NL_PL_EDITOR_PLUGIN_IMPL::GetViewExtents( navlib::box_t& extents ) const
286{
287 if( m_view == nullptr )
288 return navlib::make_result_code( navlib::navlib_errc::no_data_available );
289
290 double scale = m_viewport2D->GetGAL()->GetWorldScale();
291 BOX2D box = m_view->GetViewport();
292
294
295 extents.min_x = -box.GetWidth() / 2.0;
296 extents.min_y = -box.GetHeight() / 2.0;
297 extents.min_z = m_viewport2D->GetGAL()->GetMinDepth() / scale;
298 extents.max_x = box.GetWidth() / 2.0;
299 extents.max_y = box.GetHeight() / 2.0;
300 extents.max_z = m_viewport2D->GetGAL()->GetMaxDepth() / scale;
301 return 0;
302}
303
304
305long NL_PL_EDITOR_PLUGIN_IMPL::GetIsViewPerspective( navlib::bool_t& perspective ) const
306{
307 perspective = false;
308
309 return 0;
310}
311
312
313long NL_PL_EDITOR_PLUGIN_IMPL::SetCameraMatrix( const navlib::matrix_t& matrix )
314{
315 if( m_view == nullptr )
316 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
317
318 long result = 0;
319 VECTOR2D viewPos( matrix.m4x4[3][0], matrix.m4x4[3][1] );
320
321 if( !equals( m_view->GetCenter(), m_viewPosition,
322 static_cast<VECTOR2D::coord_type>( FLT_EPSILON ) ) )
323 {
324 m_view->SetCenter( viewPos + m_view->GetCenter() - m_viewPosition );
325 result = navlib::make_result_code( navlib::navlib_errc::error );
326 }
327 else
328 {
329 m_view->SetCenter( viewPos );
330 }
331
332 m_viewPosition = viewPos;
333
334 return result;
335}
336
337
338long NL_PL_EDITOR_PLUGIN_IMPL::SetViewExtents( const navlib::box_t& extents )
339{
340 if( m_view == nullptr )
341 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
342
343 long result = 0;
344
345 if( m_viewportWidth != m_view->GetViewport().GetWidth() )
346 result = navlib::make_result_code( navlib::navlib_errc::error );
347
348 double width = m_viewportWidth;
349 m_viewportWidth = extents.max_x - extents.min_x;
350
351 double scale = width / m_viewportWidth * m_view->GetScale();
352 m_view->SetScale( scale, m_view->GetCenter() );
353
354 if( !equals( m_view->GetScale(), scale, static_cast<double>( FLT_EPSILON ) ) )
355 result = navlib::make_result_code( navlib::navlib_errc::error );
356
357 return result;
358}
359
360
362{
363 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
364}
365
366
367long NL_PL_EDITOR_PLUGIN_IMPL::SetViewFrustum( const navlib::frustum_t& frustum )
368{
369 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
370}
371
372
373long NL_PL_EDITOR_PLUGIN_IMPL::GetModelExtents( navlib::box_t& extents ) const
374{
375 if( m_view == nullptr )
376 return navlib::make_result_code( navlib::navlib_errc::no_data_available );
377
378 BOX2I box = static_cast<PL_EDITOR_FRAME*>( m_viewport2D->GetParent() )->GetDocumentExtents();
379 box.Normalize();
380
381 double half_depth = 0.1 / m_viewport2D->GetGAL()->GetWorldScale();
382
383 if( box.GetWidth() == 0 && box.GetHeight() == 0 )
384 half_depth = 0;
385
386 extents.min_x = static_cast<double>( box.GetOrigin().x );
387 extents.min_y = static_cast<double>( box.GetOrigin().y );
388 extents.min_z = -half_depth;
389 extents.max_x = static_cast<double>( box.GetEnd().x );
390 extents.max_y = static_cast<double>( box.GetEnd().y );
391 extents.max_z = half_depth;
392
393 return 0;
394}
395
396
397long NL_PL_EDITOR_PLUGIN_IMPL::GetCoordinateSystem( navlib::matrix_t& matrix ) const
398{
399 // The coordinate system is defined as x to the right, y down and z into the screen.
400 matrix = { { { 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1 } } };
401 return 0;
402}
403
404
405long NL_PL_EDITOR_PLUGIN_IMPL::GetFrontView( navlib::matrix_t& matrix ) const
406{
407 matrix = { { { 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1 } } };
408 return 0;
409}
410
411
413{
414 empty = true;
415 return 0;
416}
417
418
419long NL_PL_EDITOR_PLUGIN_IMPL::GetIsViewRotatable( navlib::bool_t& isRotatable ) const
420{
421 isRotatable = false;
422 return 0;
423}
424
425
427{
428 if( commandId.empty() )
429 return 0;
430
431 if( m_viewport2D == nullptr )
432 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
433
434 wxWindow* parent = m_viewport2D->GetParent();
435
436 // Only allow command execution if the window is enabled. i.e. there is not a modal dialog
437 // currently active.
438 if( !parent || !parent->IsEnabled() )
439 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
440
441 TOOLS_HOLDER* tools_holder = dynamic_cast<TOOLS_HOLDER*>( parent );
442 TOOL_MANAGER* tool_manager = tools_holder ? tools_holder->GetToolManager() : nullptr;
443
444 // Only allow for command execution if the tool manager is accessible.
445 if( !tool_manager )
446 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
447
448 for( std::list<TOOL_ACTION*> actions = ACTION_MANAGER::GetActionList(); const auto action : actions )
449 {
450 if( action == nullptr )
451 continue;
452
453 if( commandId == action->GetName() )
454 {
455 // Get the selection to use to test if the action is enabled
456 const SELECTION& sel = tool_manager->GetToolHolder()->GetCurrentSelection();
457
458 const ACTION_CONDITIONS* aCond = tool_manager->GetActionManager()->GetCondition( *action );
459
460 if( aCond == nullptr )
461 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
462
463 aCond->enableCondition( sel );
464 tool_manager->RunAction( *action );
465 break;
466 }
467 }
468
469 return 0;
470}
471
472
474{
475 return 0;
476}
477
478
480{
481 m_isMoving = value;
482
483 return 0;
484}
485
486
488{
489 if( value == 0L )
490 m_viewport2D->ForceRefresh();
491
492 return 0;
493}
494
495
497{
498 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
499}
500
501
502long NL_PL_EDITOR_PLUGIN_IMPL::GetViewFrustum( navlib::frustum_t& frustum ) const
503{
504 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
505}
506
507
508long NL_PL_EDITOR_PLUGIN_IMPL::GetSelectionExtents( navlib::box_t& extents ) const
509{
510 return navlib::make_result_code( navlib::navlib_errc::no_data_available );
511}
512
513
514long NL_PL_EDITOR_PLUGIN_IMPL::GetSelectionTransform( navlib::matrix_t& transform ) const
515{
516 return navlib::make_result_code( navlib::navlib_errc::no_data_available );
517}
518
519
520long NL_PL_EDITOR_PLUGIN_IMPL::SetSelectionTransform( const navlib::matrix_t& matrix )
521{
522 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
523}
524
525
526long NL_PL_EDITOR_PLUGIN_IMPL::GetPivotPosition( navlib::point_t& position ) const
527{
528 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
529}
530
531
532long NL_PL_EDITOR_PLUGIN_IMPL::IsUserPivot( navlib::bool_t& userPivot ) const
533{
534 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
535}
536
537
538long NL_PL_EDITOR_PLUGIN_IMPL::SetPivotPosition( const navlib::point_t& position )
539{
540 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
541}
542
543
544long NL_PL_EDITOR_PLUGIN_IMPL::GetPivotVisible( navlib::bool_t& visible ) const
545{
546 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
547}
548
549
551{
552 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
553}
554
555
556long NL_PL_EDITOR_PLUGIN_IMPL::GetHitLookAt( navlib::point_t& position ) const
557{
558 return navlib::make_result_code( navlib::navlib_errc::no_data_available );
559}
560
561
563{
564 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
565}
566
567
568long NL_PL_EDITOR_PLUGIN_IMPL::SetHitDirection( const navlib::vector_t& direction )
569{
570 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
571}
572
573
574long NL_PL_EDITOR_PLUGIN_IMPL::SetHitLookFrom( const navlib::point_t& eye )
575{
576 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
577}
578
579
581{
582 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
583}
584
585
586long NL_PL_EDITOR_PLUGIN_IMPL::SetCameraTarget( const navlib::point_t& position )
587{
588 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
589}
const char * name
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition bitmap.cpp:100
@ INVALID_BITMAP
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
BOX2< VECTOR2D > BOX2D
Definition box2.h:919
const ACTION_CONDITIONS * GetCondition(const TOOL_ACTION &aAction) const
Get the conditions to use for a specific tool action.
static std::list< TOOL_ACTION * > & GetActionList()
Return list of TOOL_ACTIONs.
constexpr const Vec GetEnd() const
Definition box2.h:208
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:142
constexpr size_type GetWidth() const
Definition box2.h:210
constexpr size_type GetHeight() const
Definition box2.h:211
constexpr const Vec & GetOrigin() const
Definition box2.h:206
long SetViewExtents(const navlib::box_t &aExtents) override
long GetPivotPosition(navlib::point_t &aPosition) const override
long GetViewFOV(double &aFov) const override
long SetCameraTarget(const navlib::point_t &aPosition) override
long SetPivotPosition(const navlib::point_t &aPosition) override
long GetCameraMatrix(navlib::matrix_t &aMatrix) const override
long GetViewFrustum(navlib::frustum_t &aFrustum) const override
long SetHitDirection(const navlib::vector_t &aDirection) override
long SetHitLookFrom(const navlib::point_t &aPosition) override
long SetHitSelectionOnly(bool aSelectionOnly) override
long SetPivotVisible(bool aVisible) override
void SetCanvas(EDA_DRAW_PANEL_GAL *aViewport)
Sets the viewport controlled by the SpaceMouse.
long SetActiveCommand(std::string aCommandId) override
long SetCameraMatrix(const navlib::matrix_t &aMatrix) override
long SetSelectionTransform(const navlib::matrix_t &aMatrix) override
long GetViewExtents(navlib::box_t &aExtents) const override
long SetSettingsChanged(long aChangeNumber) override
long SetHitAperture(double aAperture) override
long GetHitLookAt(navlib::point_t &aPosition) const override
long GetPointerPosition(navlib::point_t &aPosition) const override
void exportCommandsAndImages()
Export the invocable actions and images to the 3Dconnexion UI.
void SetFocus(bool aFocus)
Set the connection to the 3Dconnexion driver to the focus state so that 3DMouse data is routed here.
long SetViewFrustum(const navlib::frustum_t &aFrustum) override
long GetIsSelectionEmpty(navlib::bool_t &aEmpty) const override
long GetIsViewRotatable(navlib::bool_t &isRotatable) const override
long GetFrontView(navlib::matrix_t &aMatrix) const override
long SetViewFOV(double aFov) override
long SetMotionFlag(bool aValue) override
long GetSelectionTransform(navlib::matrix_t &aTransform) const override
long IsUserPivot(navlib::bool_t &aUserPivot) const override
long GetModelExtents(navlib::box_t &aExtents) const override
long GetCoordinateSystem(navlib::matrix_t &aMatrix) const override
long GetPivotVisible(navlib::bool_t &aVisible) const override
long SetTransaction(long aValue) override
long GetSelectionExtents(navlib::box_t &aExtents) const override
NL_PL_EDITOR_PLUGIN_IMPL()
Initializes a new instance of the NL_PL_EDITOR_PLUGIN_IMPL.
long GetIsViewPerspective(navlib::bool_t &aPerspective) const override
The main window used in the drawing sheet editor.
const BOX2I GetDocumentExtents(bool aIncludeAllVisible=true) const override
Return bounding box of document with option to not include some items.
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
virtual SELECTION & GetCurrentSelection()
Get the current selection from the canvas area.
Master controller class:
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
TOOLS_HOLDER * GetToolHolder() const
ACTION_MANAGER * GetActionManager() const
double coord_type
Definition vector2d.h:70
static bool empty(const wxTextEntryBase *aCtrl)
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
bool SafeNavlibInit(const std::function< void()> &aInitFunc)
Attempt to run the given function, recovering from both C++ exceptions and abort() calls triggered by...
Safe initialization wrapper for the 3Dconnexion navlib SDK.
bool equals(glm::mat< L, C, T, Q > const &aFirst, glm::mat< L, C, T, Q > const &aSecond, T aEpsilon=static_cast< T >(FLT_EPSILON *10))
Template to compare two glm::mat<T> values for equality within a required epsilon.
std::map< std::string, TDx::CCommandTreeNode * > CATEGORY_STORE
static void try_add_category(const std::string &aCategoryPath, CATEGORY_STORE &aCategoryStore)
add_category wrapper.
static void try_add_category(const std::string &aCategoryPath, CATEGORY_STORE &aCategoryStore)
add_category wrapper.
static void add_category(const std::string &aCategoryPath, CATEGORY_STORE &aCategoryStore)
Add a category to the store.
const int scale
Functors that can be used to figure out how the action controls should be displayed in the UI and if ...
SELECTION_CONDITION enableCondition
Returns true if the UI control should be enabled.
wxString result
Test unit parsing edge cases and error handling.
VECTOR2< double > VECTOR2D
Definition vector2d.h:682
WX_VIEW_CONTROLS class definition.