KiCad PCB EDA Suite
Loading...
Searching...
No Matches
nl_gerbview_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 <gerbview_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_GERBVIEW_PLUGIN_IMPL::m_logTrace = wxT( "KI_TRACE_NL_GERBVIEW_PLUGIN" );
55
56
57NL_GERBVIEW_PLUGIN_IMPL::NL_GERBVIEW_PLUGIN_IMPL() : CNavigation3D( false, false )
58{
59 PutProfileHint( "KiCAD Gerbview" );
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_GERBVIEW_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 add_category( aCategoryPath, aCategoryStore );
164}
165
166
168{
169 wxLogTrace( m_logTrace, wxT( "NL_GERBVIEW_PLUGIN_IMPL::exportCommandsAndImages" ) );
170
171 std::list<TOOL_ACTION*> actions = ACTION_MANAGER::GetActionList();
172
173 if( actions.empty() )
174 return;
175
176 using TDx::SpaceMouse::CCommand;
177 using TDx::SpaceMouse::CCommandSet;
178
179 // The root action set node
180 CCommandSet commandSet( "GERBER_EDITOR", "Gerber Viewer" );
181
182 // Activate the command set
183 NAV_3D::PutActiveCommands( commandSet.GetId() );
184
185 // temporary store for the categories initialized with action set
186 CATEGORY_STORE categoryStore{ CATEGORY_STORE::value_type( ".", &commandSet ) };
187
188 std::vector<TDx::CImage> vImages;
189
190 for( const auto action : actions )
191 {
192 std::string label = action->GetMenuLabel().ToStdString();
193
194 if( label.empty() )
195 continue;
196
197 std::string name = action->GetName();
198
199 // Do no export commands for the 3DViewer app.
200
201 if( name.rfind( "3DViewer.", 0 ) == 0 )
202 continue;
203
204 std::string strCategory = action->GetToolName();
205 std::string description = action->GetDescription().ToStdString();
206
207 try_add_category( strCategory, categoryStore );
208 CATEGORY_STORE::iterator iter = categoryStore.find( strCategory );
209
210 // Arbitrary 8-bit data stream
211 wxMemoryOutputStream imageStream;
212
213 if( action->GetIcon() != BITMAPS::INVALID_BITMAP )
214 {
215 wxImage image = KiBitmap( action->GetIcon() ).ConvertToImage();
216 image.SaveFile( imageStream, wxBitmapType::wxBITMAP_TYPE_PNG );
217 image.Destroy();
218
219 if( imageStream.GetSize() )
220 {
221 const wxStreamBuffer* streamBuffer = imageStream.GetOutputStreamBuffer();
222 TDx::CImage tdxImage = TDx::CImage::FromData( "", 0, name.c_str() );
223 tdxImage.AssignImage(
224 std::string( std::bit_cast<const char*>( streamBuffer->GetBufferStart() ),
225 streamBuffer->GetBufferSize() ),
226 0 );
227
228 wxLogTrace( m_logTrace, wxT( "Adding image for : %s" ), name );
229 vImages.push_back( std::move( tdxImage ) );
230 }
231 }
232
233 if( iter != categoryStore.end() )
234 {
235 wxLogTrace( m_logTrace, wxT( "Inserting command: %s, description: %s, in category: %s" ),
236 name, description, iter->first );
237
238 iter->second->push_back( CCommand( name, label, description ) );
239 }
240 }
241
242 NAV_3D::AddCommandSet( commandSet );
243 NAV_3D::AddImages( vImages );
244}
245
246
247long NL_GERBVIEW_PLUGIN_IMPL::GetCameraMatrix( navlib::matrix_t& matrix ) const
248{
249 if( m_view == nullptr )
250 return navlib::make_result_code( navlib::navlib_errc::no_data_available );
251
252 m_viewPosition = m_view->GetCenter();
253
254 double x = m_view->IsMirroredX() ? -1 : 1;
255 double y = m_view->IsMirroredY() ? 1 : -1;
256
257 // x * y * z = 1 for a right-handed coordinate system.
258 double z = x * y;
259
260 // Note: the connexion has been configured as row vectors, the coordinate system is defined in
261 // NL_GERBVIEW_PLUGIN_IMPL::GetCoordinateSystem and the front view in NL_GERBVIEW_PLUGIN_IMPL::GetFrontView.
262 matrix = { { { x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, m_viewPosition.x, m_viewPosition.y, 0, 1 } } };
263 return 0;
264}
265
266
267long NL_GERBVIEW_PLUGIN_IMPL::GetPointerPosition( navlib::point_t& position ) const
268{
269 if( m_view == nullptr )
270 return navlib::make_result_code( navlib::navlib_errc::no_data_available );
271
272 VECTOR2D mouse_pointer = m_viewport2D->GetViewControls()->GetMousePosition();
273
274 position.x = mouse_pointer.x;
275 position.y = mouse_pointer.y;
276 position.z = 0;
277
278 return 0;
279}
280
281
282long NL_GERBVIEW_PLUGIN_IMPL::GetViewExtents( navlib::box_t& extents ) const
283{
284 if( m_view == nullptr )
285 return navlib::make_result_code( navlib::navlib_errc::no_data_available );
286
287 double scale = m_viewport2D->GetGAL()->GetWorldScale();
288 BOX2D box = m_view->GetViewport();
289
291
292 extents.min_x = -box.GetWidth() / 2.0;
293 extents.min_y = -box.GetHeight() / 2.0;
294 extents.min_z = m_viewport2D->GetGAL()->GetMinDepth() / scale;
295 extents.max_x = box.GetWidth() / 2.0;
296 extents.max_y = box.GetHeight() / 2.0;
297 extents.max_z = m_viewport2D->GetGAL()->GetMaxDepth() / scale;
298 return 0;
299}
300
301
302long NL_GERBVIEW_PLUGIN_IMPL::GetIsViewPerspective( navlib::bool_t& perspective ) const
303{
304 perspective = false;
305
306 return 0;
307}
308
309
310long NL_GERBVIEW_PLUGIN_IMPL::SetCameraMatrix( const navlib::matrix_t& matrix )
311{
312 if( m_view == nullptr )
313 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
314
315 long result = 0;
316 VECTOR2D viewPos( matrix.m4x4[3][0], matrix.m4x4[3][1] );
317
318 if( !equals( m_view->GetCenter(), m_viewPosition, static_cast<VECTOR2D::coord_type>( FLT_EPSILON ) ) )
319 {
320 m_view->SetCenter( viewPos + m_view->GetCenter() - m_viewPosition );
321 result = navlib::make_result_code( navlib::navlib_errc::error );
322 }
323 else
324 {
325 m_view->SetCenter( viewPos );
326 }
327
328 m_viewPosition = viewPos;
329
330 return result;
331}
332
333
334long NL_GERBVIEW_PLUGIN_IMPL::SetViewExtents( const navlib::box_t& extents )
335{
336 if( m_view == nullptr )
337 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
338
339 long result = 0;
340
341 if( m_viewportWidth != m_view->GetViewport().GetWidth() )
342 result = navlib::make_result_code( navlib::navlib_errc::error );
343
344 double width = m_viewportWidth;
345 m_viewportWidth = extents.max_x - extents.min_x;
346
347 double scale = width / m_viewportWidth * m_view->GetScale();
348 m_view->SetScale( scale, m_view->GetCenter() );
349
350 if( !equals( m_view->GetScale(), scale, static_cast<double>( FLT_EPSILON ) ) )
351 result = navlib::make_result_code( navlib::navlib_errc::error );
352
353 return result;
354}
355
356
358{
359 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
360}
361
362
363long NL_GERBVIEW_PLUGIN_IMPL::SetViewFrustum( const navlib::frustum_t& frustum )
364{
365 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
366}
367
368
369long NL_GERBVIEW_PLUGIN_IMPL::GetModelExtents( navlib::box_t& extents ) const
370{
371 if( m_view == nullptr )
372 return navlib::make_result_code( navlib::navlib_errc::no_data_available );
373
374 BOX2I box = static_cast<GERBVIEW_FRAME*>( m_viewport2D->GetParent() )->GetDocumentExtents();
375 box.Normalize();
376
377 double half_depth = 0.1 / m_viewport2D->GetGAL()->GetWorldScale();
378
379 if( box.GetWidth() == 0 && box.GetHeight() == 0 )
380 half_depth = 0;
381
382 extents.min_x = static_cast<double>( box.GetOrigin().x );
383 extents.min_y = static_cast<double>( box.GetOrigin().y );
384 extents.min_z = -half_depth;
385 extents.max_x = static_cast<double>( box.GetEnd().x );
386 extents.max_y = static_cast<double>( box.GetEnd().y );
387 extents.max_z = half_depth;
388
389 return 0;
390}
391
392
393long NL_GERBVIEW_PLUGIN_IMPL::GetCoordinateSystem( navlib::matrix_t& matrix ) const
394{
395 // The coordinate system is defined as x to the right, y down and z into the screen.
396 matrix = { { { 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1 } } };
397 return 0;
398}
399
400
401long NL_GERBVIEW_PLUGIN_IMPL::GetFrontView( navlib::matrix_t& matrix ) const
402{
403 matrix = { { { 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1 } } };
404 return 0;
405}
406
407
409{
410 empty = true;
411 return 0;
412}
413
414
415long NL_GERBVIEW_PLUGIN_IMPL::GetIsViewRotatable( navlib::bool_t& isRotatable ) const
416{
417 isRotatable = false;
418 return 0;
419}
420
421
422long NL_GERBVIEW_PLUGIN_IMPL::SetActiveCommand( std::string commandId )
423{
424 if( commandId.empty() )
425 return 0;
426
427 if( !m_viewport2D )
428 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
429
430 wxWindow* parent = m_viewport2D->GetParent();
431
432 // Only allow command execution if the window is enabled. i.e. there is not a modal dialog
433 // currently active.
434 if( !parent || !parent->IsEnabled() )
435 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
436
437 TOOLS_HOLDER* tools_holder = dynamic_cast<TOOLS_HOLDER*>( parent );
438 TOOL_MANAGER* tool_manager = tools_holder ? tools_holder->GetToolManager() : nullptr;
439
440 // Only allow for command execution if the tool manager is accessible.
441 if( !tool_manager )
442 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
443
444 for( std::list<TOOL_ACTION*> actions = ACTION_MANAGER::GetActionList(); const auto action : actions )
445 {
446 if( !action )
447 continue;
448
449 if( commandId == action->GetName() )
450 {
451 // Get the selection to use to test if the action is enabled
452 const SELECTION& sel = tool_manager->GetToolHolder()->GetCurrentSelection();
453
454 const ACTION_CONDITIONS* aCond = tool_manager->GetActionManager()->GetCondition( *action );
455
456 if( !aCond )
457 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
458
459 aCond->enableCondition( sel );
460 tool_manager->RunAction( *action );
461 break;
462 }
463 }
464
465 return 0;
466}
467
468
470{
471 return 0;
472}
473
474
476{
477 m_isMoving = value;
478
479 return 0;
480}
481
482
484{
485 if( value == 0L )
486 m_viewport2D->ForceRefresh();
487
488 return 0;
489}
490
491
492long NL_GERBVIEW_PLUGIN_IMPL::GetViewFOV( double& fov ) const
493{
494 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
495}
496
497
498long NL_GERBVIEW_PLUGIN_IMPL::GetViewFrustum( navlib::frustum_t& frustum ) const
499{
500 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
501}
502
503
504long NL_GERBVIEW_PLUGIN_IMPL::GetSelectionExtents( navlib::box_t& extents ) const
505{
506 return navlib::make_result_code( navlib::navlib_errc::no_data_available );
507}
508
509
510long NL_GERBVIEW_PLUGIN_IMPL::GetSelectionTransform( navlib::matrix_t& transform ) const
511{
512 return navlib::make_result_code( navlib::navlib_errc::no_data_available );
513}
514
515
516long NL_GERBVIEW_PLUGIN_IMPL::SetSelectionTransform( const navlib::matrix_t& matrix )
517{
518 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
519}
520
521
522long NL_GERBVIEW_PLUGIN_IMPL::GetPivotPosition( navlib::point_t& position ) const
523{
524 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
525}
526
527
528long NL_GERBVIEW_PLUGIN_IMPL::IsUserPivot( navlib::bool_t& userPivot ) const
529{
530 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
531}
532
533
534long NL_GERBVIEW_PLUGIN_IMPL::SetPivotPosition( const navlib::point_t& position )
535{
536 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
537}
538
539
540long NL_GERBVIEW_PLUGIN_IMPL::GetPivotVisible( navlib::bool_t& visible ) const
541{
542 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
543}
544
545
547{
548 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
549}
550
551
552long NL_GERBVIEW_PLUGIN_IMPL::GetHitLookAt( navlib::point_t& position ) const
553{
554 return navlib::make_result_code( navlib::navlib_errc::no_data_available );
555}
556
557
559{
560 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
561}
562
563
564long NL_GERBVIEW_PLUGIN_IMPL::SetHitDirection( const navlib::vector_t& direction )
565{
566 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
567}
568
569
570long NL_GERBVIEW_PLUGIN_IMPL::SetHitLookFrom( const navlib::point_t& eye )
571{
572 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
573}
574
575
577{
578 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
579}
580
581
582long NL_GERBVIEW_PLUGIN_IMPL::SetCameraTarget( const navlib::point_t& position )
583{
584 return navlib::make_result_code( navlib::navlib_errc::invalid_operation );
585}
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
const BOX2I GetDocumentExtents(bool aIncludeAllVisible=true) const override
Return bounding box of document with option to not include some items.
long GetCoordinateSystem(navlib::matrix_t &aMatrix) const override
long GetHitLookAt(navlib::point_t &aPosition) const override
long SetSettingsChanged(long aChangeNumber) override
long GetIsViewPerspective(navlib::bool_t &aPerspective) const override
long SetCameraMatrix(const navlib::matrix_t &aMatrix) override
long GetPivotPosition(navlib::point_t &aPosition) const override
long GetViewFOV(double &aFov) const override
long SetPivotVisible(bool aVisible) override
long SetSelectionTransform(const navlib::matrix_t &aMatrix) override
long GetViewFrustum(navlib::frustum_t &aFrustum) const override
long SetTransaction(long aValue) override
long GetIsViewRotatable(navlib::bool_t &isRotatable) const override
long SetHitLookFrom(const navlib::point_t &aPosition) override
long GetIsSelectionEmpty(navlib::bool_t &aEmpty) const override
long SetMotionFlag(bool aValue) override
long GetCameraMatrix(navlib::matrix_t &aMatrix) const override
long SetViewExtents(const navlib::box_t &aExtents) override
long SetCameraTarget(const navlib::point_t &aPosition) override
long GetFrontView(navlib::matrix_t &aMatrix) const override
long SetHitSelectionOnly(bool aSelectionOnly) override
long GetSelectionExtents(navlib::box_t &aExtents) const override
long SetHitDirection(const navlib::vector_t &aDirection) override
long SetViewFOV(double aFov) override
NL_GERBVIEW_PLUGIN_IMPL()
Initializes a new instance of the NL_GERBVIEW_PLUGIN_IMPL.
long GetPointerPosition(navlib::point_t &aPosition) const override
long SetPivotPosition(const navlib::point_t &aPosition) override
void exportCommandsAndImages()
Export the invocable actions and images to the 3Dconnexion UI.
void SetCanvas(EDA_DRAW_PANEL_GAL *aViewport)
Sets the viewport controlled by the SpaceMouse.
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 SetActiveCommand(std::string aCommandId) override
long SetHitAperture(double aAperture) override
long GetViewExtents(navlib::box_t &aExtents) const override
long IsUserPivot(navlib::bool_t &aUserPivot) const override
long GetPivotVisible(navlib::bool_t &aVisible) const override
long GetModelExtents(navlib::box_t &aExtents) const override
long GetSelectionTransform(navlib::matrix_t &aTransform) const override
EDA_DRAW_PANEL_GAL * m_viewport2D
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 add_category(const std::string &aCategoryPath, CATEGORY_STORE &aCategoryStore)
Add a category to the store.
Declaration of the NL_GERBVIEW_PLUGIN_IMPL class.
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.