#ifndef CNavlibInterface_HPP_INCLUDED #define CNavlibInterface_HPP_INCLUDED /* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (c) 2018-2021 3Dconnexion. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ /** * @file CNavlibInterface.hpp * @brief The interface to the navlib. */ #include #include #include // stdlib #include #include #include #include #include // navlib #include #include #include namespace TDx { namespace SpaceMouse { /// /// Template to allow defining the static members in the header file /// template struct StaticSinkCollection { protected: static CCookieCollection s_sinkCollection; static std::mutex s_mutex; }; template CCookieCollection StaticSinkCollection::s_sinkCollection; /// /// Mutex used to synchronize the trace output. /// template std::mutex StaticSinkCollection::s_mutex; namespace Navigation3D { /// /// Class implements the interface to the navlib. /// class CNavlibInterface : public INavlib, private StaticSinkCollection { public: /// /// Initializes a new instance of the CNavlibInterface class. /// /// Shared pointer to the instance implementing the IAccessors interface /// accessors and mutators. /// true to use multi-threading, false for single-threaded. /// true for row-major ordered matrices, false for column-major. explicit CNavlibInterface(std::shared_ptr sink, bool multiThreaded = false, bool rowMajor = false) : m_hdl(INVALID_NAVLIB_HANDLE), m_cookie(s_sinkCollection.insert(sink)) #if defined(_MSC_VER) && (_MSC_VER < 1800) { navlib::nlCreateOptions_t options = {sizeof(navlib::nlCreateOptions_t), multiThreaded, rowMajor ? navlib::row_major_order : navlib::none}; m_createOptions = options; } #else , m_createOptions{sizeof(navlib::nlCreateOptions_t), multiThreaded, rowMajor ? navlib::row_major_order : navlib::none} { } #endif /// /// Initializes a new instance of the CNavlibInterface class. /// /// Shared pointer to the instance implementing the IAccessors interface /// accessors and mutators. /// true to use multi-threading, false for single-threaded. /// A combination of the values. explicit CNavlibInterface(std::shared_ptr sink, bool multiThreaded, navlib::nlOptions_t options) : m_hdl(INVALID_NAVLIB_HANDLE), m_cookie(s_sinkCollection.insert(sink)) #if defined(_MSC_VER) && (_MSC_VER < 1800) { navlib::nlCreateOptions_t createOptions = {sizeof(navlib::nlCreateOptions_t), multiThreaded, options}; m_createOptions = createOptions; } #else , m_createOptions{sizeof(navlib::nlCreateOptions_t), multiThreaded, options} { } #endif /// /// Clean up the resources /// virtual ~CNavlibInterface() { using namespace ::navlib; if (m_cookie) { s_sinkCollection.erase(m_cookie); } if (m_hdl != INVALID_NAVLIB_HANDLE) { NlClose(m_hdl); } } public: /// /// Close the connection to the 3D navigation library. /// void Close() override { using namespace ::navlib; if (m_hdl != INVALID_NAVLIB_HANDLE) { std::unique_lock lock(m_mutex); if (m_hdl != INVALID_NAVLIB_HANDLE) { NlClose(m_hdl); m_hdl = INVALID_NAVLIB_HANDLE; } } } /// /// Opens a connection to the 3D navigation library. /// void Open() override { Open(m_name); } /// /// Opens a connection to the 3D navigation library /// /// The text to display in the 3Dconnexion profile. /// The connection to the library is already open. /// Cannot create a connection to the library. /// The text for the profile is empty. void Open(std::string profileText) override { using namespace ::navlib; if (profileText.empty()) { throw std::invalid_argument("The text for the profile is empty."); } std::unique_lock lock(m_mutex); if (m_hdl != INVALID_NAVLIB_HANDLE) { throw std::system_error(navlib::make_error_code(navlib_errc::already_connected), "Connection to the library is already open."); } accessor_t accessors[] = { {motion_k, nullptr, &CNavlibInterface::SetMotionFlag, m_cookie}, {transaction_k, nullptr, &CNavlibInterface::SetTransaction, m_cookie}, {coordinate_system_k, &CNavlibInterface::GetCoordinateSystem, nullptr, m_cookie}, {views_front_k, &CNavlibInterface::GetFrontView, nullptr, m_cookie}, // view access {view_affine_k, &CNavlibInterface::GetCameraMatrix, &CNavlibInterface::SetCameraMatrix, m_cookie}, {view_constructionPlane_k, &CNavlibInterface::GetViewConstructionPlane, nullptr, m_cookie}, {view_extents_k, &CNavlibInterface::GetViewExtents, &CNavlibInterface::SetViewExtents, m_cookie}, {view_fov_k, &CNavlibInterface::GetViewFOV, &CNavlibInterface::SetViewFOV, m_cookie}, {view_frustum_k, &CNavlibInterface::GetViewFrustum, &CNavlibInterface::SetViewFrustum, m_cookie}, {view_perspective_k, &CNavlibInterface::GetIsViewPerspective, nullptr, m_cookie}, {view_target_k, &CNavlibInterface::GetCameraTarget, &CNavlibInterface::SetCameraTarget, m_cookie}, {view_rotatable_k, &CNavlibInterface::GetIsViewRotatable, nullptr, m_cookie}, {pointer_position_k, &CNavlibInterface::GetPointerPosition, &CNavlibInterface::SetPointerPosition, m_cookie}, // pivot accessors {pivot_position_k, &CNavlibInterface::GetPivotPosition, &CNavlibInterface::SetPivotPosition, m_cookie}, {pivot_user_k, &CNavlibInterface::IsUserPivot, nullptr, m_cookie}, {pivot_visible_k, &CNavlibInterface::GetPivotVisible, &CNavlibInterface::SetPivotVisible, m_cookie}, // hit testing for auto pivot algorithm etc. {hit_lookfrom_k, nullptr, &CNavlibInterface::SetHitLookFrom, m_cookie}, {hit_direction_k, nullptr, &CNavlibInterface::SetHitDirection, m_cookie}, {hit_aperture_k, nullptr, &CNavlibInterface::SetHitAperture, m_cookie}, {hit_lookat_k, &CNavlibInterface::GetHitLookAt, nullptr, m_cookie}, {hit_selectionOnly_k, nullptr, &CNavlibInterface::SetHitSelectionOnly, m_cookie}, // model access {model_extents_k, &CNavlibInterface::GetModelExtents, nullptr, m_cookie}, {selection_empty_k, &CNavlibInterface::GetIsSelectionEmpty, nullptr, m_cookie}, {selection_extents_k, &CNavlibInterface::GetSelectionExtents, nullptr, m_cookie}, {selection_affine_k, &CNavlibInterface::GetSelectionTransform, &CNavlibInterface::SetSelectionTransform, m_cookie}, // events {commands_activeCommand_k, nullptr, &CNavlibInterface::SetActiveCommand, m_cookie}, {events_keyPress_k, nullptr, &CNavlibInterface::SetKeyPress, m_cookie}, {events_keyRelease_k, nullptr, &CNavlibInterface::SetKeyRelease, m_cookie}, {settings_changed_k, nullptr, &CNavlibInterface::SetSettingsChanged, m_cookie}}; // Create the navlib instance long error = NlCreate(&m_hdl, profileText.c_str(), static_cast(accessors), sizeof(accessors) / sizeof(accessors[0]), &m_createOptions); if (error != 0) { throw std::system_error( navlib::make_error_code(static_cast(error & 0xffff)), "Cannot create a connection to the 3DMouse."); } m_name = std::move(profileText); } /// /// Writes the value of a property to the navlib. /// /// The name of the navlib property to /// write. /// The to write. /// 0 =no error, otherwise a value from . /// No connection to the navlib. long Write(const std::string &propertyName, const navlib::value &value) override { if (m_hdl == INVALID_NAVLIB_HANDLE) { return navlib::make_result_code(navlib::navlib_errc::invalid_operation); } long resultCode = WriteValue(m_hdl, propertyName.c_str(), &value); return resultCode; } /// /// Reads the value of a navlib property. /// /// The name of the navlib property to /// read. /// The to read. /// 0 =no error, otherwise a value from . /// No connection to the navlib. long Read(const std::string &propertyName, navlib::value &value) const override { if (m_hdl == INVALID_NAVLIB_HANDLE) { return navlib::make_result_code(navlib::navlib_errc::invalid_operation); } long resultCode = ReadValue(m_hdl, propertyName.c_str(), &value); return resultCode; } /// /// Reads the value of a navlib string property. /// /// The name of the navlib property to /// read. /// The value of the property. /// 0 =no error, otherwise a value from . /// No connection to the navlib. long Read(const std::string &propertyName, std::string &string) const override { if (m_hdl == INVALID_NAVLIB_HANDLE) { return navlib::make_result_code(navlib::navlib_errc::invalid_operation); } navlib::value value(&string[0], string.length()); long resultCode = ReadValue(m_hdl, propertyName.c_str(), &value); if ((resultCode & 0xffff) == static_cast(navlib::navlib_errc::insufficient_buffer)) { string.resize(value.string.length); value = navlib::value(&string[0], string.length()); resultCode = ReadValue(m_hdl, propertyName.c_str(), &value); } return resultCode; } private: typedef std::shared_ptr isink_t; template static long GetValue(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value, F fn) { isink_t isink; try { isink = s_sinkCollection.at(cookie); long result = fn(isink); #if _DEBUG && TRACE_NAVLIB std::unique_lock lock(s_mutex); std::clog << "GetValue(0x" << std::hex << cookie << std::dec << ", " << property << ", " << *value << ") result =0x" << std::hex << result << std::endl; #endif return result; } catch (const std::out_of_range &e) { std::unique_lock lock(s_mutex); std::cerr << "std::out_of_range exception thrown in GetValue(0x" << std::hex << cookie << std::dec << ", " << property << ", value)\n" << *value << e.what() << std::endl; return navlib::make_result_code(navlib::navlib_errc::invalid_argument); } catch (const std::exception &e) { std::cerr << "Uncaught exception thrown in GetValue(0x" << std::hex << cookie << std::dec << ", " << property << ", value)\n" << *value << e.what() << std::endl; } return navlib::make_result_code(navlib::navlib_errc::error); } template static long SetValue(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value, F fn) { isink_t isink; try { isink = s_sinkCollection.at(cookie); long result = fn(isink); #if _DEBUG && TRACE_NAVLIB std::clog << "SetValue(0x" << std::hex << cookie << std::dec << ", " << property << ", " << *value << ") result =0x" << std::hex << result << std::endl; #endif return result; } catch (const std::out_of_range &e) { std::unique_lock lock(s_mutex); std::cerr << "std::out_of_range exception thrown in SetValue(0x" << std::hex << cookie << std::dec << ", " << property << ", value)\n" << *value << e.what() << std::endl; return navlib::make_result_code(navlib::navlib_errc::invalid_argument); } catch (const std::exception &e) { std::unique_lock lock(s_mutex); std::cerr << "Uncaught exception thrown in SetValue(0x" << std::hex << cookie << std::dec << ", " << property << "," << *value << ")\n" << e.what() << std::endl; } return navlib::make_result_code(navlib::navlib_errc::error); } /// /// IEvents accessors and mutators /// static long SetActiveCommand(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetActiveCommand(static_cast(*value)); }); } static long SetSettingsChanged(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetSettingsChanged(*value); }); } static long SetKeyPress(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetKeyPress(*value); }); } static long SetKeyRelease(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetKeyRelease(*value); }); } /// /// IHit accessors and mutators /// static long GetHitLookAt(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetHitLookAt(*value); }); } static long SetHitAperture(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetHitAperture(*value); }); } static long SetHitDirection(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetHitDirection(*value); }); } static long SetHitLookFrom(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetHitLookFrom(*value); }); } static long SetHitSelectionOnly(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetHitSelectionOnly(*value); }); } /// /// IModel accessors and mutators /// static long GetModelExtents(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetModelExtents(*value); }); } static long GetSelectionExtents(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetSelectionExtents(*value); }); } static long GetSelectionTransform(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetSelectionTransform(*value); }); } static long GetIsSelectionEmpty(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetIsSelectionEmpty(*value); }); } static long SetSelectionTransform(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetSelectionTransform(*value); }); } /// /// IPivot accessors and mutators /// static long GetPivotPosition(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetPivotPosition(*value); }); } static long IsUserPivot(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->IsUserPivot(*value); }); } static long SetPivotPosition(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetPivotPosition(*value); }); } static long GetPivotVisible(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetPivotVisible(*value); }); } static long SetPivotVisible(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetPivotVisible(*value); }); } /// /// ISpace3D accessors and mutators /// static long GetCoordinateSystem(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetCoordinateSystem(*value); }); } static long GetFrontView(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetFrontView(*value); }); } /// /// IState accessors and mutators /// static long SetTransaction(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetTransaction(*value); }); } static long SetMotionFlag(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetMotionFlag(*value); }); } /// /// IView accessors and mutators /// static long GetCameraMatrix(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetCameraMatrix(*value); }); } static long GetCameraTarget(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetCameraTarget(*value); }); } static long GetPointerPosition(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetPointerPosition(*value); }); } static long GetViewConstructionPlane(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetViewConstructionPlane(*value); }); } static long GetViewExtents(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetViewExtents(*value); }); } static long GetViewFOV(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetViewFOV(*value); }); } static long GetViewFrustum(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetViewFrustum(*value); }); } static long GetIsViewPerspective(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetIsViewPerspective(*value); }); } static long GetIsViewRotatable(navlib::param_t cookie, navlib::property_t property, navlib::value_t *value) { return GetValue(cookie, property, value, [&](isink_t isink) { return isink->GetIsViewRotatable(*value); }); } static long SetCameraMatrix(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetCameraMatrix(*value); }); } static long SetCameraTarget(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetCameraTarget(*value); }); } static long SetPointerPosition(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetPointerPosition(*value); }); } static long SetViewExtents(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetViewExtents(*value); }); } static long SetViewFOV(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetViewFOV(*value); }); } static long SetViewFrustum(navlib::param_t cookie, navlib::property_t property, const navlib::value_t *value) { return SetValue(cookie, property, value, [&](isink_t isink) { return isink->SetViewFrustum(*value); }); } private: /// /// Read a value from the navlib. /// /// The to the navigation library returned by /// a previous call to . /// The name of the navlib property to read. /// Pointer to a to receive the value. /// 0 =no error, otherwise a value from . /// No connection to the navlib / 3D Mouse. long ReadValue(navlib::nlHandle_t nh, navlib::property_t name, navlib::value_t *value) const { using namespace ::navlib; try { long resultCode = NlReadValue(nh, name, value); #if _DEBUG && TRACE_NAVLIB std::unique_lock lock(s_mutex); std::clog << "NlReadValue(0x" << std::hex << nh << std::dec << ", " << name << ", " << *value << ") result =0x" << std::hex << resultCode << std::endl; #endif return resultCode; } catch (const std::exception &e) { std::unique_lock lock(s_mutex); std::cerr << "exception thrown in NlReadValue(0x" << std::hex << nh << std::dec << ", " << name << ", value)\n" << *value << " " << e.what() << std::endl; } return navlib::make_result_code(navlib::navlib_errc::error); } /// /// Write a value to the navlib. /// /// The to the navigation library returned by /// a previous call to . /// The name of the navlib property to read. /// Pointer to a to receive the value. /// 0 =no error, otherwise a value from . /// No connection to the navlib / 3D Mouse. long WriteValue(navlib::nlHandle_t nh, navlib::property_t name, const navlib::value_t *value) { using namespace ::navlib; try { long resultCode = NlWriteValue(nh, name, value); #if _DEBUG && TRACE_NAVLIB std::unique_lock lock(s_mutex); std::clog << "NlWriteValue(0x" << std::hex << nh << std::dec << ", " << name << ", " << *value << ") result =0x" << std::hex << resultCode << std::endl; #endif return resultCode; } catch (const std::exception &e) { std::unique_lock lock(s_mutex); std::cerr << "exception thrown in NlWriteValue(0x" << std::hex << nh << std::dec << ", " << name << ", value)\n" << *value << " " << e.what() << std::endl; } return navlib::make_result_code(navlib::navlib_errc::error); } private: navlib::nlHandle_t m_hdl; std::mutex m_mutex; navlib::param_t m_cookie; std::string m_name; navlib::nlCreateOptions_t m_createOptions; }; } // namespace Navigation3D } // namespace SpaceMouse } // namespace TDx #endif // CNavigationModelImpl_HPP_INCLUDED