From 36d16b22430e14351147e2666faf4d58130bfe8e Mon Sep 17 00:00:00 2001 From: hlorus Date: Sun, 14 Jan 2024 21:46:50 +0100 Subject: [PATCH] [Measure] Add features, viewproviders and preferences for unified measurement facility --- .../InitializeFreeCADBuildOptions.cmake | 1 + cMake/FreeCAD_Helpers/PrintFinalReport.cmake | 1 + src/Mod/CMakeLists.txt | 5 +- src/Mod/Measure/App/AppMeasure.cpp | 124 +++- src/Mod/Measure/App/CMakeLists.txt | 41 +- src/Mod/Measure/App/MeasureAngle.cpp | 264 ++++++++ src/Mod/Measure/App/MeasureAngle.h | 93 +++ src/Mod/Measure/App/MeasureArea.cpp | 181 ++++++ src/Mod/Measure/App/MeasureArea.h | 86 +++ src/Mod/Measure/App/MeasureBase.cpp | 211 +++++++ src/Mod/Measure/App/MeasureBase.h | 131 ++++ src/Mod/Measure/App/MeasureBasePy.xml | 20 + src/Mod/Measure/App/MeasureBasePyImp.cpp | 40 ++ src/Mod/Measure/App/MeasureDistance.cpp | 231 +++++++ src/Mod/Measure/App/MeasureDistance.h | 87 +++ src/Mod/Measure/App/MeasureLength.cpp | 184 ++++++ src/Mod/Measure/App/MeasureLength.h | 81 +++ src/Mod/Measure/App/MeasurePosition.cpp | 177 ++++++ src/Mod/Measure/App/MeasurePosition.h | 82 +++ src/Mod/Measure/App/MeasureRadius.cpp | 211 +++++++ src/Mod/Measure/App/MeasureRadius.h | 90 +++ src/Mod/Measure/App/Measurement.cpp | 2 +- src/Mod/Measure/App/MeasurementPy.xml | 1 + src/Mod/Measure/App/Preferences.cpp | 80 +++ src/Mod/Measure/App/Preferences.h | 58 ++ src/Mod/Measure/CMakeLists.txt | 25 +- src/Mod/Measure/Gui/AppMeasureGui.cpp | 99 +++ src/Mod/Measure/Gui/CMakeLists.txt | 90 +++ src/Mod/Measure/Gui/Command.cpp | 33 + .../Gui/DlgPrefsMeasureAppearanceImp.cpp | 75 +++ .../Gui/DlgPrefsMeasureAppearanceImp.h | 55 ++ .../Gui/DlgPrefsMeasureAppearanceImp.ui | 247 ++++++++ src/Mod/Measure/Gui/PreCompiled.cpp | 22 + src/Mod/Measure/Gui/PreCompiled.h | 97 +++ src/Mod/Measure/Gui/Resources/Measure.qrc | 6 + .../Resources/icons/preferences-measure.svg | 317 ++++++++++ .../Gui/Resources/icons/umf-measurement.svg | 317 ++++++++++ .../Gui/Resources/translations/Measure.ts | 42 ++ .../Measure/Gui/ViewProviderMeasureAngle.cpp | 339 ++++++++++ .../Measure/Gui/ViewProviderMeasureAngle.h | 73 +++ .../Measure/Gui/ViewProviderMeasureBase.cpp | 597 ++++++++++++++++++ src/Mod/Measure/Gui/ViewProviderMeasureBase.h | 159 +++++ .../Gui/ViewProviderMeasureDistance.cpp | 222 +++++++ .../Measure/Gui/ViewProviderMeasureDistance.h | 69 ++ src/Mod/Measure/InitGui.py | 43 ++ src/Mod/Measure/MeasureCOM.py | 152 +++++ src/Mod/Measure/MeasureGlobal.h | 13 +- src/Mod/Measure/UtilsMeasure.py | 52 ++ .../App/{Measure.cpp => MeasureClient.cpp} | 268 ++++---- 49 files changed, 5762 insertions(+), 132 deletions(-) create mode 100644 src/Mod/Measure/App/MeasureAngle.cpp create mode 100644 src/Mod/Measure/App/MeasureAngle.h create mode 100644 src/Mod/Measure/App/MeasureArea.cpp create mode 100644 src/Mod/Measure/App/MeasureArea.h create mode 100644 src/Mod/Measure/App/MeasureBase.cpp create mode 100644 src/Mod/Measure/App/MeasureBase.h create mode 100644 src/Mod/Measure/App/MeasureBasePy.xml create mode 100644 src/Mod/Measure/App/MeasureBasePyImp.cpp create mode 100644 src/Mod/Measure/App/MeasureDistance.cpp create mode 100644 src/Mod/Measure/App/MeasureDistance.h create mode 100644 src/Mod/Measure/App/MeasureLength.cpp create mode 100644 src/Mod/Measure/App/MeasureLength.h create mode 100644 src/Mod/Measure/App/MeasurePosition.cpp create mode 100644 src/Mod/Measure/App/MeasurePosition.h create mode 100644 src/Mod/Measure/App/MeasureRadius.cpp create mode 100644 src/Mod/Measure/App/MeasureRadius.h create mode 100644 src/Mod/Measure/App/Preferences.cpp create mode 100644 src/Mod/Measure/App/Preferences.h create mode 100644 src/Mod/Measure/Gui/AppMeasureGui.cpp create mode 100644 src/Mod/Measure/Gui/CMakeLists.txt create mode 100644 src/Mod/Measure/Gui/Command.cpp create mode 100644 src/Mod/Measure/Gui/DlgPrefsMeasureAppearanceImp.cpp create mode 100644 src/Mod/Measure/Gui/DlgPrefsMeasureAppearanceImp.h create mode 100644 src/Mod/Measure/Gui/DlgPrefsMeasureAppearanceImp.ui create mode 100644 src/Mod/Measure/Gui/PreCompiled.cpp create mode 100644 src/Mod/Measure/Gui/PreCompiled.h create mode 100644 src/Mod/Measure/Gui/Resources/Measure.qrc create mode 100644 src/Mod/Measure/Gui/Resources/icons/preferences-measure.svg create mode 100644 src/Mod/Measure/Gui/Resources/icons/umf-measurement.svg create mode 100644 src/Mod/Measure/Gui/Resources/translations/Measure.ts create mode 100644 src/Mod/Measure/Gui/ViewProviderMeasureAngle.cpp create mode 100644 src/Mod/Measure/Gui/ViewProviderMeasureAngle.h create mode 100644 src/Mod/Measure/Gui/ViewProviderMeasureBase.cpp create mode 100644 src/Mod/Measure/Gui/ViewProviderMeasureBase.h create mode 100644 src/Mod/Measure/Gui/ViewProviderMeasureDistance.cpp create mode 100644 src/Mod/Measure/Gui/ViewProviderMeasureDistance.h create mode 100644 src/Mod/Measure/InitGui.py create mode 100644 src/Mod/Measure/MeasureCOM.py create mode 100644 src/Mod/Measure/UtilsMeasure.py rename src/Mod/Part/App/{Measure.cpp => MeasureClient.cpp} (57%) diff --git a/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake b/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake index a08f96438d..ee77487493 100644 --- a/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake +++ b/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake @@ -130,6 +130,7 @@ macro(InitializeFreeCADBuildOptions) option(BUILD_SPREADSHEET "Build the FreeCAD spreadsheet module" ON) option(BUILD_START "Build the FreeCAD start module" ON) option(BUILD_TEST "Build the FreeCAD test module" ON) + option(BUILD_MEASURE "Build the FreeCAD Measure module" ON) option(BUILD_TECHDRAW "Build the FreeCAD Technical Drawing module" ON) option(BUILD_TUX "Build the FreeCAD Tux module" ON) option(BUILD_WEB "Build the FreeCAD Web module" ON) diff --git a/cMake/FreeCAD_Helpers/PrintFinalReport.cmake b/cMake/FreeCAD_Helpers/PrintFinalReport.cmake index 01c26c4c5c..b255abd4cf 100644 --- a/cMake/FreeCAD_Helpers/PrintFinalReport.cmake +++ b/cMake/FreeCAD_Helpers/PrintFinalReport.cmake @@ -118,6 +118,7 @@ macro(PrintFinalReport) value(BUILD_SPREADSHEET) value(BUILD_START) value(BUILD_SURFACE) + value(BUILD_MEASURE) value(BUILD_TECHDRAW) value(BUILD_TEST) value(BUILD_TUX) diff --git a/src/Mod/CMakeLists.txt b/src/Mod/CMakeLists.txt index c7101069f6..f439136157 100644 --- a/src/Mod/CMakeLists.txt +++ b/src/Mod/CMakeLists.txt @@ -122,8 +122,11 @@ if(BUILD_SURFACE) add_subdirectory(Surface) endif(BUILD_SURFACE) -if(BUILD_TECHDRAW) +if(BUILD_MEASURE) add_subdirectory(Measure) +endif(BUILD_MEASURE) + +if(BUILD_TECHDRAW) add_subdirectory(TechDraw) endif(BUILD_TECHDRAW) diff --git a/src/Mod/Measure/App/AppMeasure.cpp b/src/Mod/Measure/App/AppMeasure.cpp index 4be22df894..856c44c875 100644 --- a/src/Mod/Measure/App/AppMeasure.cpp +++ b/src/Mod/Measure/App/AppMeasure.cpp @@ -22,14 +22,38 @@ #include "PreCompiled.h" +#include #include #include +#include +#include + #include "Measurement.h" #include "MeasurementPy.h" +// unified measurement facility +#include "MeasureBase.h" +#include "MeasureBasePy.h" + +#include "MeasureAngle.h" +#include "MeasureDistance.h" +#include "MeasurePosition.h" +#include "MeasureLength.h" +#include "MeasureArea.h" +#include "MeasureRadius.h" namespace Measure { + +// explicit template instantiations +template class MeasureExport MeasureBaseExtendable; +template class MeasureExport MeasureBaseExtendable; +template class MeasureExport MeasureBaseExtendable; +template class MeasureExport MeasureBaseExtendable; +template class MeasureExport MeasureBaseExtendable; +template class MeasureExport MeasureBaseExtendable; + + class Module : public Py::ExtensionModule { public: @@ -48,6 +72,7 @@ PyObject* initModule() } // namespace Measure +using namespace Measure; /* Python entry */ PyMOD_INIT_FUNC(Measure) @@ -62,14 +87,103 @@ PyMOD_INIT_FUNC(Measure) } PyObject* mod = Measure::initModule(); // Add Types to module - Base::Interpreter().addType(&Measure::MeasurementPy ::Type,mod,"Measurement"); - Base::Console().Log("Loading Inspection module... done\n"); - Measure::Measurement ::init(); + Base::Interpreter().addType(&Measure::MeasurementPy::Type, mod, "Measurement"); + Base::Interpreter().addType(&Measure::MeasureBasePy::Type, mod, "MeasureBase"); + + Measure::Measurement ::init(); + + // umf classes + Measure::MeasureBase ::init(); + Measure::MeasurePython ::init(); + Measure::MeasureAngle ::init(); + Measure::MeasureDistance ::init(); + Measure::MeasurePosition ::init(); + Measure::MeasureLength ::init(); + Measure::MeasureArea ::init(); + Measure::MeasureRadius ::init(); + + // Add fundamental umf Measure Types + + App::MeasureManager::addMeasureType("DISTANCE", + "Distance", + "Measure::MeasureDistance", + MeasureDistance::isValidSelection, + MeasureDistance::isPrioritizedSelection + ); + + App::MeasureManager::addMeasureType( + "ANGLE", + "Angle", + "Measure::MeasureAngle", + MeasureAngle::isValidSelection, + MeasureAngle::isPrioritizedSelection + ); + + App::MeasureManager::addMeasureType( + "LENGTH", + "Length", + "Measure::MeasureLength", + MeasureLength::isValidSelection, + nullptr + ); + + App::MeasureManager::addMeasureType( + "POSITION", + "Position", + "Measure::MeasurePosition", + MeasurePosition::isValidSelection, + nullptr + ); + + App::MeasureManager::addMeasureType( + "AREA", + "Area", + "Measure::MeasureArea", + MeasureArea::isValidSelection, + nullptr + ); + + App::MeasureManager::addMeasureType( + "RADIUS", + "Radius", + "Measure::MeasureRadius", + MeasureRadius::isValidSelection, + MeasureRadius::isPrioritizedSelection + ); + + // load measure callbacks from Part module + auto lengthList = Part::MeasureClient::reportLengthCB(); + for (auto& entry : lengthList) { + MeasureBaseExtendable::addGeometryHandler(entry.m_module, entry.m_callback); + } + auto angleList = Part::MeasureClient::reportAngleCB(); + for (auto& entry : angleList) { + MeasureBaseExtendable::addGeometryHandler(entry.m_module, entry.m_callback); + } + auto areaList = Part::MeasureClient::reportAreaCB(); + for (auto& entry : areaList) { + MeasureBaseExtendable::addGeometryHandler(entry.m_module, entry.m_callback); + } + auto distanceList = Part::MeasureClient::reportDistanceCB(); + for (auto& entry : distanceList) { + MeasureBaseExtendable::addGeometryHandler(entry.m_module, entry.m_callback); + } + auto positionList = Part::MeasureClient::reportPositionCB(); + for (auto& entry : positionList) { + MeasureBaseExtendable::addGeometryHandler(entry.m_module, entry.m_callback); + } + auto radiusList = Part::MeasureClient::reportRadiusCB(); + for (auto& entry : radiusList) { + MeasureBaseExtendable::addGeometryHandler(entry.m_module, entry.m_callback); + } + + + Base::Console().Log("Loading Measure module... done\n"); PyMOD_Return(mod); } // debug print for sketchsolv -void debugprint(const std::string& s) +void debugprint(const std::string& text) { - Base::Console().Log("%s", s.c_str()); + Base::Console().Log("%s", text.c_str()); } diff --git a/src/Mod/Measure/App/CMakeLists.txt b/src/Mod/Measure/App/CMakeLists.txt index 2c22cad1b4..0ccf425de3 100644 --- a/src/Mod/Measure/App/CMakeLists.txt +++ b/src/Mod/Measure/App/CMakeLists.txt @@ -17,19 +17,43 @@ set(Measure_LIBS ) generate_from_xml(MeasurementPy) +generate_from_xml(MeasureBasePy) SET(Python_SRCS MeasurementPy.xml MeasurementPyImp.cpp + MeasureBasePy.xml + MeasureBasePyImp.cpp ) SOURCE_GROUP("Python" FILES ${Python_SRCS}) SET(MeasureModule_SRCS - AppMeasure.cpp - Measurement.cpp - Measurement.h PreCompiled.cpp PreCompiled.h + AppMeasure.cpp + +# original service routines + Measurement.cpp + Measurement.h + +# umf + MeasureBase.cpp + MeasureBase.h + MeasureAngle.cpp + MeasureAngle.h + MeasureDistance.cpp + MeasureDistance.h + MeasurePosition.cpp + MeasurePosition.h + MeasureLength.cpp + MeasureLength.h + MeasureArea.cpp + MeasureArea.h + MeasureRadius.cpp + MeasureRadius.h + + Preferences.cpp + Preferences.h ) SOURCE_GROUP("Module" FILES ${MeasureModule_SRCS}) @@ -45,18 +69,9 @@ if(FREECAD_USE_PCH) ADD_MSVC_PRECOMPILED_HEADER(Measure PreCompiled.h PreCompiled.cpp Measure_CPP_SRCS) endif(FREECAD_USE_PCH) -SET(Measure_Scripts - ../Init.py -) - -add_library(Measure SHARED ${Measure_SRCS} ${Measure_Scripts}) +add_library(Measure SHARED ${Measure_SRCS}) target_link_libraries(Measure ${Measure_LIBS}) -fc_target_copy_resource_flat(Measure - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_BINARY_DIR}/Mod/Measure - ${Measure_Scripts}) - #if(MSVC) # set_target_properties(Measure PROPERTIES SUFFIX ".pyd") # set_target_properties(Measure PROPERTIES DEBUG_OUTPUT_NAME "Measure_d") diff --git a/src/Mod/Measure/App/MeasureAngle.cpp b/src/Mod/Measure/App/MeasureAngle.cpp new file mode 100644 index 0000000000..87b8fbd419 --- /dev/null +++ b/src/Mod/Measure/App/MeasureAngle.cpp @@ -0,0 +1,264 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#include "PreCompiled.h" + +#include +#include +#include +#include +#include +#include + +#include "MeasureAngle.h" + +using namespace Measure; + +PROPERTY_SOURCE(Measure::MeasureAngle, Measure::MeasureBase) + + +MeasureAngle::MeasureAngle() +{ + ADD_PROPERTY_TYPE(Element1,(nullptr), "Measurement", App::Prop_None, "First element of the measurement"); + Element1.setScope(App::LinkScope::Global); + Element1.setAllowExternal(true); + + ADD_PROPERTY_TYPE(Element2,(nullptr), "Measurement", App::Prop_None, "Second element of the measurement"); + Element2.setScope(App::LinkScope::Global); + Element2.setAllowExternal(true); + + ADD_PROPERTY_TYPE(Angle,(0.0) ,"Measurement",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Angle between the two elements"); + Angle.setUnit(Base::Unit::Angle); +} + +MeasureAngle::~MeasureAngle() = default; + + +bool MeasureAngle::isValidSelection(const App::MeasureSelection& selection) +{ + if (selection.size() != 2) { + return false; + } + + for (auto element : selection) { + auto objT = element.object; + + App::DocumentObject* ob = objT.getObject(); + const std::string& subName = objT.getSubName(); + const char* className = objT.getSubObject()->getTypeId().getName(); + std::string mod = Base::Type::getModuleName(className); + + if (!hasGeometryHandler(mod)) { + return false; + } + + App::MeasureHandler handler = App::MeasureManager::getMeasureHandler(mod.c_str()); + App::MeasureElementType type = handler.typeCb(ob, subName.c_str()); + + if (type == App::MeasureElementType::INVALID) { + return false; + } + + if (!(type == App::MeasureElementType::LINE || + type == App::MeasureElementType::PLANE || + type == App::MeasureElementType::LINESEGMENT)) { + return false; + } + } + return true; +} + +bool MeasureAngle::isPrioritizedSelection(const App::MeasureSelection& selection) +{ + if (selection.size() != 2) { + return false; + } + + // Check if the two elements are parallel + auto element1 = selection.at(0); + auto objT1 = element1.object; + App::DocumentObject* ob1 = objT1.getObject(); + std::string sub1 = objT1.getSubName(); + Base::Vector3d vec1; + getVec(*ob1, sub1, vec1); + + auto element2 = selection.at(1); + auto objT2 = element2.object; + App::DocumentObject* ob2 = objT2.getObject(); + std::string sub2 = objT2.getSubName(); + Base::Vector3d vec2; + getVec(*ob2, sub2, vec2); + + + double angle = std::fmod(vec1.GetAngle(vec2), D_PI); + return angle > Base::Precision::Angular(); +} + + +void MeasureAngle::parseSelection(const App::MeasureSelection& selection) { + + assert(selection.size() >= 2); + + auto element1 = selection.at(0); + auto objT1 = element1.object; + App::DocumentObject* ob1 = objT1.getObject(); + const std::vector elems1 = {objT1.getSubName()}; + Element1.setValue(ob1, elems1); + + auto element2 = selection.at(1); + auto objT2 = element2.object; + App::DocumentObject* ob2 = objT2.getObject(); + const std::vector elems2 = {objT2.getSubName()}; + Element2.setValue(ob2, elems2); +} + + +bool MeasureAngle::getVec(App::DocumentObject& ob, std::string& subName, Base::Vector3d& vecOut) { + const char* className = ob.getSubObject(subName.c_str())->getTypeId().getName(); + std::string mod = Base::Type::getModuleName(className); + + if (!hasGeometryHandler(mod)) { + return false; + } + + auto handler = getGeometryHandler(mod); + App::SubObjectT subject{&ob, subName.c_str()}; + auto info = handler(subject); + + auto angleInfo = std::dynamic_pointer_cast(info); + vecOut = angleInfo->orientation; + return true; +} + +Base::Vector3d MeasureAngle::getLoc(App::DocumentObject& ob, std::string& subName) +{ + const char* className = ob.getSubObject(subName.c_str())->getTypeId().getName(); + std::string mod = Base::Type::getModuleName(className); + + if (!hasGeometryHandler(mod)) { + return Base::Vector3d(); + } + + auto handler = getGeometryHandler(mod); + App::SubObjectT subject{&ob, subName.c_str()}; + auto info = handler(subject); + auto angleInfo = std::dynamic_pointer_cast(info); + return angleInfo->position; +} + +gp_Vec MeasureAngle::vector1() { + + App::DocumentObject* ob = Element1.getValue(); + std::vector subs = Element1.getSubValues(); + + if (!ob || !ob->isValid() || subs.empty() ) { + return {}; + } + + Base::Vector3d vec; + getVec(*ob, subs.at(0), vec); + return gp_Vec(vec.x, vec.y, vec.z); +} + +gp_Vec MeasureAngle::vector2() { + App::DocumentObject* ob = Element2.getValue(); + std::vector subs = Element2.getSubValues(); + + if (!ob || !ob->isValid() || subs.empty() ) { + return gp_Vec(); + } + + Base::Vector3d vec; + getVec(*ob, subs.at(0), vec); + return gp_Vec(vec.x, vec.y, vec.z); +} + +gp_Vec MeasureAngle::location1() { + + App::DocumentObject* ob = Element1.getValue(); + std::vector subs = Element1.getSubValues(); + + if (!ob || !ob->isValid() || subs.empty() ) { + return {}; + } + auto temp = getLoc(*ob, subs.at(0)); + return {temp.x, temp.y, temp.z}; +} +gp_Vec MeasureAngle::location2() { + App::DocumentObject* ob = Element2.getValue(); + std::vector subs = Element2.getSubValues(); + + if (!ob || !ob->isValid() || subs.empty() ) { + return {}; + } + + auto temp = getLoc(*ob, subs.at(0)); + return {temp.x, temp.y, temp.z}; +} + +App::DocumentObjectExecReturn *MeasureAngle::execute() +{ + App::DocumentObject* ob1 = Element1.getValue(); + std::vector subs1 = Element1.getSubValues(); + + App::DocumentObject* ob2 = Element2.getValue(); + std::vector subs2 = Element2.getSubValues(); + + if (!ob1 || !ob1->isValid() || !ob2 || !ob2->isValid()) { + return new App::DocumentObjectExecReturn("Submitted object(s) is not valid"); + } + + if (subs1.empty() || subs2.empty()) { + return new App::DocumentObjectExecReturn("No geometry element picked"); + } + + Base::Vector3d vec1; + getVec(*ob1, subs1.at(0), vec1); + + Base::Vector3d vec2; + getVec(*ob2, subs2.at(0), vec2); + + Angle.setValue(Base::toDegrees(vec1.GetAngle(vec2))); + + return DocumentObject::StdReturn; +} + +void MeasureAngle::onChanged(const App::Property* prop) +{ + + if (prop == &Element1 || prop == &Element2) { + if (!isRestoring()) { + App::DocumentObjectExecReturn *ret = recompute(); + delete ret; + } + } + DocumentObject::onChanged(prop); +} + +//! Return the object we are measuring +//! used by the viewprovider in determining visibility +std::vector MeasureAngle::getSubject() const +{ + return {Element1.getValue()}; +} + diff --git a/src/Mod/Measure/App/MeasureAngle.h b/src/Mod/Measure/App/MeasureAngle.h new file mode 100644 index 0000000000..adbb47f62b --- /dev/null +++ b/src/Mod/Measure/App/MeasureAngle.h @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#ifndef APP_MEASUREANGLE_H +#define APP_MEASUREANGLE_H + +#include + +#include + +#include +#include +#include +#include + +#include + +#include "MeasureBase.h" + + +namespace Measure +{ + + +class MeasureExport MeasureAngle : public Measure::MeasureBaseExtendable +{ + PROPERTY_HEADER_WITH_OVERRIDE(Measure::MeasureAngle); + +public: + /// Constructor + MeasureAngle(); + ~MeasureAngle() override; + + App::PropertyLinkSub Element1; + App::PropertyLinkSub Element2; + App::PropertyAngle Angle; + + App::DocumentObjectExecReturn *execute() override; + + const char* getViewProviderName() const override { + return "MeasureGui::ViewProviderMeasureAngle"; + } + + static bool isValidSelection(const App::MeasureSelection& selection); + static bool isPrioritizedSelection(const App::MeasureSelection& selection); + void parseSelection(const App::MeasureSelection& selection) override; + + std::vector getInputProps() override {return {"Element1", "Element2"};} + App::Property* getResultProp() override {return &this->Angle;} + + // Return the object we are measuring + std::vector getSubject() const override; + + + static bool getVec(App::DocumentObject& ob, std::string& subName, Base::Vector3d& vecOut); + Base::Vector3d getLoc(App::DocumentObject& ob, std::string& subName); + + // Orientation Vectors + gp_Vec vector1(); + gp_Vec vector2(); + + // Location Vectors + gp_Vec location1(); + gp_Vec location2(); + +private: + + void onChanged(const App::Property* prop) override; +}; + +} //namespace Measure + + +#endif // APP_MEASUREANGLE_H diff --git a/src/Mod/Measure/App/MeasureArea.cpp b/src/Mod/Measure/App/MeasureArea.cpp new file mode 100644 index 0000000000..553a3e6acf --- /dev/null +++ b/src/Mod/Measure/App/MeasureArea.cpp @@ -0,0 +1,181 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#include "PreCompiled.h" + +#include +#include +#include + +#include "MeasureArea.h" + + +using namespace Measure; + +PROPERTY_SOURCE(Measure::MeasureArea, Measure::MeasureBase) + + + +MeasureArea::MeasureArea() +{ + ADD_PROPERTY_TYPE(Elements,(nullptr), "Measurement", App::Prop_None, "Element to get the area from"); + Elements.setScope(App::LinkScope::Global); + Elements.setAllowExternal(true); + + ADD_PROPERTY_TYPE(Area,(0.0), "Measurement", App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Area of element"); + +} + +MeasureArea::~MeasureArea() = default; + + +bool MeasureArea::isValidSelection(const App::MeasureSelection& selection){ + + if (selection.empty()) { + return false; + } + + for (auto element : selection) { + auto objT = element.object; + + App::DocumentObject* ob = objT.getObject(); + const std::string& subName = objT.getSubName(); + const char* className = objT.getSubObject()->getTypeId().getName(); + std::string mod = Base::Type::getModuleName(className); + + if (!hasGeometryHandler(mod)) { + return false; + } + + App::MeasureHandler handler = App::MeasureManager::getMeasureHandler(mod.c_str()); + App::MeasureElementType type = handler.typeCb(ob, subName.c_str()); + + if (type == App::MeasureElementType::INVALID) { + return false; + } + + // TODO: Also support Cylinder & Volume? + if ((type != App::MeasureElementType::PLANE && type != App::MeasureElementType::CYLINDER)) { + return false; + } + } + return true; +} + +void MeasureArea::parseSelection(const App::MeasureSelection& selection) { + // Set properties from selection, method is only invoked when isValid Selection returns true + + std::vector objects; + std::vector subElements; + + for (auto element : selection) { + auto objT = element.object; + + objects.push_back(objT.getObject()); + subElements.push_back(objT.getSubName()); + } + + Elements.setValues(objects, subElements); +} + + +App::DocumentObjectExecReturn *MeasureArea::execute() +{ + recalculateArea(); + return DocumentObject::StdReturn; +} + +void MeasureArea::recalculateArea() +{ + const std::vector& objects = Elements.getValues(); + const std::vector& subElements = Elements.getSubValues(); + + double result(0.0); + + // Loop through Elements and call the valid geometry handler + for (std::vector::size_type i=0; igetSubObject(subElement.c_str())->getTypeId().getName(); + const std::string& mod = Base::Type::getModuleName(className); + auto handler = getGeometryHandler(mod); + if (!handler) { + throw Base::RuntimeError("No geometry handler available for submitted element type"); + } + + App::SubObjectT subject{object, subElement.c_str()}; + auto info = handler(subject); + auto areaInfo = std::dynamic_pointer_cast(info); + result += areaInfo->area; + } + + Area.setValue(result); +} + +void MeasureArea::onChanged(const App::Property* prop) +{ + if (isRestoring() || isRemoving()) { + return; + } + + if (prop == &Elements) { + recalculateArea(); + } + + MeasureBase::onChanged(prop); +} + + +Base::Placement MeasureArea::getPlacement() { + const std::vector& objects = Elements.getValues(); + const std::vector& subElements = Elements.getSubValues(); + + if (objects.empty() || subElements.empty()) { + return Base::Placement(); + } + + App::DocumentObject* object = objects.front(); + std::string subElement = subElements.front(); + const char* className = object->getSubObject(subElement.c_str())->getTypeId().getName(); + const std::string& mod = Base::Type::getModuleName(className); + + auto handler = getGeometryHandler(mod); + if (!handler) { + throw Base::RuntimeError("No geometry handler available for submitted element type"); + } + + App::SubObjectT subject{object, subElement.c_str()}; + auto info = handler(subject); + auto areaInfo = std::dynamic_pointer_cast(info); + return areaInfo->placement; +} + + +//! Return the object we are measuring +//! used by the viewprovider in determining visibility +std::vector MeasureArea::getSubject() const +{ + return Elements.getValues(); +} diff --git a/src/Mod/Measure/App/MeasureArea.h b/src/Mod/Measure/App/MeasureArea.h new file mode 100644 index 0000000000..0fef35e382 --- /dev/null +++ b/src/Mod/Measure/App/MeasureArea.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#ifndef MEASURE_MEASUREAREA_H +#define MEASURE_MEASUREAREA_H + +#include + +#include + +#include +#include +#include + +#include + +#include "MeasureBase.h" + + +namespace Measure +{ + + + + +class MeasureExport MeasureArea : public Measure::MeasureBaseExtendable +{ + PROPERTY_HEADER_WITH_OVERRIDE(Measure::MeasureArea); + +public: + /// Constructor + MeasureArea(); + ~MeasureArea() override; + + App::PropertyLinkSubList Elements; + App::PropertyArea Area; + + App::DocumentObjectExecReturn *execute() override; + void recalculateArea(); + + const char* getViewProviderName() const override { + return "MeasureGui::ViewProviderMeasure"; + } + + static bool isValidSelection(const App::MeasureSelection& selection); + void parseSelection(const App::MeasureSelection& selection) override; + + std::vector getInputProps() override {return {"Elements"};} + App::Property* getResultProp() override {return &this->Area;} + + // Return a placement for the viewprovider, just use the first element for now + Base::Placement getPlacement() override; + + // Return the object we are measuring + std::vector getSubject() const override; + + +private: + void onChanged(const App::Property* prop) override; + +}; + +} //namespace Measure + + +#endif // MEASURE_MEASUREAREA_H + diff --git a/src/Mod/Measure/App/MeasureBase.cpp b/src/Mod/Measure/App/MeasureBase.cpp new file mode 100644 index 0000000000..07883d42bb --- /dev/null +++ b/src/Mod/Measure/App/MeasureBase.cpp @@ -0,0 +1,211 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" + +#include +#include +#include +#include + +#include "MeasureBase.h" +// Generated from MeasureBasePy.xml +#include "MeasureBasePy.h" + +using namespace Measure; + + +PROPERTY_SOURCE(Measure::MeasureBase, App::DocumentObject) + +MeasureBase::MeasureBase() { + ADD_PROPERTY_TYPE(Placement, (Base::Placement()), nullptr, App::PropertyType(App::Prop_ReadOnly|App::Prop_Output|App::Prop_NoRecompute), "Visual placement of the measurement"); + +} + + +PyObject *MeasureBase::getPyObject(void) +{ + if (PythonObject.is(Py::_None())) { + // ref counter is set to 1 + PythonObject = Py::Object(new MeasureBasePy(this), true); + } + return Py::new_reference_to(PythonObject); +} + +Py::Object MeasureBase::getProxyObject() const { + Base::PyGILStateLocker lock; + App::Property* prop = this->getPropertyByName("Proxy"); + if (!prop) { + return Py::None(); + } + return dynamic_cast(prop)->getValue(); +}; + +std::vector MeasureBase::getSubject() const { + Base::PyGILStateLocker lock; + + Py::Object proxy = getProxyObject(); + + // Pass the feture object to the proxy + Py::Tuple args(1); + args.setItem(0, Py::Object(const_cast(this)->getPyObject())); + + Py::Object ret; + try { + ret = proxy.callMemberFunction("getSubject", args); + } catch (Py::Exception&) { + Base::PyException e; + e.ReportException(); + return {}; + } + + Py::Sequence retTuple(ret); + std::vector retVec; + for (Py::Object o : retTuple) { + retVec.push_back(static_cast(o.ptr())->getDocumentObjectPtr()); + } + + return retVec; + }; + + + + +void MeasureBase::parseSelection(const App::MeasureSelection& selection) { + Base::PyGILStateLocker lock; + + Py::Object proxy = getProxyObject(); + + // Convert selection to python list + Py::Tuple selectionPy = App::MeasureManager::getSelectionPy(selection); + + Py::Tuple args(2); + + // Pass the feture object to the proxy + args.setItem(0, Py::Object(const_cast(this)->getPyObject())); + args.setItem(1, selectionPy); + + // Call the parseSelection method of the proxy object + try { + proxy.callMemberFunction("parseSelection", args); + } catch (Py::Exception&) { + Base::PyException e; + e.ReportException(); + } + +} + + +std::vector MeasureBase::getInputProps() { + Base::PyGILStateLocker lock; + Py::Object proxy = getProxyObject(); + + if (proxy.isNone()) { + return {}; + } + + Py::Object ret; + try { + ret = proxy.callMemberFunction("getInputProps"); + } catch (Py::Exception&) { + Base::PyException e; + e.ReportException(); + return {}; + } + Py::Sequence propsPy(ret); + + // Get cpp vector from propsPy + std::vector props; + for (Py::Object o : propsPy) { + props.push_back(o.as_string()); + } + + return props; +} + + + +QString MeasureBase::getResultString() { + Py::Object proxy = getProxyObject(); + Base::PyGILStateLocker lock; + + if (!proxy.isNone()) { + + // Pass the feture object to the proxy + Py::Tuple args(1); + args.setItem(0, Py::Object(const_cast(this)->getPyObject())); + + Py::Object ret; + try { + ret = proxy.callMemberFunction("getResultString", args); + } catch (Py::Exception&) { + Base::PyException e; + e.ReportException(); + return QString(); + } + return QString::fromStdString(ret.as_string()); + } + + App::Property* prop = getResultProp(); + if (prop == nullptr) { + return QString(); + } + + if (prop->isDerivedFrom(App::PropertyQuantity::getClassTypeId())) { + return static_cast(prop)->getQuantityValue().getUserString(); + } + + + return QString(); +} + +void MeasureBase::onDocumentRestored() { + // Force recompute the measurement + recompute(); +} + +Base::Placement MeasureBase::getPlacement() { + return this->Placement.getValue(); +} + + +// Python Drawing feature --------------------------------------------------------- + +namespace App { +/// @cond DOXERR +PROPERTY_SOURCE_TEMPLATE(Measure::MeasurePython, Measure::MeasureBase) +template<> const char* Measure::MeasurePython::getViewProviderName(void) const { + return "MeasureGui::ViewProviderMeasure"; +} +template<> PyObject* Measure::MeasurePython::getPyObject() { + if (PythonObject.is(Py::_None())) { + // ref counter is set to 1 + PythonObject = Py::Object(new FeaturePythonPyT(this),true); + } + return Py::new_reference_to(PythonObject); +} +/// @endcond + +// explicit template instantiation +template class MeasureExport FeaturePythonT; +} + + diff --git a/src/Mod/Measure/App/MeasureBase.h b/src/Mod/Measure/App/MeasureBase.h new file mode 100644 index 0000000000..fe256dc940 --- /dev/null +++ b/src/Mod/Measure/App/MeasureBase.h @@ -0,0 +1,131 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#ifndef MEASURE_MEASUREBASE_H +#define MEASURE_MEASUREBASE_H + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // needed? + + +namespace Measure +{ + +class MeasureExport MeasureBase : public App::DocumentObject +{ + PROPERTY_HEADER_WITH_OVERRIDE(Measure::MeasureBase); + +public: + MeasureBase(); + ~MeasureBase() override = default; + + App::PropertyPlacement Placement; + + // boost::signals2::signal signalGuiInit; + + //return PyObject as MeasureBasePy + PyObject *getPyObject() override; + + // Initialize measurement properties from selection + virtual void parseSelection(const App::MeasureSelection& selection); + + + virtual QString getResultString(); + + virtual std::vector getInputProps(); + virtual App::Property* getResultProp() {return {};}; + virtual Base::Placement getPlacement(); + + // Return the objects that are measured + virtual std::vector getSubject() const; + +private: + Py::Object getProxyObject() const; + +protected: + void onDocumentRestored() override; +}; + +// Create a scriptable object based on MeasureBase +using MeasurePython = App::FeaturePythonT; + +template +class MeasureExport MeasureBaseExtendable : public MeasureBase +{ + + using GeometryHandler = std::function; + using HandlerMap = std::map; + + +public: + + static void addGeometryHandler(const std::string& module, GeometryHandler callback) { + _mGeometryHandlers[module] = callback; + } + + static GeometryHandler getGeometryHandler(const std::string& module) { + + if (!hasGeometryHandler(module)) { + return {}; + } + + return _mGeometryHandlers[module]; + } + + static void addGeometryHandlers(const std::vector& modules, GeometryHandler callback){ + // TODO: this will replace a callback with a later one. Should we check that there isn't already a + // handler defined for this module? + for (auto& mod : modules) { + _mGeometryHandlers[mod] = callback; + } + } + + + static bool hasGeometryHandler(const std::string& module) { + return (_mGeometryHandlers.count(module) > 0); + } + +private: + inline static HandlerMap _mGeometryHandlers = MeasureBaseExtendable::HandlerMap(); +}; + + + +} //namespace Measure + + +#endif // MEASURE_MEASUREBASE_H diff --git a/src/Mod/Measure/App/MeasureBasePy.xml b/src/Mod/Measure/App/MeasureBasePy.xml new file mode 100644 index 0000000000..c56bf805d1 --- /dev/null +++ b/src/Mod/Measure/App/MeasureBasePy.xml @@ -0,0 +1,20 @@ + + + + + + User documentation here + + Developer documentation here + + + diff --git a/src/Mod/Measure/App/MeasureBasePyImp.cpp b/src/Mod/Measure/App/MeasureBasePyImp.cpp new file mode 100644 index 0000000000..ca9af1e6eb --- /dev/null +++ b/src/Mod/Measure/App/MeasureBasePyImp.cpp @@ -0,0 +1,40 @@ +#include "PreCompiled.h" + +#include "MeasureBase.h" + +// Inclusion of the generated files (generated out of MeasureBasePy.xml) +#include "MeasureBasePy.h" +#include "MeasureBasePy.cpp" + + +#include + + +// returns a string which represents the object e.g. when printed in python +std::string MeasureBasePy::representation() const +{ + return ""; +} + +PyObject *MeasureBasePy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper +{ + // create a new instance of MeasureBasePy and the Twin object + return new MeasureBasePy(new MeasureBase); +} + + +// constructor method +int MeasureBasePy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) +{ + return 0; +} + +PyObject *MeasureBasePy::getCustomAttributes(const char* /*attr*/) const +{ + return nullptr; +} + +int MeasureBasePy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +} diff --git a/src/Mod/Measure/App/MeasureDistance.cpp b/src/Mod/Measure/App/MeasureDistance.cpp new file mode 100644 index 0000000000..9a04b77bcd --- /dev/null +++ b/src/Mod/Measure/App/MeasureDistance.cpp @@ -0,0 +1,231 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#include "PreCompiled.h" + +#include +#include +#include +#include +#include +#include + +#include "MeasureDistance.h" + + +using namespace Measure; + +PROPERTY_SOURCE(Measure::MeasureDistance, Measure::MeasureBase) + + +MeasureDistance::MeasureDistance() +{ + ADD_PROPERTY_TYPE(Element1,(nullptr), "Measurement", App::Prop_None, "First element of the measurement"); + Element1.setScope(App::LinkScope::Global); + Element1.setAllowExternal(true); + + ADD_PROPERTY_TYPE(Element2,(nullptr), "Measurement", App::Prop_None, "Second element of the measurement"); + Element2.setScope(App::LinkScope::Global); + Element2.setAllowExternal(true); + + ADD_PROPERTY_TYPE(Distance,(0.0) ,"Measurement",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Distance between the two elements"); + Distance.setUnit(Base::Unit::Length); + + ADD_PROPERTY_TYPE(Position1,(Base::Vector3d(0.0,0.0,0.0)),"Measurement", App::Prop_Hidden, "Position1"); + ADD_PROPERTY_TYPE(Position2,(Base::Vector3d(0.0,1.0,0.0)),"Measurement", App::Prop_Hidden, "Position2"); + +} + +MeasureDistance::~MeasureDistance() = default; + + +bool MeasureDistance::isValidSelection(const App::MeasureSelection& selection){ + + if (selection.size() != 2) { + return false; + } + + for (auto element : selection) { + + auto objT = element.object; + + App::DocumentObject* ob = objT.getObject(); + const std::string& subName = objT.getSubName(); + const char* className = objT.getSubObject()->getTypeId().getName(); + std::string mod = Base::Type::getModuleName(className); + + if (!hasGeometryHandler(mod)) { + return false; + } + + App::MeasureHandler handler = App::MeasureManager::getMeasureHandler(mod.c_str()); + App::MeasureElementType type = handler.typeCb(ob, subName.c_str()); + + if (type == App::MeasureElementType::INVALID) { + return false; + } + + if ( + type != App::MeasureElementType::POINT && + type != App::MeasureElementType::LINE && + type != App::MeasureElementType::LINESEGMENT && + type != App::MeasureElementType::CIRCLE && + type != App::MeasureElementType::ARC && + type != App::MeasureElementType::CURVE && + type != App::MeasureElementType::PLANE && + type != App::MeasureElementType::CYLINDER + ) { + return false; + } + } + return true; +} + +bool MeasureDistance::isPrioritizedSelection(const App::MeasureSelection& selection) { + + (void)selection; + + // TODO: Check if Elements ar parallel + // if (selection.size() == 2) { + // return true; + // } + + return false; +} + + +void MeasureDistance::parseSelection(const App::MeasureSelection& selection) { + + assert(selection.size() >= 2); + + auto element1 = selection.at(0); + auto objT1 = element1.object; + App::DocumentObject* ob1 = objT1.getObject(); + const std::vector elems1 = {objT1.getSubName()}; + Element1.setValue(ob1, elems1); + + auto element2 = selection.at(1); + auto objT2 = element2.object; + App::DocumentObject* ob2 = objT2.getObject(); + const std::vector elems2 = {objT2.getSubName()}; + Element2.setValue(ob2, elems2); +} + + +bool MeasureDistance::getShape(App::PropertyLinkSub* prop, TopoDS_Shape& rShape) { + + App::DocumentObject* ob = prop->getValue(); + std::vector subs = prop->getSubValues(); + + if (!ob || !ob->isValid() || subs.empty() ) { + return false; + } + + std::string subName = subs.at(0); + const char* className = ob->getSubObject(subName.c_str())->getTypeId().getName(); + std::string mod = Base::Type::getModuleName(className); + + if (!hasGeometryHandler(mod)) { + return false; + } + + auto handler = getGeometryHandler(mod); + std::string obName = static_cast(ob->getNameInDocument()); + App::SubObjectT subject{ob, subName.c_str()}; + auto info = handler(subject); + if (!info->valid) { + return false; + } + auto distanceInfo = std::dynamic_pointer_cast(info); + + rShape = *distanceInfo->getShape(); + return true; +} + + +App::DocumentObjectExecReturn *MeasureDistance::execute() +{ + + App::DocumentObject* ob1 = Element1.getValue(); + std::vector subs1 = Element1.getSubValues(); + + App::DocumentObject* ob2 = Element2.getValue(); + std::vector subs2 = Element2.getSubValues(); + + if (!ob1 || !ob1->isValid() || !ob2 || !ob2->isValid()) { + return new App::DocumentObjectExecReturn("Submitted object(s) is not valid"); + } + + if (subs1.empty() || subs2.empty()) { + return new App::DocumentObjectExecReturn("No geometry element picked"); + } + + // Get both shapes from geometry handler + TopoDS_Shape shape1; + if (!this->getShape(&Element1, shape1)) { + return new App::DocumentObjectExecReturn("Could not get shape"); + } + + TopoDS_Shape shape2; + if (!this->getShape(&Element2, shape2)) { + return new App::DocumentObjectExecReturn("Could not get shape"); + } + + // Calculate the extrema + BRepExtrema_DistShapeShape measure(shape1, shape2); + if (!measure.IsDone() || measure.NbSolution() < 1) { + return new App::DocumentObjectExecReturn("Could not get extrema"); + } + + Distance.setValue(measure.Value()); + + gp_Pnt p1 = measure.PointOnShape1(1); + Position1.setValue(p1.X(), p1.Y(), p1.Z()); + + gp_Pnt p2 = measure.PointOnShape2(1); + Position2.setValue(p2.X(), p2.Y(), p2.Z()); + + + return DocumentObject::StdReturn; +} + +void MeasureDistance::onChanged(const App::Property* prop) +{ + + if (prop == &Element1 || prop == &Element2) { + if (!isRestoring()) { + App::DocumentObjectExecReturn *ret = recompute(); + delete ret; + } + } + DocumentObject::onChanged(prop); +} + + +//! Return the object we are measuring +//! used by the viewprovider in determining visibility +std::vector MeasureDistance::getSubject() const +{ + return {Element1.getValue()}; +} + diff --git a/src/Mod/Measure/App/MeasureDistance.h b/src/Mod/Measure/App/MeasureDistance.h new file mode 100644 index 0000000000..ad6f8984ff --- /dev/null +++ b/src/Mod/Measure/App/MeasureDistance.h @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#ifndef MEASUREAPP_MEASUREDISTANCE_H +#define MEASUREAPP_MEASUREDISTANCE_H + +#include + +#include + +#include +#include +#include + +#include + +#include "MeasureBase.h" + +namespace Measure +{ + + + +class MeasureExport MeasureDistance : public Measure::MeasureBaseExtendable +{ + PROPERTY_HEADER_WITH_OVERRIDE(Measure::MeasureDistance); + +public: + /// Constructor + MeasureDistance(); + ~MeasureDistance() override; + + App::PropertyLinkSub Element1; + App::PropertyLinkSub Element2; + App::PropertyDistance Distance; + + // Position properties for the viewprovider + App::PropertyVector Position1; + App::PropertyVector Position2; + + App::DocumentObjectExecReturn *execute() override; + + const char* getViewProviderName() const override { + return "MeasureGui::ViewProviderMeasureDistance"; + } + + static bool isValidSelection(const App::MeasureSelection& selection); + static bool isPrioritizedSelection(const App::MeasureSelection& selection); + void parseSelection(const App::MeasureSelection& selection) override; + + std::vector getInputProps() override {return {"Element1", "Element2"};} + App::Property* getResultProp() override {return &this->Distance;} + + bool getShape(App::PropertyLinkSub* prop, TopoDS_Shape& rShape); + + // Return the object we are measuring + std::vector getSubject() const override; + + +private: + + void onChanged(const App::Property* prop) override; +}; + +} //namespace Measure + + +#endif // MEASUREAPP_MEASUREDISTANCE_H diff --git a/src/Mod/Measure/App/MeasureLength.cpp b/src/Mod/Measure/App/MeasureLength.cpp new file mode 100644 index 0000000000..ba95db39a1 --- /dev/null +++ b/src/Mod/Measure/App/MeasureLength.cpp @@ -0,0 +1,184 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#include "PreCompiled.h" + +#include +#include +#include + +#include + +#include "MeasureLength.h" + + +using namespace Measure; + +PROPERTY_SOURCE(Measure::MeasureLength, Measure::MeasureBase) + + + +MeasureLength::MeasureLength() +{ + ADD_PROPERTY_TYPE(Elements,(nullptr), "Measurement", App::Prop_None, "Elements to get the length from"); + Elements.setScope(App::LinkScope::Global); + Elements.setAllowExternal(true); + + ADD_PROPERTY_TYPE(Length,(0.0) ,"Measurement",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Length of selection"); + +} + +MeasureLength::~MeasureLength() = default; + + +bool MeasureLength::isValidSelection(const App::MeasureSelection& selection){ + + if (selection.empty()) { + return false; + } + + for (auto element : selection) { + auto objT = element.object; + + App::DocumentObject* ob = objT.getObject(); + const std::string& subName = objT.getSubName(); + const char* className = objT.getSubObject()->getTypeId().getName(); + std::string mod = Base::Type::getModuleName(className); + + if (!hasGeometryHandler(mod)) { + return false; + } + + App::MeasureHandler handler = App::MeasureManager::getMeasureHandler(mod.c_str()); + App::MeasureElementType type = handler.typeCb(ob, subName.c_str()); + + if (type == App::MeasureElementType::INVALID) { + return false; + } + + if ((type != App::MeasureElementType::LINESEGMENT && type != App::MeasureElementType::CIRCLE + && type != App::MeasureElementType::ARC && type != App::MeasureElementType::CURVE)) { + return false; + } + } + return true; +} + +void MeasureLength::parseSelection(const App::MeasureSelection& selection) { + // Set properties from selection, method is only invoked when isValid Selection returns true + + std::vector objects; + std::vector subElements; + + for (auto element : selection) { + auto objT = element.object; + + objects.push_back(objT.getObject()); + subElements.push_back(objT.getSubName()); + } + + Elements.setValues(objects, subElements); +} + + +App::DocumentObjectExecReturn *MeasureLength::execute() +{ + recalculateLength(); + return DocumentObject::StdReturn; +} + +void MeasureLength::recalculateLength() +{ + const std::vector& objects = Elements.getValues(); + const std::vector& subElements = Elements.getSubValues(); + + double result(0.0); + + // Loop through Elements and call the valid geometry handler + for (std::vector::size_type i=0; igetSubObject(subElement.c_str())->getTypeId().getName(); + const std::string& mod = Base::Type::getModuleName(className); + auto handler = getGeometryHandler(mod); + if (!handler) { + throw Base::RuntimeError("No geometry handler available for submitted element type"); + } + + App::SubObjectT subject{object, subElement.c_str()}; + auto info = handler(subject); + auto lengthInfo = std::dynamic_pointer_cast(info); + result += lengthInfo->length; + } + + Length.setValue(result); +} + +void MeasureLength::onChanged(const App::Property* prop) +{ + if (isRestoring() || isRemoving()) { + return; + } + + if (prop == &Elements) { + recalculateLength(); + } + + MeasureBase::onChanged(prop); +} + + +Base::Placement MeasureLength::getPlacement() { + const std::vector& objects = Elements.getValues(); + const std::vector& subElements = Elements.getSubValues(); + + if (!objects.size() || !subElements.size()) { + return Base::Placement(); + } + + App::DocumentObject* object = objects.front(); + std::string subElement = subElements.front(); + const char* className = object->getSubObject(subElement.c_str())->getTypeId().getName(); + const std::string& mod = Base::Type::getModuleName(className); + + auto handler = getGeometryHandler(mod); + if (!handler) { + throw Base::RuntimeError("No geometry handler available for submitted element type"); + } + + App::SubObjectT subject{object, subElement.c_str()}; + auto info = handler(subject); + auto lengthInfo = std::dynamic_pointer_cast(info); + return lengthInfo->placement; +} + + +//! Return the object we are measuring +//! used by the viewprovider in determining visibility +std::vector MeasureLength::getSubject() const +{ + return Elements.getValues(); +} + diff --git a/src/Mod/Measure/App/MeasureLength.h b/src/Mod/Measure/App/MeasureLength.h new file mode 100644 index 0000000000..3942925754 --- /dev/null +++ b/src/Mod/Measure/App/MeasureLength.h @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#ifndef MEASURE_MEASURELENGTH_H +#define MEASURE_MEASURELENGTH_H + +#include + +#include +#include +#include + +#include +#include + +#include "MeasureBase.h" + +namespace Measure +{ + +class MeasureExport MeasureLength : public Measure::MeasureBaseExtendable +{ + PROPERTY_HEADER_WITH_OVERRIDE(Measure::MeasureLength); + +public: + /// Constructor + MeasureLength(); + ~MeasureLength() override; + + App::PropertyLinkSubList Elements; + App::PropertyDistance Length; + + App::DocumentObjectExecReturn *execute() override; + void recalculateLength(); + + const char* getViewProviderName() const override { + return "MeasureGui::ViewProviderMeasure"; + } + + static bool isValidSelection(const App::MeasureSelection& selection); + void parseSelection(const App::MeasureSelection& selection) override; + + std::vector getInputProps() override {return {"Elements"};} + App::Property* getResultProp() override {return &this->Length;} + + // Return a placement for the viewprovider, just use the first element for now + Base::Placement getPlacement(); + + // Return the object we are measuring + std::vector getSubject() const override; + + +private: + void onChanged(const App::Property* prop) override; + +}; + +} //namespace Measure + + +#endif // MEASURE_MEASURELENGTH_H + diff --git a/src/Mod/Measure/App/MeasurePosition.cpp b/src/Mod/Measure/App/MeasurePosition.cpp new file mode 100644 index 0000000000..c4c00d413d --- /dev/null +++ b/src/Mod/Measure/App/MeasurePosition.cpp @@ -0,0 +1,177 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#include "PreCompiled.h" + +#include +#include +#include +#include + +#include "MeasurePosition.h" + + +using namespace Measure; + +PROPERTY_SOURCE(Measure::MeasurePosition, Measure::MeasureBase) + + + +MeasurePosition::MeasurePosition() +{ + ADD_PROPERTY_TYPE(Element,(nullptr), "Measurement", App::Prop_None, "Element to get the position from"); + Element.setScope(App::LinkScope::Global); + Element.setAllowExternal(true); + + + ADD_PROPERTY_TYPE(Position,(0.0, 0.0, 0.0), "Measurement", App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "The absolute position"); + +} + +MeasurePosition::~MeasurePosition() = default; + + +bool MeasurePosition::isValidSelection(const App::MeasureSelection& selection){ + + if (selection.empty() || selection.size() > 1) { + return false; + } + + for (auto element : selection) { + auto objT = element.object; + + App::DocumentObject* ob = objT.getObject(); + const std::string& subName = objT.getSubName(); + const char* className = objT.getSubObject()->getTypeId().getName(); + std::string mod = Base::Type::getModuleName(className); + + if (!hasGeometryHandler(mod)) { + return false; + } + + App::MeasureHandler handler = App::MeasureManager::getMeasureHandler(mod.c_str()); + App::MeasureElementType type = handler.typeCb(ob, subName.c_str()); + + if (type == App::MeasureElementType::INVALID) { + return false; + } + + if (type != App::MeasureElementType::POINT) { + return false; + } + } + return true; +} + +void MeasurePosition::parseSelection(const App::MeasureSelection& selection) { + // Set properties from selection, method is only invoked when isValid Selection returns true + + for (auto element : selection) { + auto objT = element.object; + + std::vector subElements {objT.getSubName()}; + Element.setValue(objT.getObject(), subElements); + break; + } +} + + +App::DocumentObjectExecReturn *MeasurePosition::execute() +{ + recalculatePosition(); + return DocumentObject::StdReturn; +} + +void MeasurePosition::recalculatePosition() +{ + const App::DocumentObject* object = Element.getValue(); + const std::vector& subElements = Element.getSubValues(); + + // Get the position of the first point + std::string subElement = subElements.front(); + + // Get the Geometry handler based on the module + const char* className = object->getSubObject(subElement.c_str())->getTypeId().getName(); + const std::string& mod = Base::Type::getModuleName(className); + auto handler = getGeometryHandler(mod); + if (!handler) { + throw Base::RuntimeError("No geometry handler available for submitted element type"); + } + + App::SubObjectT subject{object, subElement.c_str()}; + auto info = handler(subject); + auto positionInfo = std::dynamic_pointer_cast(info); + Position.setValue(positionInfo->position); +} + +void MeasurePosition::onChanged(const App::Property* prop) +{ + if (isRestoring() || isRemoving()) { + return; + } + + if (prop == &Element) { + recalculatePosition(); + } + DocumentObject::onChanged(prop); +} + + +QString MeasurePosition::getResultString() { + App::Property* prop = this->getResultProp(); + if (prop == nullptr) { + return {}; + } + + Base::Vector3d value = Position.getValue(); + QString unit = Position.getUnit().getString(); + int precision = 2; + QString text; + #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + QTextStream(&text) + << "X: " << QString::number(value.x, 'f', precision) << " " << unit << endl + << "Y: " << QString::number(value.y, 'f', precision) << " " << unit << endl + << "Z: " << QString::number(value.z, 'f', precision) << " " << unit; + #else + QTextStream(&text) + << "X: " << QString::number(value.x, 'f', precision) << " " << unit << Qt::endl + << "Y: " << QString::number(value.y, 'f', precision) << " " << unit << Qt::endl + << "Z: " << QString::number(value.z, 'f', precision) << " " << unit; + #endif + return text; +} + + +Base::Placement MeasurePosition::getPlacement() { + Base::Placement placement; + placement.setPosition(Position.getValue()); + return placement; +} + + +//! Return the object we are measuring +//! used by the viewprovider in determining visibility +std::vector MeasurePosition::getSubject() const +{ + return {Element.getValue()}; +} diff --git a/src/Mod/Measure/App/MeasurePosition.h b/src/Mod/Measure/App/MeasurePosition.h new file mode 100644 index 0000000000..054ff37021 --- /dev/null +++ b/src/Mod/Measure/App/MeasurePosition.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#ifndef APP_MEASUREPOSITION_H +#define APP_MEASUREPOSITION_H + +#include + +#include + +#include +#include +#include + +#include + +#include "MeasureBase.h" + + +namespace Measure +{ + + +class MeasureExport MeasurePosition : public Measure::MeasureBaseExtendable +{ + PROPERTY_HEADER_WITH_OVERRIDE(Measure::MeasurePosition); + +public: + /// Constructor + MeasurePosition(); + ~MeasurePosition() override; + + App::PropertyLinkSub Element; + App::PropertyPosition Position; + + App::DocumentObjectExecReturn *execute() override; + void recalculatePosition(); + + const char* getViewProviderName() const override { + return "MeasureGui::ViewProviderMeasure"; + } + + static bool isValidSelection(const App::MeasureSelection& selection); + void parseSelection(const App::MeasureSelection& selection) override; + + std::vector getInputProps() override {return {"Element"};} + App::Property* getResultProp() override {return &this->Position;} + QString getResultString() override; + + Base::Placement getPlacement() override; + + // Return the object we are measuring + std::vector getSubject() const override; + +private: + + void onChanged(const App::Property* prop) override; +}; + +} //namespace Measure + + +#endif // APP_MEASUREPOSITION_H diff --git a/src/Mod/Measure/App/MeasureRadius.cpp b/src/Mod/Measure/App/MeasureRadius.cpp new file mode 100644 index 0000000000..c573aff30e --- /dev/null +++ b/src/Mod/Measure/App/MeasureRadius.cpp @@ -0,0 +1,211 @@ +/*************************************************************************** + * Copyright (c) 2023 WandererFan * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "MeasureRadius.h" + + +using namespace Measure; + +PROPERTY_SOURCE(Measure::MeasureRadius, Measure::MeasureBase) + + + +MeasureRadius::MeasureRadius() +{ + ADD_PROPERTY_TYPE(Element,(nullptr), "Measurement", App::Prop_None, "Element to get the radius from"); + Element.setScope(App::LinkScope::Global); + Element.setAllowExternal(true); + + ADD_PROPERTY_TYPE(Radius,(0.0) ,"Measurement",App::PropertyType(App::Prop_ReadOnly|App::Prop_Output), + "Radius of selection"); + +} + +MeasureRadius::~MeasureRadius() = default; + +//! validate all the object+subelement pairs in the selection. Must be circle or arc +//! and have a geometry handler available. We only calculate radius if there is a +//! single valid item in the selection +bool MeasureRadius::isValidSelection(const App::MeasureSelection& selection){ + + if (selection.empty() || selection.size() > 1) { + // too few or too many selections + return false; + } + + auto element = selection.front(); + auto objT = element.object; + + App::DocumentObject* ob = objT.getObject(); + const std::string& subName = objT.getSubName(); + const char* className = objT.getSubObject()->getTypeId().getName(); + std::string mod = Base::Type::getModuleName(className); + + if (!hasGeometryHandler(mod)) { + return false; + } + + App::MeasureHandler handler = App::MeasureManager::getMeasureHandler(mod.c_str()); + App::MeasureElementType type = handler.typeCb(ob, subName.c_str()); + + if (type == App::MeasureElementType::INVALID) { + return false; + } + + if (type != App::MeasureElementType::CIRCLE + && type != App::MeasureElementType::ARC) { + return false; + } + + return true; +} + +//! return true if the selection is particularly interesting to MeasureRadius. +//! In this case we claim circles and arcs. +bool MeasureRadius::isPrioritizedSelection(const App::MeasureSelection& selection) { + if (selection.size() != 1) { + return false; + } + + auto element = selection.front(); + auto objT = element.object; + + App::DocumentObject* ob = objT.getObject(); + const std::string& subName = objT.getSubName(); + const char* className = objT.getSubObject()->getTypeId().getName(); + std::string mod = Base::Type::getModuleName(className); + + if (!hasGeometryHandler(mod)) { + return false; + } + + App::MeasureHandler handler = App::MeasureManager::getMeasureHandler(mod.c_str()); + App::MeasureElementType type = handler.typeCb(ob, subName.c_str()); + + if (type == App::MeasureElementType::CIRCLE + || type == App::MeasureElementType::ARC) { + return true; + } + + return false; +} + + +//! Set properties from first item in selection. assumes a valid selection. +void MeasureRadius::parseSelection(const App::MeasureSelection& selection) { + auto element = selection.front(); + auto objT = element.object; + + std::vector subElementList { objT.getSubName() }; + Element.setValue(objT.getObject(), subElementList); +} + + +App::DocumentObjectExecReturn *MeasureRadius::execute() +{ + recalculateRadius(); + return DocumentObject::StdReturn; +} + + +void MeasureRadius::recalculateRadius() +{ + Radius.setValue(getMeasureInfoFirst()->radius); +} + +void MeasureRadius::onChanged(const App::Property* prop) +{ + if (isRestoring() || isRemoving()) { + return; + } + + if (prop == &Element) { + recalculateRadius(); + } + + MeasureBase::onChanged(prop); +} + + +//! return a placement (location + orientation) for the first element +Base::Placement MeasureRadius::getPlacement() { + auto loc = getMeasureInfoFirst()->pointOnCurve; + auto p = Base::Placement(); + p.setPosition(loc); + return p; +} + + +//! return the pointOnCurve element in MeasureRadiusInfo for the first element +Base::Vector3d MeasureRadius::getPointOnCurve() const +{ + return getMeasureInfoFirst()->pointOnCurve; +} + +//! get the handler's result for the first element +Part::MeasureRadiusInfoPtr MeasureRadius::getMeasureInfoFirst() const +{ + const App::DocumentObject* object = Element.getValue(); + const std::vector& subElements = Element.getSubValues(); + + if (!object || subElements.empty()) { +// NOLINTNEXTLINE(modernize-return-braced-init-list) + return std::make_shared(); + } + + std::string subElement = subElements.front(); + const char* className = object->getSubObject(subElement.c_str())->getTypeId().getName(); + const std::string& mod = Base::Type::getModuleName(className); + + auto handler = getGeometryHandler(mod); + if (!handler) { + throw Base::RuntimeError("No geometry handler available for submitted element type"); + } + + std::string obName = object->getNameInDocument(); + App::SubObjectT subject{object, subElement.c_str()}; + auto info = handler(subject); + auto radiusInfo = std::dynamic_pointer_cast(info); + return radiusInfo; +} + +//! Return the object we are measuring +//! used by the viewprovider in determining visibility +std::vector MeasureRadius::getSubject() const +{ + return {Element.getValue()}; +} diff --git a/src/Mod/Measure/App/MeasureRadius.h b/src/Mod/Measure/App/MeasureRadius.h new file mode 100644 index 0000000000..9686e4a392 --- /dev/null +++ b/src/Mod/Measure/App/MeasureRadius.h @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright (c) 2023 WandererFan * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#ifndef MEASURE_MEASURERADIUS_H +#define MEASURE_MEASURERADIUS_H + +#include + +#include +#include +#include +#include +#include + +#include + +#include "MeasureBase.h" + + +namespace Measure +{ + + +class MeasureExport MeasureRadius : public Measure::MeasureBaseExtendable +{ + PROPERTY_HEADER_WITH_OVERRIDE(Measure::MeasureRadius); + +public: + /// Constructor + MeasureRadius(); + ~MeasureRadius() override; + + App::PropertyLinkSub Element; + App::PropertyDistance Radius; + + App::DocumentObjectExecReturn *execute() override; + const char* getViewProviderName() const override { + return "MeasureGui::ViewProviderMeasure"; + } + + void recalculateRadius(); + + static bool isValidSelection(const App::MeasureSelection& selection); + static bool isPrioritizedSelection(const App::MeasureSelection& selection); + void parseSelection(const App::MeasureSelection& selection) override; + + std::vector getInputProps() override {return {"Element"};} + App::Property* getResultProp() override {return &this->Radius;} + + // Return a placement for the viewprovider, just use the first element for now + Base::Placement getPlacement() override; + // Return a point on curve for the viewprovider + Base::Vector3d getPointOnCurve() const; + + // Return the object we are measuring + std::vector getSubject() const override; + + +private: + void onChanged(const App::Property* prop) override; + Part::MeasureRadiusInfoPtr getMeasureInfoFirst() const; + + +}; + +} //namespace Measure + + +#endif // MEASURE_MEASURERADIUS_H + + diff --git a/src/Mod/Measure/App/Measurement.cpp b/src/Mod/Measure/App/Measurement.cpp index 3c4c6e20f7..1d8fd954f4 100644 --- a/src/Mod/Measure/App/Measurement.cpp +++ b/src/Mod/Measure/App/Measurement.cpp @@ -202,7 +202,7 @@ TopoDS_Shape Measurement::getShape(App::DocumentObject *obj , const char *subNam try { Part::TopoShape partShape = Part::Feature::getTopoShape(obj); - App::GeoFeature* geoFeat = dynamic_cast(obj); + App::GeoFeature* geoFeat = dynamic_cast(obj); if (geoFeat) { partShape.setPlacement(geoFeat->globalPlacement()); } diff --git a/src/Mod/Measure/App/MeasurementPy.xml b/src/Mod/Measure/App/MeasurementPy.xml index 4fa16fef3b..ae4f37b0f9 100644 --- a/src/Mod/Measure/App/MeasurementPy.xml +++ b/src/Mod/Measure/App/MeasurementPy.xml @@ -1,4 +1,5 @@  + * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +#endif + +#include +#include +#include +#include + +#include "Preferences.h" + + +//getters for parameters used in multiple places. +//ensure this is in sync with parameter names and default values on preference pages + +using namespace Measure; + +//! Returns the Measure preference group +Base::Reference Preferences::getPreferenceGroup(const char* Name) +{ + return App::GetApplication().GetUserParameter().GetGroup("BaseApp/Preferences/Mod/Measure")->GetGroup(Name); +} + +App::Color Preferences::defaultLineColor() +{ + App::Color fcColor; + fcColor.setPackedValue(getPreferenceGroup("Appearance")->GetUnsigned("DefaultLineColor", 0xFFFFFFFF)); + return fcColor; +} + +App::Color Preferences::defaultTextColor() +{ + App::Color fcColor; + fcColor.setPackedValue(getPreferenceGroup("Appearance")->GetUnsigned("DefaultTextColor", 0x00000000)); + return fcColor; +} + +App::Color Preferences::defaultTextBackgroundColor() +{ + App::Color fcColor; + fcColor.setPackedValue(getPreferenceGroup("Appearance")->GetUnsigned("DefaultTextBackgroundColor", 0xFFFFFFFF)); + return fcColor; +} + +double Preferences::defaultDistFactor() +{ + return getPreferenceGroup("Appearance")->GetFloat("DefaultDistFactor", 1.0); +} + +int Preferences::defaultFontSize() +{ + return getPreferenceGroup("Appearance")->GetInt("DefaultFontSize", 18); +} + +bool Preferences::defaultMirror() +{ + return getPreferenceGroup("Appearance")->GetBool("DefaultMirror", false); +} diff --git a/src/Mod/Measure/App/Preferences.h b/src/Mod/Measure/App/Preferences.h new file mode 100644 index 0000000000..44f186c485 --- /dev/null +++ b/src/Mod/Measure/App/Preferences.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (c) 2023 WandererFan * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef Preferences_h_ +#define Preferences_h_ + +#include + +#include + +#include +#include + +namespace App +{ +class Color; +} + +namespace Measure +{ + +//getters for parameters used in multiple places. +class MeasureExport Preferences +{ + +public: + static Base::Reference getPreferenceGroup(const char* Name); + + static App::Color defaultLineColor(); + static App::Color defaultTextColor(); + static double defaultDistFactor(); + static int defaultFontSize(); + static bool defaultMirror(); + static App::Color defaultTextBackgroundColor(); +}; + + +}//end namespace Measure +#endif + diff --git a/src/Mod/Measure/CMakeLists.txt b/src/Mod/Measure/CMakeLists.txt index 3ab73a3860..3babe21cd1 100644 --- a/src/Mod/Measure/CMakeLists.txt +++ b/src/Mod/Measure/CMakeLists.txt @@ -1,12 +1,29 @@ add_subdirectory(App) -if(FREECAD_BUILD_GUI) - #add_subdirectory(Gui) -endif(FREECAD_BUILD_GUI) + +set(Measure_Scripts + Init.py + UtilsMeasure.py + MeasureCOM.py +) + +if(BUILD_GUI) + list(APPEND Measure_Scripts InitGui.py) + add_subdirectory(Gui) +endif(BUILD_GUI) + +add_custom_target(MeasureScripts ALL + SOURCES ${Measure_Scripts} +) + +fc_target_copy_resource(MeasureScripts + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_BINARY_DIR}/Mod/Measure + ${Measure_Scripts}) INSTALL( FILES - Init.py + ${Measure_Scripts} DESTINATION Mod/Measure ) diff --git a/src/Mod/Measure/Gui/AppMeasureGui.cpp b/src/Mod/Measure/Gui/AppMeasureGui.cpp new file mode 100644 index 0000000000..41bbb81712 --- /dev/null +++ b/src/Mod/Measure/Gui/AppMeasureGui.cpp @@ -0,0 +1,99 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "DlgPrefsMeasureAppearanceImp.h" +#include "ViewProviderMeasureAngle.h" +#include "ViewProviderMeasureDistance.h" +#include "ViewProviderMeasureBase.h" + + +// use a different name to CreateCommand() +void CreateMeasureCommands(); + + +namespace MeasureGui +{ +class Module: public Py::ExtensionModule +{ +public: + Module() + : Py::ExtensionModule("MeasureGui") + { + initialize("This module is the MeasureGui module.");// register with Python + } + + ~Module() override = default; + +private: +}; + +PyObject* initModule() +{ + return Base::Interpreter().addModule(new Module); +} + +}// namespace MeasureGui + +/* Python entry */ +PyMOD_INIT_FUNC(MeasureGui) +{ + if (!Gui::Application::Instance) { + PyErr_SetString(PyExc_ImportError, "Cannot load Gui module in console application."); + PyMOD_Return(nullptr); + } + + // load dependent module + try { + Base::Interpreter().loadModule("Measure"); + } + catch(const Base::Exception& e) { + PyErr_SetString(PyExc_ImportError, e.what()); + PyMOD_Return(nullptr); + } + + PyObject* mod = MeasureGui::initModule(); + Base::Console().Log("Loading GUI of Measure module... done\n"); + + // instantiating the commands + CreateMeasureCommands(); + + MeasureGui::ViewProviderMeasureBase ::init(); + MeasureGui::ViewProviderMeasure ::init(); + MeasureGui::ViewProviderMeasureAngle ::init(); + MeasureGui::ViewProviderMeasureDistance ::init(); + + // register preferences pages + new Gui::PrefPageProducer(QT_TRANSLATE_NOOP("QObject", "Measure")); + +// Q_INIT_RESOURCE(Measure); + + PyMOD_Return(mod); +} diff --git a/src/Mod/Measure/Gui/CMakeLists.txt b/src/Mod/Measure/Gui/CMakeLists.txt new file mode 100644 index 0000000000..343906e412 --- /dev/null +++ b/src/Mod/Measure/Gui/CMakeLists.txt @@ -0,0 +1,90 @@ +include_directories( + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_BINARY_DIR}/src + ${CMAKE_CURRENT_BINARY_DIR} + ${Boost_INCLUDE_DIRS} + ${COIN3D_INCLUDE_DIRS} + ${OCC_INCLUDE_DIR} + ${PYTHON_INCLUDE_DIRS} + ${XercesC_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIR} +) +link_directories(${OCC_LIBRARY_DIR}) + +link_directories(${OCC_LIBRARY_DIR}) + +set(MeasureGui_LIBS + Measure + FreeCADGui +) + +if(MSVC) + include_directories( + ${CMAKE_SOURCE_DIR}/src/3rdParty/OpenGL/api + ) +endif(MSVC) + + +set (Measure_TR_QRC ${CMAKE_CURRENT_BINARY_DIR}/Resources/Measure_translation.qrc) +qt_find_and_add_translation(QM_SRCS "Resources/translations/*_*.ts" + ${CMAKE_CURRENT_BINARY_DIR}/Resources/translations) +qt_create_resource_file(${Measure_TR_QRC} ${QM_SRCS}) +qt_add_resources(MeasureGui_SRCS Resources/Measure.qrc ${Measure_TR_QRC}) + +SET(MeasureGui_UIC_SRCS + DlgPrefsMeasureAppearanceImp.ui +) + +SET(MeasureGui_SRCS + ${CMAKE_SOURCE_DIR}/src/Mod/Measure/InitGui.py + ${MeasureGui_SRCS} + AppMeasureGui.cpp + Command.cpp + Resources/Measure.qrc + PreCompiled.cpp + PreCompiled.h + ViewProviderMeasureBase.cpp + ViewProviderMeasureBase.h + ViewProviderMeasureAngle.cpp + ViewProviderMeasureAngle.h + ViewProviderMeasureDistance.cpp + ViewProviderMeasureDistance.h + # Workbench.cpp + # Workbench.h + DlgPrefsMeasureAppearanceImp.ui + DlgPrefsMeasureAppearanceImp.cpp + DlgPrefsMeasureAppearanceImp.h + +) + +SET(MeasureGuiTaskDlgs_SRCS + DlgPrefsMeasureAppearanceImp.ui +) + + +#set(MeasureGui_Scripts +# ../InitGui.py +#) + +if(FREECAD_USE_PCH) + add_definitions(-D_PreComp_) + GET_MSVC_PRECOMPILED_SOURCE("PreCompiled.cpp" PCH_SRCS ${MeasureGui_SRCS}) + ADD_MSVC_PRECOMPILED_HEADER(MeasureGui PreCompiled.h PreCompiled.cpp PCH_SRCS) +endif(FREECAD_USE_PCH) + +SET(MeasureGuiIcon_SVG + Resources/icons/preferences-measure.svg +) + +add_library(MeasureGui SHARED ${MeasureGui_SRCS} ${MeasureGuiIcon_SVG}) +target_link_libraries(MeasureGui ${MeasureGui_LIBS}) + +SET_BIN_DIR(MeasureGui MeasureGui /Mod/Measure) +SET_PYTHON_PREFIX_SUFFIX(MeasureGui) + +fc_copy_sources(MeasureGui "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Mod/Measure/" ${MeasureGuiIcon_SVG}) + +INSTALL(TARGETS MeasureGui DESTINATION ${CMAKE_INSTALL_LIBDIR}) +INSTALL(FILES ${MeasureGuiIcon_SVG} DESTINATION "${CMAKE_INSTALL_DATADIR}/Mod/Measure/Resources/icons") + diff --git a/src/Mod/Measure/Gui/Command.cpp b/src/Mod/Measure/Gui/Command.cpp new file mode 100644 index 0000000000..f7cbe5442f --- /dev/null +++ b/src/Mod/Measure/Gui/Command.cpp @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" + +#include +#include + + +using namespace std; + +void CreateMeasureCommands() { + Base::Console().Message("Init MeasureGui\n"); + Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager(); +} \ No newline at end of file diff --git a/src/Mod/Measure/Gui/DlgPrefsMeasureAppearanceImp.cpp b/src/Mod/Measure/Gui/DlgPrefsMeasureAppearanceImp.cpp new file mode 100644 index 0000000000..fe6aa3ede1 --- /dev/null +++ b/src/Mod/Measure/Gui/DlgPrefsMeasureAppearanceImp.cpp @@ -0,0 +1,75 @@ + /************************************************************************** + * Copyright (c) 2023 Wanderer Fan * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" + +#include "DlgPrefsMeasureAppearanceImp.h" +#include "ui_DlgPrefsMeasureAppearanceImp.h" + +using namespace MeasureGui; + +DlgPrefsMeasureAppearanceImp::DlgPrefsMeasureAppearanceImp( QWidget* parent ) + : PreferencePage( parent ) + , ui(new Ui_DlgPrefsMeasureAppearanceImp) +{ + ui->setupUi(this); +} + +DlgPrefsMeasureAppearanceImp::~DlgPrefsMeasureAppearanceImp() +{ + // no need to delete child widgets, Qt does it all for us +} + +void DlgPrefsMeasureAppearanceImp::saveSettings() +{ + ui->cbMirror->onSave(); + ui->dsbDistFactor->onSave(); + ui->sbFontSize->onSave(); + ui->cbText->onSave(); + ui->cbLine->onSave(); + ui->cbBackground->onSave(); +} + +void DlgPrefsMeasureAppearanceImp::loadSettings() +{ + ui->sbFontSize->onRestore(); + ui->cbText->onRestore(); + ui->cbBackground->onRestore(); + ui->cbLine->onRestore(); + ui->cbMirror->onRestore(); + ui->dsbDistFactor->onRestore(); +} + +/** + * Sets the strings of the subwidgets using the current language. + */ +void DlgPrefsMeasureAppearanceImp::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::LanguageChange) { + ui->retranslateUi(this); + } + else { + QWidget::changeEvent(e); + } +} + +#include + diff --git a/src/Mod/Measure/Gui/DlgPrefsMeasureAppearanceImp.h b/src/Mod/Measure/Gui/DlgPrefsMeasureAppearanceImp.h new file mode 100644 index 0000000000..b4d1634d23 --- /dev/null +++ b/src/Mod/Measure/Gui/DlgPrefsMeasureAppearanceImp.h @@ -0,0 +1,55 @@ + /************************************************************************** + * Copyright (c) 2023 Wanderer Fan * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef MeasureGui_DlgPrefsAppearanceImp_H +#define MeasureGui_DlgPrefsAppearanceImp_H + +#include + +#include +#include + + +namespace MeasureGui { + +class Ui_DlgPrefsMeasureAppearanceImp; + +class DlgPrefsMeasureAppearanceImp : public Gui::Dialog::PreferencePage +{ + Q_OBJECT + +public: + explicit DlgPrefsMeasureAppearanceImp( QWidget* parent = nullptr ); + ~DlgPrefsMeasureAppearanceImp() override; + +protected: + void saveSettings() override; + void loadSettings() override; + void changeEvent(QEvent *e) override; + +private: + std::unique_ptr ui; +}; + +} // namespace MeasureGui + +#endif // MeasureGui_DlgPrefsAppearanceImp_H + diff --git a/src/Mod/Measure/Gui/DlgPrefsMeasureAppearanceImp.ui b/src/Mod/Measure/Gui/DlgPrefsMeasureAppearanceImp.ui new file mode 100644 index 0000000000..acaf50ff97 --- /dev/null +++ b/src/Mod/Measure/Gui/DlgPrefsMeasureAppearanceImp.ui @@ -0,0 +1,247 @@ + + + MeasureGui::DlgPrefsMeasureAppearanceImp + + + + 0 + 0 + 623 + 287 + + + + + 0 + 0 + + + + Appearance + + + + :/icons/preferences-measure.svg:/icons/preferences-measure.svg + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 200 + + + + Default Property Values + + + + + + + + Distance Factor + + + + + + + Text color + + + + + + + + 0 + 0 + 0 + + + + DefaultTextColor + + + Mod/Measure/Appearance + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 1 + + + 18 + + + DefaultFontSize + + + Mod/Measure/Appearance + + + + + + + + 0 + 0 + + + + + 255 + 255 + 255 + + + + DefaultLineColor + + + Mod/Measure/Appearance + + + + + + + Text size + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 1.000000000000000 + + + DefaultDistFactor + + + Mod/Measure/Appearance + + + + + + + Line color + + + + + + + Mirror + + + DefaultMirror + + + Mod/Measure/Appearance + + + + + + + Background color + + + + + + + + 0 + 0 + 0 + + + + DefaultTextBackgroundColor + + + Mod/Measure/Appearance + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Gui::ColorButton + QPushButton +
Gui/Widgets.h
+
+ + Gui::PrefSpinBox + QSpinBox +
Gui/PrefWidgets.h
+
+ + Gui::PrefColorButton + Gui::ColorButton +
Gui/PrefWidgets.h
+
+ + Gui::PrefCheckBox + QCheckBox +
Gui/PrefWidgets.h
+
+ + Gui::PrefDoubleSpinBox + QDoubleSpinBox +
Gui/PrefWidgets.h
+
+
+ + + + +
diff --git a/src/Mod/Measure/Gui/PreCompiled.cpp b/src/Mod/Measure/Gui/PreCompiled.cpp new file mode 100644 index 0000000000..78c1dbe181 --- /dev/null +++ b/src/Mod/Measure/Gui/PreCompiled.cpp @@ -0,0 +1,22 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" diff --git a/src/Mod/Measure/Gui/PreCompiled.h b/src/Mod/Measure/Gui/PreCompiled.h new file mode 100644 index 0000000000..f42331cb55 --- /dev/null +++ b/src/Mod/Measure/Gui/PreCompiled.h @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef MEASUREGUI_PRECOMPILED_H +#define MEASUREGUI_PRECOMPILED_H + +#include + +#include + +// point at which warnings of overly long specifiers disabled (needed for VC6) +#ifdef _MSC_VER +# pragma warning( disable : 4251 ) +# pragma warning( disable : 4503 ) +# pragma warning( disable : 4786 ) // specifier longer then 255 chars +# pragma warning( disable : 4273 ) +#endif + +#ifdef FC_OS_WIN32 +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include +#endif + +#ifdef _PreComp_ + +// standard +#include +#include + +// STL +#include +#include +#include +#include +#include + +// OpenCasCade +#include + +// Boost +#include +#include + +// GL +// Include glext before QtAll/InventorAll +#ifdef FC_OS_WIN32 +# include +# include +#else +# ifdef FC_OS_MACOSX +# include +# include +# else +# ifndef GL_GLEXT_PROTOTYPES +# define GL_GLEXT_PROTOTYPES 1 +# endif +# include +# include +# endif //FC_OS_MACOSX +#endif //FC_OS_WIN32 +// Should come after glext.h to avoid warnings +#include + +// Qt Toolkit +#ifndef __QtAll__ +# include +#endif + +// Inventor includes OpenGL +#ifndef __InventorAll__ +# include +#endif + +#endif //_PreComp_ + +#endif + diff --git a/src/Mod/Measure/Gui/Resources/Measure.qrc b/src/Mod/Measure/Gui/Resources/Measure.qrc new file mode 100644 index 0000000000..43a28bbdab --- /dev/null +++ b/src/Mod/Measure/Gui/Resources/Measure.qrc @@ -0,0 +1,6 @@ + + + icons/umf-measurement.svg + icons/preferences-measure.svg + + diff --git a/src/Mod/Measure/Gui/Resources/icons/preferences-measure.svg b/src/Mod/Measure/Gui/Resources/icons/preferences-measure.svg new file mode 100644 index 0000000000..e9971aed73 --- /dev/null +++ b/src/Mod/Measure/Gui/Resources/icons/preferences-measure.svg @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Measure/Gui/Resources/icons/umf-measurement.svg b/src/Mod/Measure/Gui/Resources/icons/umf-measurement.svg new file mode 100644 index 0000000000..e9971aed73 --- /dev/null +++ b/src/Mod/Measure/Gui/Resources/icons/umf-measurement.svg @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/Measure/Gui/Resources/translations/Measure.ts b/src/Mod/Measure/Gui/Resources/translations/Measure.ts new file mode 100644 index 0000000000..ab1f5953c1 --- /dev/null +++ b/src/Mod/Measure/Gui/Resources/translations/Measure.ts @@ -0,0 +1,42 @@ + + + + + MeasureGui::DlgPrefsMeasureAppearanceImp + + + Appearance + + + + + Default Property Values + + + + + Distance Factor + + + + + Text color + + + + + Text size + + + + + Line color + + + + + Mirror + + + + diff --git a/src/Mod/Measure/Gui/ViewProviderMeasureAngle.cpp b/src/Mod/Measure/Gui/ViewProviderMeasureAngle.cpp new file mode 100644 index 0000000000..392ef01ce4 --- /dev/null +++ b/src/Mod/Measure/Gui/ViewProviderMeasureAngle.cpp @@ -0,0 +1,339 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * Copyright (c) 2013 Thomas Anderson * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include +#include +#include +#include +#include +# include +# include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ViewProviderMeasureAngle.h" + + +using namespace MeasureGui; +using namespace Measure; + +gp_Lin getLine(gp_Vec& vec, gp_Vec& origin) { + gp_Pnt tempOrigin; + tempOrigin.SetXYZ(origin.XYZ()); + return gp_Lin(tempOrigin, gp_Dir(vec)); +} + +SbMatrix ViewProviderMeasureAngle::getMatrix() { + // Code ported from src/Mod/Part/Gui/TaskDimension.cpp + + if (pcObject == nullptr) { + throw Base::RuntimeError("no DocumentObject"); + } + + Measure::MeasureAngle* measurement = static_cast(pcObject); + + if (!measurement->Element1.getValue() || measurement->Element1.getSubValues().empty()){ + return SbMatrix(); + } + if (!measurement->Element2.getValue() || measurement->Element2.getSubValues().empty()){ + return SbMatrix(); + } + + + gp_Vec vector1 = measurement->vector1(); + gp_Vec vector2 = measurement->vector2(); + + gp_Vec loc1 = measurement->location1(); + gp_Vec loc2 = measurement->location2(); + + if (vector1.Magnitude() == 0 || vector2.Magnitude() == 0) { + return SbMatrix(); + } + + gp_Lin lin1 = getLine(vector1, loc1); + gp_Lin lin2 = getLine(vector2, loc2); + + SbMatrix dimSys = SbMatrix(); + double radius; + + if (vector1.IsParallel(vector2, Precision::Angular())) { + //take first point project it onto second vector. + Handle(Geom_Curve) heapLine2 = new Geom_Line(lin2); + gp_Pnt tempPoint(loc1.XYZ()); + + GeomAPI_ProjectPointOnCurve projection(tempPoint, heapLine2); + if (projection.NbPoints() < 1) + { + throw Base::RuntimeError("parallel vectors: couldn't project onto line"); + } + gp_Vec newPoint2; + newPoint2.SetXYZ(projection.Point(1).XYZ()); + + //if points are colinear, projection doesn't work and returns the same point. + //In this case we just use the original point. + if ((newPoint2 - loc1).Magnitude() < Precision::Confusion()) + newPoint2 = loc2; + + //now get midpoint between for dim origin. + gp_Vec point1 = loc1; + gp_Vec midPointProjection = newPoint2 - point1; + double distance = midPointProjection.Magnitude(); + midPointProjection.Normalize(); + midPointProjection *= distance / 2.0; + + gp_Vec origin = point1 + midPointProjection; + + //yaxis should be the same as vector1, but doing this to eliminate any potential slop from + //using precision::angular. If lines are colinear and we have no plane, we can't establish zAxis from crossing. + //we just the absolute axis. + gp_Vec xAxis = (point1 - origin).Normalized(); + gp_Vec zAxis; + if (xAxis.IsParallel(vector1, Precision::Angular())) { + if (!xAxis.IsParallel(gp_Vec(0.0, 0.0, 1.0), Precision::Angular())) + zAxis = gp_Vec(0.0, 0.0, 1.0); + else + zAxis = gp_Vec(0.0, 1.0, 0.0); + } + else + zAxis = xAxis.Crossed(vector1).Normalized(); + gp_Vec yAxis = zAxis.Crossed(xAxis).Normalized(); + zAxis = xAxis.Crossed(yAxis).Normalized(); + + dimSys = SbMatrix + ( + xAxis.X(), yAxis.X(), zAxis.X(), origin.X(), + xAxis.Y(), yAxis.Y(), zAxis.Y(), origin.Y(), + xAxis.Z(), yAxis.Z(), zAxis.Z(), origin.Z(), + 0.0, 0.0, 0.0, 1.0 + ); + dimSys = dimSys.transpose(); + + radius = midPointProjection.Magnitude(); + } else { + Handle(Geom_Curve) heapLine1 = new Geom_Line(lin1); + Handle(Geom_Curve) heapLine2 = new Geom_Line(lin2); + + GeomAPI_ExtremaCurveCurve extrema(heapLine1, heapLine2); + + if (extrema.NbExtrema() < 1) + { + throw Base::RuntimeError("couldn't get extrema"); + } + + gp_Pnt extremaPoint1, extremaPoint2, dimensionOriginPoint; + extrema.Points(1, extremaPoint1, extremaPoint2); + if (extremaPoint1.Distance(extremaPoint2) < Precision::Confusion()) + dimensionOriginPoint = extremaPoint1; + else + { + //find halfway point in between extrema points for dimension origin. + gp_Vec vec1(extremaPoint1.XYZ()); + gp_Vec vec2(extremaPoint2.XYZ()); + gp_Vec connection(vec2-vec1); + Standard_Real distance = connection.Magnitude(); + connection.Normalize(); + connection *= (distance / 2.0); + dimensionOriginPoint.SetXYZ((vec1 + connection).XYZ()); + } + + gp_Vec thirdPoint(loc2); + gp_Vec originVector(dimensionOriginPoint.XYZ()); + gp_Vec extrema2Vector(extremaPoint2.XYZ()); + radius = (loc1 - originVector).Magnitude(); + double legOne = (extrema2Vector - originVector).Magnitude(); + if (legOne > Precision::Confusion()) + { + double legTwo = sqrt(pow(radius, 2) - pow(legOne, 2)); + gp_Vec projectionVector(vector2); + projectionVector.Normalize(); + projectionVector *= legTwo; + thirdPoint = extrema2Vector + projectionVector; + gp_Vec hyp(thirdPoint - originVector); + hyp.Normalize(); + gp_Vec otherSide(loc1 - originVector); + otherSide.Normalize(); + } + + gp_Vec xAxis = (loc1 - originVector).Normalized(); + gp_Vec fakeYAxis = (thirdPoint - originVector).Normalized(); + gp_Vec zAxis = (xAxis.Crossed(fakeYAxis)).Normalized(); + gp_Vec yAxis = zAxis.Crossed(xAxis).Normalized(); + + dimSys = SbMatrix + ( + xAxis.X(), yAxis.X(), zAxis.X(), dimensionOriginPoint.X(), + xAxis.Y(), yAxis.Y(), zAxis.Y(), dimensionOriginPoint.Y(), + xAxis.Z(), yAxis.Z(), zAxis.Z(), dimensionOriginPoint.Z(), + 0.0, 0.0, 0.0, 1.0 + ); + + dimSys = dimSys.transpose(); + } + + return dimSys; +} + + + +PROPERTY_SOURCE(MeasureGui::ViewProviderMeasureAngle, MeasureGui::ViewProviderMeasureBase) + + +ViewProviderMeasureAngle::ViewProviderMeasureAngle() +{ + sPixmap = "umf-measurement"; + + // Primary Arc + Gui::ArcEngine *arcEngine = new Gui::ArcEngine(); + arcEngine->angle.connectFrom(&fieldAngle); + + auto calculatorRadius = new SoCalculator(); + calculatorRadius->A.connectFrom(&pDragger->translation); + calculatorRadius->expression.setValue("oa=length(A)"); + arcEngine->radius.connectFrom(&calculatorRadius->oa); + arcEngine->deviation.setValue(0.1f); + + SoCoordinate3 *coordinates = new SoCoordinate3(); + coordinates->point.connectFrom(&arcEngine->points); + + SoLineSet *lineSet = new SoLineSet(); + lineSet->vertexProperty.setValue(coordinates); + lineSet->numVertices.connectFrom(&arcEngine->pointCount); + lineSet->startIndex.setValue(0); + + pLineSeparator->addChild(lineSet); + + // Secondary Arc, from midpoint to label + auto engineAngle = new SoCalculator(); + engineAngle->A.connectFrom(&arcEngine->midpoint); + engineAngle->B.connectFrom(&pLabelTranslation->translation); + engineAngle->expression.setValue("tA=normalize(A); tB=normalize(B); oa=atan2(tB[1], tB[0])-atan2(tA[1], tA[0])"); + + Gui::ArcEngine *arcEngineSecondary = new Gui::ArcEngine(); + arcEngineSecondary->radius.connectFrom(&calculatorRadius->oa); + arcEngineSecondary->deviation.setValue(0.1f); + arcEngineSecondary->angle.connectFrom(&engineAngle->oa); + + // Rotate arc + auto engineRotMidpoint = new SoComposeRotationFromTo(); // absolute angle to midpoint + engineRotMidpoint->from.setValue(SbVec3f(1.0, 0.0, 0.0)); + engineRotMidpoint->to.connectFrom(&arcEngine->midpoint); + + auto matrixEngine = new SoComposeMatrix(); + matrixEngine->rotation.connectFrom(&engineRotMidpoint->rotation); + auto transformEngine = new SoTransformVec3f(); + transformEngine->matrix.connectFrom(&matrixEngine->matrix); + transformEngine->vector.connectFrom(&arcEngineSecondary->points); + + SoCoordinate3 *coordinatesSecondary = new SoCoordinate3(); + coordinatesSecondary->point.connectFrom(&transformEngine->point); + + SoLineSet *lineSetSecondary = new SoLineSet(); + lineSetSecondary->vertexProperty.setValue(coordinatesSecondary); + lineSetSecondary->numVertices.connectFrom(&arcEngineSecondary->pointCount); + lineSetSecondary->startIndex.setValue(0); + + pLineSeparatorSecondary->addChild(lineSetSecondary); +} + + +void ViewProviderMeasureAngle::redrawAnnotation() +{ + auto obj = dynamic_cast(getMeasureObject()); + double angleDeg = obj->Angle.getValue(); + constexpr double radiansPerDegree = M_PI / 180.0; + this->fieldAngle = angleDeg * radiansPerDegree; + + // Set matrix + try { + SbMatrix matrix = getMatrix(); + pcTransform->setMatrix(matrix); + + } catch (const Base::Exception& e) { + Base::Console().Error("Error in ViewProviderMeasureAngle::redrawAnnotation: %s\n", e.what()); + return; + } + + // Set Label + setLabelValue(static_cast(pcObject)->getResultString()); +} + + +//! return the feature as a MeasureAngle +Measure::MeasureAngle* ViewProviderMeasureAngle::getMeasureAngle() +{ + Measure::MeasureAngle* feature = dynamic_cast(pcObject); + if (!feature) { + throw Base::RuntimeError("Feature not found for ViewProviderMeasureAngle"); + } + return feature; +} + + +void ViewProviderMeasureAngle::positionAnno(const Measure::MeasureBase* measureObject) { + (void)measureObject; + setLabelTranslation(SbVec3f(0, 10, 0)); +} + diff --git a/src/Mod/Measure/Gui/ViewProviderMeasureAngle.h b/src/Mod/Measure/Gui/ViewProviderMeasureAngle.h new file mode 100644 index 0000000000..8182b68c38 --- /dev/null +++ b/src/Mod/Measure/Gui/ViewProviderMeasureAngle.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#ifndef GUI_VIEWPROVIDERMEASUREANGLE_H +#define GUI_VIEWPROVIDERMEASUREANGLE_H + +#include + +#include + +#include +#include +#include + +#include + +#include "ViewProviderMeasureBase.h" + +//NOLINTBEGIN +class SoText2; +class SoTranslation; +class SoCoordinate3; +class SoIndexedLineSet; +class SoTransform; +//NOLINTEND + +namespace MeasureGui +{ + +class MeasureGuiExport ViewProviderMeasureAngle : public MeasureGui::ViewProviderMeasureBase +{ + PROPERTY_HEADER_WITH_OVERRIDE(MeasureGui::ViewProviderMeasureAngle); + +public: + /// Constructor + ViewProviderMeasureAngle(); + + Measure::MeasureAngle* getMeasureAngle(); + void redrawAnnotation() override; + void positionAnno(const Measure::MeasureBase* measureObject) override; + +private: + // Fields + SoSFFloat fieldAngle; //radians. + + SbMatrix getMatrix(); +}; + + +} //namespace MeasureGui + + + +#endif // GUI_VIEWPROVIDERMEASUREANGLE_H diff --git a/src/Mod/Measure/Gui/ViewProviderMeasureBase.cpp b/src/Mod/Measure/Gui/ViewProviderMeasureBase.cpp new file mode 100644 index 0000000000..a0bf468f27 --- /dev/null +++ b/src/Mod/Measure/Gui/ViewProviderMeasureBase.cpp @@ -0,0 +1,597 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "Gui/Application.h" +#include "Gui/MDIView.h" +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ViewProviderMeasureBase.h" + + +using namespace MeasureGui; +using namespace Measure; + +//NOLINTBEGIN +PROPERTY_SOURCE(MeasureGui::ViewProviderMeasureBase, Gui::ViewProviderDocumentObject) +//NOLINTEND + +ViewProviderMeasureBase::ViewProviderMeasureBase() +{ + static const char *agroup = "Appearance"; +//NOLINTBEGIN + ADD_PROPERTY_TYPE(TextColor, (Preferences::defaultTextColor()), agroup, App::Prop_None, "Color for the measurement text"); + ADD_PROPERTY_TYPE(TextBackgroundColor, (Preferences::defaultTextBackgroundColor()), agroup, App::Prop_None, "Color for the measurement text background"); + ADD_PROPERTY_TYPE(LineColor, (Preferences::defaultLineColor()), agroup, App::Prop_None, "Color for the measurement lines"); + ADD_PROPERTY_TYPE(FontSize, (Preferences::defaultFontSize()), agroup, App::Prop_None, "Size of measurement text"); +//NOLINTEND + + // setupAnnoSceneGraph() - sets up the annotation scene graph + pLabel = new Gui::SoFrameLabel(); + pLabel->ref(); + pColor = new SoBaseColor(); + pColor->ref(); + pLabelTranslation = new SoTransform(); + pLabelTranslation->ref(); + + auto ps = getSoPickStyle(); + + // Dragger + SoSeparator* dragSeparator = new SoSeparator(); + pDragger = new SoTranslate2Dragger(); + pDragger->ref(); + pDraggerOrientation = new SoTransform(); + pDraggerOrientation->ref(); + dragSeparator->addChild(pDraggerOrientation); + dragSeparator->addChild(pDragger); + + // Transform drag location by dragger local orientation and connect to labelTranslation + auto matrixEngine = new SoComposeMatrix(); + matrixEngine->rotation.connectFrom(&pDraggerOrientation->rotation); + auto transformEngine = new SoTransformVec3f(); + transformEngine->vector.connectFrom(&pDragger->translation); + transformEngine->matrix.connectFrom(&matrixEngine->matrix); + pLabelTranslation->translation.connectFrom(&transformEngine->point); + + pTextSeparator = new SoSeparator(); + pTextSeparator->ref(); + pTextSeparator->addChild(dragSeparator); + pTextSeparator->addChild(pLabelTranslation); + pTextSeparator->addChild(pLabel); + + // Empty line separator which can be populated by inherited class + pLineSeparator = new SoSeparator(); + pLineSeparator->ref(); + pLineSeparator->addChild(ps); + pLineSeparator->addChild(getSoLineStylePrimary()); + pLineSeparator->addChild(pColor); + + // Secondary line separator + pLineSeparatorSecondary = new SoSeparator(); + pLineSeparatorSecondary->ref(); + pLineSeparatorSecondary->addChild(ps); + pLineSeparatorSecondary->addChild(getSoLineStyleSecondary()); + pLineSeparatorSecondary->addChild(pColor); + + pRootSeparator = new SoAnnotation(); + pRootSeparator->ref(); + pRootSeparator->addChild(pLineSeparator); + pRootSeparator->addChild(pLineSeparatorSecondary); + pRootSeparator->addChild(pTextSeparator); + addDisplayMaskMode(pRootSeparator, "Base"); + + pRootSeparator->touch(); + pTextSeparator->touch(); + pLineSeparator->touch(); + + // Register dragger callback + auto dragger = pDragger; + + dragger->addValueChangedCallback(draggerChangedCallback, this); + + + // Use the label node as the transform handle + SoSearchAction sa; + sa.setInterest(SoSearchAction::FIRST); + sa.setSearchingAll(true); + sa.setNode(pLabel); + sa.apply(pcRoot); + SoPath * labelPath = sa.getPath(); + assert(labelPath); + dragger->setPartAsPath("translator", labelPath); + + // Hide the dragger feedback during translation + dragger->setPart("translatorActive", NULL); + dragger->setPart("xAxisFeedback", NULL); + dragger->setPart("yAxisFeedback", NULL); + // end setupSceneGraph + + // these touches cause onChanged to run which then updates pLabel and pColor with the initial values + TextColor.touch(); + TextBackgroundColor.touch(); + FontSize.touch(); + LineColor.touch(); + +} + +ViewProviderMeasureBase::~ViewProviderMeasureBase() +{ + _mVisibilityChangedConnection.disconnect(); + pLabel->unref(); + pColor->unref(); + pDragger->unref(); + pDraggerOrientation->unref(); + pLabelTranslation->unref(); + pTextSeparator->unref(); + pLineSeparator->unref(); + pRootSeparator->unref(); +} + +std::vector ViewProviderMeasureBase::getDisplayModes() const +{ + // add modes + std::vector StrList; + StrList.emplace_back("Base"); + return StrList; +} + +void ViewProviderMeasureBase::setDisplayMode(const char* ModeName) +{ + if (strcmp(ModeName, "Base") == 0) { + setDisplayMaskMode("Base"); + } + ViewProviderDocumentObject::setDisplayMode(ModeName); +} + + +void ViewProviderMeasureBase::finishRestoring() { + // Force measurement visibility when loading a document + show(); +} + + +void ViewProviderMeasureBase::onChanged(const App::Property* prop) +{ + if (prop == &TextColor) { + const App::Color& color = TextColor.getValue(); + pLabel->textColor.setValue(color.r, color.g, color.b); + } + else if (prop == &TextBackgroundColor) { + const App::Color& color = TextBackgroundColor.getValue(); + pLabel->backgroundColor.setValue(color.r, color.g, color.b); + } + else if (prop == &LineColor) { + const App::Color& color = LineColor.getValue(); + pColor->rgb.setValue(color.r, color.g, color.b); + } + else if (prop == &FontSize) { + pLabel->size = FontSize.getValue(); + } + ViewProviderDocumentObject::onChanged(prop); +} + +void ViewProviderMeasureBase::draggerChangedCallback(void *data, SoDragger *) { + auto me = static_cast(data); + me->onLabelMoved(); +} + +void ViewProviderMeasureBase::setLabelValue(const Base::Quantity& value) { + pLabel->string.setValue(value.getUserString().toUtf8().constData()); +} + +void ViewProviderMeasureBase::setLabelValue(const QString& value) { + auto lines = value.split(QString::fromLatin1("\n")); + + int i = 0; + for (auto& it : lines) { + pLabel->string.set1Value(i, it.toUtf8().constData()); + i++; + } +} + +void ViewProviderMeasureBase::setLabelTranslation(const SbVec3f& position) { + // Set the dragger translation to keep it in sync with pLabelTranslation + pDragger->translation.setValue(position); +} + + +SoPickStyle* ViewProviderMeasureBase::getSoPickStyle() { + auto ps = new SoPickStyle(); + ps->style = SoPickStyle::UNPICKABLE; + return ps; +} + +SoDrawStyle* ViewProviderMeasureBase::getSoLineStylePrimary() { + auto style = new SoDrawStyle(); + style->lineWidth = 2.0f; + return style; +} + +SoDrawStyle* ViewProviderMeasureBase::getSoLineStyleSecondary() { + auto style = new SoDrawStyle(); + style->lineWidth = 1.0f; + return style; +} + +SoSeparator* ViewProviderMeasureBase::getSoSeparatorText() { + return pTextSeparator; +} + + +void ViewProviderMeasureBase::positionAnno(const Measure::MeasureBase* measureObject) { + (void)measureObject; +} + + +void ViewProviderMeasureBase::attach(App::DocumentObject *pcObj) +{ + ViewProviderDocumentObject::attach(pcObj); + positionAnno(static_cast(pcObj)); +} + + +//! handle changes to the feature's properties +void ViewProviderMeasureBase::updateData(const App::Property* prop) +{ + bool doUpdate = false; + + auto obj = getMeasureObject(); + if (!obj) { + return; + } + + // Check if one of the input properties has been changed + auto inputProps = obj->getInputProps(); + if (std::find(inputProps.begin(), inputProps.end(), std::string(prop->getName())) != inputProps.end()) { + doUpdate = true; + + // Add connections to be notified when the measured objects are changed + connectToSubject(obj->getSubject()); + } + + // Check if the result prop has been changed + auto resultProp = obj->getResultProp(); + if (resultProp && prop == resultProp){ + doUpdate = true; + } + + if (doUpdate) { + redrawAnnotation(); + } + + ViewProviderDocumentObject::updateData(prop); +} + + +// TODO: should this be pure virtual? +void ViewProviderMeasureBase::redrawAnnotation() +{ + // Base::Console().Message("VPMB::redrawAnnotation()\n"); +} + +//! connect to the subject to receive visibility updates +void ViewProviderMeasureBase::connectToSubject(App::DocumentObject* subject) +{ + if (!subject) { + return; + } + + // disconnect any existing connection + if (_mVisibilityChangedConnection.connected()) { + _mVisibilityChangedConnection.disconnect(); + } + + //NOLINTBEGIN + auto bndVisibility = std::bind(&ViewProviderMeasureBase::onSubjectVisibilityChanged, this, std::placeholders::_1, std::placeholders::_2); + //NOLINTEND + _mVisibilityChangedConnection = subject->signalChanged.connect(bndVisibility); +} + +//! connect to the subject to receive visibility updates +void ViewProviderMeasureBase::connectToSubject(std::vector subject) +{ + if (subject.empty()) { + return; + } + + // TODO: should we connect to all the subject objects when there is >1? + auto proxy = subject.front(); + connectToSubject(proxy); +} + + +//! retrieve the feature +Measure::MeasureBase* ViewProviderMeasureBase::getMeasureObject() +{ + // Note: Cast to MeasurePropertyBase once we use it to provide the needed values e.g. basePosition textPosition etc. + auto feature = dynamic_cast(pcObject); + if (!feature) { + throw Base::RuntimeError("Feature not found for ViewProviderMeasureBase"); + } + return feature; +} + + +//! calculate a good direction from the elements being measured to the annotation text based on the layout +//! of the elements and relationship with the cardinal axes and the view direction. elementDirection +//! is expected to be a normalized vector. +//! an example of an elementDirection would be the vector from the start of a line to the end. +Base::Vector3d ViewProviderMeasureBase::getTextDirection(Base::Vector3d elementDirection, double tolerance) +{ + // TODO: this can fail if the active view is not a 3d view (spreadsheet, techdraw page) and something causes a measure to try to update + // we need to search through the mdi views for a 3d view and take the direction from it (or decide that if the active view is not 3d, + // assume we are looking from the front). + Base::Vector3d viewDirection; + Base::Vector3d upDirection; + +Gui::View3DInventor* view = nullptr; + try { + view = dynamic_cast(this->getActiveView()); + } catch (const Base::RuntimeError& e) { + Base::Console().Log("ViewProviderMeasureBase::getTextDirection: Could not get active view\n"); + } + + if (view) { + Gui::View3DInventorViewer* viewer = view->getViewer(); + viewDirection = toVector3d(viewer->getViewDirection()).Normalize(); + upDirection = toVector3d(viewer->getUpDirection()).Normalize(); + // Measure doesn't work with this kind of active view. Might be dependency graph, might be TechDraw, or ???? + //throw Base::RuntimeError("Measure doesn't work with this kind of active view."); + } else { + viewDirection = Base::Vector3d(0.0, 1.0, 0.0); + upDirection = Base::Vector3d(0.0, 0.0, 1.0); + } + + Base::Vector3d textDirection = elementDirection.Cross(viewDirection); + if (textDirection.Length() < tolerance) { + // either elementDirection and viewDirection are parallel or one of them is null. + textDirection = elementDirection.Cross(upDirection); + } + + return textDirection.Normalize(); +} + + +//! true if the subject of this measurement is visible. For Measures that have multiple object subject, +//! all of the subjects must be visible. +bool ViewProviderMeasureBase::isSubjectVisible() +{ + Gui::Document* guiDoc = nullptr; + try { + guiDoc = this->getDocument(); + } catch (const Base::RuntimeError& e) { + Base::Console().Log("ViewProviderMeasureBase::isSubjectVisible: Could not get document\n"); + return false; + } + + // we need these things to proceed + if (!getMeasureObject() || + getMeasureObject()->getSubject().empty() || + !guiDoc ) { + return false; + } + + for (auto & obj : getMeasureObject()->getSubject()) { + Gui::ViewProvider* vp = guiDoc->getViewProvider(obj); + if (!vp || !vp->isVisible()) { + return false; + } + } + + // all of the subject objects are visible + return true; +} + + +//! gets called when the subject object issues a signalChanged (ie a property change). We are only interested in the subject's +//! Visibility property +void ViewProviderMeasureBase::onSubjectVisibilityChanged(const App::DocumentObject& docObj, const App::Property& prop) +{ + if (docObj.isRemoving()) { + return; + } + + std::string propName = prop.getName(); + if (propName == "Visibility") { + if (!docObj.Visibility.getValue()) { + // show ourselves only if subject is visible + setVisible(false); + } else { + // here, we don't know if we should be visible or not, so we have to check the whole subject + setVisible(isSubjectVisible()); + } + } +} + + +//NOLINTBEGIN +PROPERTY_SOURCE(MeasureGui::ViewProviderMeasure, MeasureGui::ViewProviderMeasureBase) +//NOLINTEND + +//! the general purpose view provider. handles area, length, etc - any measure without a specialized VP +ViewProviderMeasure::ViewProviderMeasure() +{ + sPixmap = "umf-measurement"; + + + // setupSceneGraph for leader? + const size_t lineCount(3); + + // indexes used to create the edges + // this makes a line from verts[0] to verts[1] + static const int32_t lines[lineCount] = + { + 0,1,-1 + }; + + pCoords = new SoCoordinate3(); + pCoords->ref(); + + // Combine coordinates from baseTranslation and labelTranslation + auto engineCat = new SoConcatenate(SoMFVec3f::getClassTypeId()); + auto origin = new SoSFVec3f(); + origin->setValue(0,0,0); + engineCat->input[0]->connectFrom(origin); + engineCat->input[1]->connectFrom(&pLabelTranslation->translation); + pCoords->point.setNum(engineCat->output->getNumConnections()); + pCoords->point.connectFrom(engineCat->output); + + pLines = new SoIndexedLineSet(); + pLines->ref(); + pLines->coordIndex.setNum(lineCount); + pLines->coordIndex.setValues(0, lineCount, lines); + + auto lineSep = pLineSeparator; + lineSep->addChild(pCoords); + lineSep->addChild(pLines); + auto points = new SoMarkerSet(); + points->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("CROSS", + Gui::ViewParams::instance()->getMarkerSize()); + points->numPoints=1; + lineSep->addChild(points); + + // Connect dragger local orientation to view orientation + Gui::View3DInventor* view = nullptr; + try { + view = dynamic_cast(this->getActiveView()); + } catch (const Base::RuntimeError& e) { + Base::Console().Log("ViewProviderMeasure::ViewProviderMeasure: Could not get active view\n"); + } + + if (view) { + Gui::View3DInventorViewer* viewer = view->getViewer(); + auto renderManager = viewer->getSoRenderManager(); + auto cam = renderManager->getCamera(); + pDraggerOrientation->rotation.connectFrom(&cam->orientation); + } + + +} + +ViewProviderMeasure::~ViewProviderMeasure() +{ + pCoords->unref(); + pLines->unref(); +} + +void ViewProviderMeasure::positionAnno(const Measure::MeasureBase* measureObject) { + (void)measureObject; + + // Initialize the text position + Base::Vector3d textPos = getTextPosition(); + auto srcVec = SbVec3f(textPos.x, textPos.y, textPos.z); + + // Translate the position by the local dragger matrix (pDraggerOrientation) + Gui::View3DInventor* view = nullptr; + try { + view = dynamic_cast(this->getActiveView()); + } catch (const Base::RuntimeError& e) { + Base::Console().Log("ViewProviderMeasure::positionAnno: Could not get active view\n"); + } + + if(!view){ + return; + } + + Gui::View3DInventorViewer* viewer = view->getViewer(); + auto gma = SoGetMatrixAction(viewer->getSoRenderManager()->getViewportRegion()); + gma.apply(pDraggerOrientation); + auto mat = gma.getMatrix(); + SbVec3f destVec(0, 0, 0); + mat.multVecMatrix(srcVec, destVec); + + setLabelTranslation(destVec); + updateView(); +} + +void ViewProviderMeasure::onChanged(const App::Property* prop) +{ + if (pcObject == nullptr) { + return; + } + + ViewProviderMeasureBase::onChanged(prop); +} + + +//! repaint the annotation +void ViewProviderMeasure::redrawAnnotation() +{ + // point on element + Base::Vector3d basePos = getBasePosition(); + pcTransform->translation.setValue(SbVec3f(basePos.x, basePos.y, basePos.z)); + + setLabelValue(getMeasureObject()->getResultString()); + + ViewProviderMeasureBase::redrawAnnotation(); + ViewProviderDocumentObject::updateView(); +} + + +Base::Vector3d ViewProviderMeasure::getBasePosition(){ + auto measureObject = getMeasureObject(); + Base::Placement placement = measureObject->getPlacement(); + return placement.getPosition(); +} + + +Base::Vector3d ViewProviderMeasure::getTextPosition(){ + constexpr float DefaultLeaderLength{20.0}; + auto basePoint = getBasePosition(); + Base::Vector3d textDirection(1.0, 1.0, 1.0); + textDirection.Normalize(); + + return basePoint + textDirection * DefaultLeaderLength; +} + +//! called by the system when it is time to display this measure +void ViewProviderMeasureBase::show() +{ + if (isSubjectVisible()) { + // only show the annotation if the subject is visible. + // this avoids disconnected annotations floating in space. + ViewProviderDocumentObject::show(); + } +} diff --git a/src/Mod/Measure/Gui/ViewProviderMeasureBase.h b/src/Mod/Measure/Gui/ViewProviderMeasureBase.h new file mode 100644 index 0000000000..42832ca035 --- /dev/null +++ b/src/Mod/Measure/Gui/ViewProviderMeasureBase.h @@ -0,0 +1,159 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef GUI_VIEWPROVIDER_MEASUREMENTBASE_H +#define GUI_VIEWPROVIDER_MEASUREMENTBASE_H + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +//NOLINTBEGIN +class SbVec2s; +class SoFontStyle; +class SoBaseColor; +class SoText2; +class SoTranslation; +class SoPickStyle; +class SoCoordinate3; +class SoIndexedLineSet; +class SoTranslate2Dragger; +//NOLINTEND + + +namespace MeasureGui { + +//NOLINTBEGIN +class MeasureGuiExport ViewProviderMeasureBase :public Gui::ViewProviderDocumentObject +{ + PROPERTY_HEADER_WITH_OVERRIDE(ViewProviderMeasureBase); + +public: + /// constructor. + ViewProviderMeasureBase(); + + /// destructor. + ~ViewProviderMeasureBase() override; + + // Display properties + App::PropertyColor TextColor; + App::PropertyColor TextBackgroundColor; + App::PropertyColor LineColor; + App::PropertyInteger FontSize; +//NOLINTEND + + /** + * Attaches the document object to this view provider. + */ + bool isPartOfPhysicalObject() const override {return false;}; + void attach(App::DocumentObject *pcObj) override; + void updateData(const App::Property* prop) override; + virtual void positionAnno(const Measure::MeasureBase* measureObject); + void finishRestoring() override; + + bool useNewSelectionModel() const override {return true;} + std::vector getDisplayModes() const override; + void setDisplayMode(const char* ModeName) override; + /// Show the annotation in the 3d window + void show() override; + + virtual void redrawAnnotation(); + Measure::MeasureBase* getMeasureObject(); + + virtual bool isSubjectVisible(); + + static Base::Vector3d toVector3d(SbVec3f svec) { return Base::Vector3d(svec[0], svec[1], svec[2]); } + static SbVec3f toSbVec3f(Base::Vector3d vec3) { return SbVec3f(vec3.x, vec3.y, vec3.z); } + + void onSubjectVisibilityChanged(const App::DocumentObject& docObj, const App::Property& prop); + void connectToSubject(App::DocumentObject* subject); + void connectToSubject(std::vector subject); + +protected: + static void draggerChangedCallback(void* data, SoDragger*); + void onChanged(const App::Property* prop) override; + virtual void onLabelMoved() {}; + void setLabelValue(const Base::Quantity& value); + void setLabelValue(const QString& value); + void setLabelTranslation(const SbVec3f& position); + + SoPickStyle* getSoPickStyle(); + SoDrawStyle* getSoLineStylePrimary(); + SoDrawStyle* getSoLineStyleSecondary(); + SoSeparator* getSoSeparatorText(); + + static constexpr double defaultTolerance = 10e-6; + virtual Base::Vector3d getTextDirection(Base::Vector3d elementDirection, double tolerance = defaultTolerance); + + + // TODO: getters & setters and move variables to private? + bool _mShowTree = true; + + Gui::SoFrameLabel * pLabel; + SoTranslate2Dragger* pDragger; + SoTransform* pDraggerOrientation; + SoTransform * pLabelTranslation; + SoBaseColor * pColor; + SoSeparator* pRootSeparator; + SoSeparator* pTextSeparator; + SoSeparator* pLineSeparator; + SoSeparator* pLineSeparatorSecondary; +private: + boost::signals2::connection _mVisibilityChangedConnection; +}; + +//NOLINTBEGIN +class MeasureGuiExport ViewProviderMeasure : public MeasureGui::ViewProviderMeasureBase +{ + PROPERTY_HEADER_WITH_OVERRIDE(MeasureGui::ViewProviderMeasure); +//NOLINTEND + +public: + /// Constructor + ViewProviderMeasure(); + ~ViewProviderMeasure() override; + + void redrawAnnotation() override; + void positionAnno(const Measure::MeasureBase* measureObject) override; + +protected: + void onChanged(const App::Property* prop) override; + + virtual Base::Vector3d getBasePosition(); + virtual Base::Vector3d getTextPosition(); + +private: + SoCoordinate3 * pCoords; + SoIndexedLineSet * pLines; +}; + +} // namespace Gui + +#endif // GUI_VIEWPROVIDER_MEASUREMENTBASE_H + diff --git a/src/Mod/Measure/Gui/ViewProviderMeasureDistance.cpp b/src/Mod/Measure/Gui/ViewProviderMeasureDistance.cpp new file mode 100644 index 0000000000..019932eebf --- /dev/null +++ b/src/Mod/Measure/Gui/ViewProviderMeasureDistance.cpp @@ -0,0 +1,222 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * Copyright (c) 2008 Werner Mayer * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include + +#include +#include +#include +#include +#include "Mod/Measure/App/MeasureDistance.h" +#include + +#include "ViewProviderMeasureDistance.h" +#include "Gui/Application.h" +#include +#include "Gui/Document.h" +#include "Gui/ViewParams.h" + + +using namespace Gui; +using namespace MeasureGui; +using namespace Measure; + +PROPERTY_SOURCE(MeasureGui::ViewProviderMeasureDistance, MeasureGui::ViewProviderMeasureBase) + + +SbMatrix ViewProviderMeasureDistance::getMatrix() { + auto object = dynamic_cast(getObject()); + auto vec1 = object->Position1.getValue(); + auto vec2 = object->Position2.getValue(); + + const double tolerance(10.0e-6); + SbVec3f origin = toSbVec3f((vec2 + vec1) / 2); + Base::Vector3d localXAxis = (vec2 - vec1).Normalize(); + Base::Vector3d localYAxis = getTextDirection(localXAxis, tolerance).Normalize(); + + // X and Y axis have to be 90° to eachother + assert(fabs(localYAxis.Dot(localXAxis)) < tolerance); + Base::Vector3d localZAxis = localYAxis.Cross(localXAxis).Normalize(); + + SbMatrix matrix = SbMatrix( + localXAxis.x, localXAxis.y, localXAxis.z, 0, + localYAxis.x, localYAxis.y, localYAxis.z ,0, + localZAxis.x, localZAxis.y, localZAxis.z, 0, + // 0,0,0,1 + origin[0], origin[1], origin[2], 1 + ); + + return matrix; +} + + +//! calculate a good direction from the elements being measured to the annotation text based on the layout +//! of the elements and its relationship with the cardinal axes and the view direction. elementDirection +//! is expected to be a normalized vector. +//! an example of an elementDirection would be the vector from the start of a line to the end. +Base::Vector3d ViewProviderMeasureDistance::getTextDirection(Base::Vector3d elementDirection, double tolerance) +{ + const Base::Vector3d stdX(1.0, 0.0, 0.0); + const Base::Vector3d stdY(0.0, 1.0, 0.0); + const Base::Vector3d stdZ(0.0, 0.0, 1.0); + + Base::Vector3d textDirection = elementDirection.Cross(stdX); + if (textDirection.Length() < tolerance) { + textDirection = elementDirection.Cross(stdY); + } + if (textDirection.Length() < tolerance) { + textDirection = elementDirection.Cross(stdZ); + } + textDirection.Normalize(); + if (textDirection.Dot(stdZ) < 0.0) { + textDirection = textDirection * -1.0; + } + + return textDirection.Normalize(); +} + + +ViewProviderMeasureDistance::ViewProviderMeasureDistance() +{ + sPixmap = "umf-measurement"; + + // vert indexes used to create the annotation lines + const size_t lineCount(3); + static const int32_t lines[lineCount] = + { + 2,3,-1 // dimension line + }; + + const size_t lineCountSecondary(9); + static const int32_t linesSecondary[lineCountSecondary] = { + 0,2,-1, // extension line 1 + 1,3,-1, // extension line 2 + 2,4,-1 // label helper line + }; + + // Line Coordinates + // 0-1 points on shape (dimension points) + // 2-3 ends of extension lines/dimension line + // 4 label position + pCoords = new SoCoordinate3(); + pCoords->ref(); + + auto engineCoords = new SoCalculator(); + engineCoords->a.connectFrom(&fieldDistance); + engineCoords->A.connectFrom(&pLabelTranslation->translation); + engineCoords->expression.setValue("ta=a/2; tb=A[1]; oA=vec3f(ta, 0, 0); oB=vec3f(-ta, 0, 0); " + "oC=vec3f(ta, tb, 0); oD=vec3f(-ta, tb, 0)"); + + auto engineCat = new SoConcatenate(SoMFVec3f::getClassTypeId()); + engineCat->input[0]->connectFrom(&engineCoords->oA); + engineCat->input[1]->connectFrom(&engineCoords->oB); + engineCat->input[2]->connectFrom(&engineCoords->oC); + engineCat->input[3]->connectFrom(&engineCoords->oD); + engineCat->input[4]->connectFrom(&pLabelTranslation->translation); + + pCoords->point.connectFrom(engineCat->output); + pCoords->point.setNum(engineCat->output->getNumConnections()); + + pLines = new SoIndexedLineSet(); + pLines->ref(); + pLines->coordIndex.setNum(lineCount); + pLines->coordIndex.setValues(0, lineCount, lines); + + pLineSeparator->addChild(pCoords); + pLineSeparator->addChild(pLines); + + + // Secondary Lines + auto lineSetSecondary = new SoIndexedLineSet(); + lineSetSecondary->coordIndex.setNum(lineCountSecondary); + lineSetSecondary->coordIndex.setValues(0, lineCountSecondary, linesSecondary); + + pLineSeparatorSecondary->addChild(pCoords); + pLineSeparatorSecondary->addChild(lineSetSecondary); + + auto points = new SoMarkerSet(); + points->markerIndex = Gui::Inventor::MarkerBitmaps::getMarkerIndex("CROSS", + ViewParams::instance()->getMarkerSize()); + points->numPoints=2; + pLineSeparator->addChild(points); +} + +ViewProviderMeasureDistance::~ViewProviderMeasureDistance() +{ + pCoords->unref(); + pLines->unref(); +} + + +Measure::MeasureDistance* ViewProviderMeasureDistance::getMeasureDistance() +{ + Measure::MeasureDistance* feature = dynamic_cast(pcObject); + if (!feature) { + throw Base::RuntimeError("Feature not found for ViewProviderMeasureDistance"); + } + return feature; +} + +//! repaint the annotation +void ViewProviderMeasureDistance::redrawAnnotation() +{ + auto object = dynamic_cast(getObject()); + auto vec1 = object->Position1.getValue(); + auto vec2 = object->Position2.getValue(); + + // Set the distance + fieldDistance = (vec2 - vec1).Length(); + + setLabelValue(object->Distance.getQuantityValue().getUserString()); + + // Set matrix + SbMatrix matrix = getMatrix(); + pcTransform->setMatrix(matrix); + + ViewProviderMeasureBase::redrawAnnotation(); + updateView(); +} + + +void ViewProviderMeasureDistance::positionAnno(const Measure::MeasureBase* measureObject) { + (void)measureObject; + setLabelTranslation(SbVec3f(0, 10, 0)); +} diff --git a/src/Mod/Measure/Gui/ViewProviderMeasureDistance.h b/src/Mod/Measure/Gui/ViewProviderMeasureDistance.h new file mode 100644 index 0000000000..45551e1231 --- /dev/null +++ b/src/Mod/Measure/Gui/ViewProviderMeasureDistance.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + + +#ifndef MEASUREGUI_VIEWPROVIDERMEASUREDISTANCE_H +#define MEASUREGUI_VIEWPROVIDERMEASUREDISTANCE_H + +#include + +#include + +#include + +#include "ViewProviderMeasureBase.h" + +class SoCoordinate3; +class SoIndexedLineSet; + +namespace MeasureGui +{ + + +class MeasureGuiExport ViewProviderMeasureDistance : public MeasureGui::ViewProviderMeasureBase +{ + PROPERTY_HEADER_WITH_OVERRIDE(MeasureGui::ViewProviderMeasureDistance); + +public: + /// Constructor + ViewProviderMeasureDistance(); + ~ViewProviderMeasureDistance() override; + + Measure::MeasureDistance* getMeasureDistance(); + void redrawAnnotation() override; + void positionAnno(const Measure::MeasureBase* measureObject) override; + +protected: + Base::Vector3d getTextDirection(Base::Vector3d elementDirection, double tolerance = defaultTolerance) override; + +private: + SoCoordinate3 * pCoords; + SoIndexedLineSet * pLines; + + SoSFFloat fieldDistance; + + SbMatrix getMatrix(); +}; + +} //namespace MeasureGui + + +#endif // MEASUREGUI_VIEWPROVIDERMEASUREDISTANCE_H diff --git a/src/Mod/Measure/InitGui.py b/src/Mod/Measure/InitGui.py new file mode 100644 index 0000000000..22952279b8 --- /dev/null +++ b/src/Mod/Measure/InitGui.py @@ -0,0 +1,43 @@ +# /*************************************************************************** +# * Copyright (c) 2023 David Friedli * +# * * +# * This file is part of FreeCAD. * +# * * +# * FreeCAD is free software: you can redistribute it and/or modify it * +# * under the terms of the GNU Lesser General Public License as * +# * published by the Free Software Foundation, either version 2.1 of the * +# * License, or (at your option) any later version. * +# * * +# * FreeCAD 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 * +# * Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with FreeCAD. If not, see * +# * . * +# * * +# **************************************************************************/ + + +# Import the measure module here in order to ensure that default measurement types are loaded during startup. +# Note that they won't auto-load in gui-less mode. Ideally they would be loaded from "Init.py", similar +# to how the import/export types are registered. This would require to register measurement types from +# python which could complicate things further. + +import Measure +from MeasureCOM import makeMeasureCOM, MeasureCOM + + +# Expose create functions +Measure.makeMeasureCOM = makeMeasureCOM + + +# Register python measure types +import FreeCAD +FreeCAD.MeasureManager.addMeasureType( + "CENTEROFMASS", + "Center of Mass", + MeasureCOM, +) + diff --git a/src/Mod/Measure/MeasureCOM.py b/src/Mod/Measure/MeasureCOM.py new file mode 100644 index 0000000000..39e73df5cf --- /dev/null +++ b/src/Mod/Measure/MeasureCOM.py @@ -0,0 +1,152 @@ +# /*************************************************************************** +# * Copyright (c) 2023 David Friedli * +# * * +# * This file is part of FreeCAD. * +# * * +# * FreeCAD is free software: you can redistribute it and/or modify it * +# * under the terms of the GNU Lesser General Public License as * +# * published by the Free Software Foundation, either version 2.1 of the * +# * License, or (at your option) any later version. * +# * * +# * FreeCAD 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 * +# * Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with FreeCAD. If not, see * +# * . * +# * * +# **************************************************************************/ + +import FreeCAD +from FreeCAD import Units, Placement +from UtilsMeasure import MeasureBasePython +from PySide.QtCore import QT_TRANSLATE_NOOP + + + +__title__="Measure Center of Mass Object" +__author__ = "David Friedli" +__url__ = "http://www.freecad.org" + + + +""" + The Measure cpp object defines a result and a placement property. The Python measure type + adds it's own specific properties. Once the object is recomputed the parent properties are updated + based on the specific python properties. + + We'll need some kind of interface for the measure command which exposes "parseSelection", "isValidSelection" etc. + +""" + + + +def makeMeasureCOM(name="CenterOfMass"): + '''makeMeasureCOM(name): make a CenterofMass measurement''' + obj = FreeCAD.ActiveDocument.addObject("Measure::MeasurePython", name) + MeasureCOM(obj) + return obj + + +class MeasureCOM(MeasureBasePython): + "The MeasureCOM object" + + def __init__(self, obj): + obj.Proxy = self + + obj.addProperty("App::PropertyLinkSubGlobal", "Element", "", QT_TRANSLATE_NOOP("App::Property", "Element to measure")) + obj.addProperty("App::PropertyPosition", "Result", "", QT_TRANSLATE_NOOP("App::PropertyVector", "The result location")) + + @classmethod + def isValidSelection(cls, selection): + if not len(selection) == 1: + return False + + element = selection[0] + ob = element["object"] + subName = element["subName"] + + if not ob: + return + + sub = ob.getSubObject(subName) + if not sub: + return + + if not hasattr(sub, "CenterOfMass"): + return + + return True + + @classmethod + def isPrioritySelection(cls, selection): + return False + + @classmethod + def getInputProps(cls): + return ("Element",) + + def getSubject(self, obj): + if not obj: + return () + + element = obj.Element + if not element: + return () + + ob = element[0] + if not ob: + return () + return (ob,) + + + def parseSelection(self, obj, selection): + item = selection[0] + o = item["object"] + obj.Element = (o, item["subName"]) + + + def getResultString(self, obj): + values = [Units.Quantity(v, Units.Length).getUserPreferred()[0] for v in obj.Result] + return "COM\nX: {}\nY: {}\nZ: {}".format(*values) + + def execute(self, obj): + element = obj.Element + if not element: + return + + ob = element[0] + subElements = element[1] + + if subElements: + subName = subElements[0] + sub = ob.getSubObject(subName) + + if not sub or not hasattr(sub, "CenterOfMass"): + return + com = sub.CenterOfMass + + else: + # Get Center of Mass of the object + if not hasattr(ob, "Shape"): + return + + shape = ob.Shape + if not hasattr(shape, "CenterOfMass"): + return + + com = shape.CenterOfMass + + obj.Result = com + placement = Placement() + placement.Base = com + obj.Placement = placement + + + def onChanged(self, obj, prop): + '''Do something when a property has changed''' + + if prop == "Element": + self.execute(obj) diff --git a/src/Mod/Measure/MeasureGlobal.h b/src/Mod/Measure/MeasureGlobal.h index 3df4317b45..5a935a8413 100644 --- a/src/Mod/Measure/MeasureGlobal.h +++ b/src/Mod/Measure/MeasureGlobal.h @@ -29,9 +29,18 @@ // Measure #ifndef MeasureExport #ifdef Measure_EXPORTS -# define MeasureExport FREECAD_DECL_EXPORT +#define MeasureExport FREECAD_DECL_EXPORT #else -# define MeasureExport FREECAD_DECL_IMPORT +#define MeasureExport FREECAD_DECL_IMPORT +#endif +#endif + +// MeasureGui +#ifndef MeasureGuiExport +#ifdef MeasureGui_EXPORTS +# define MeasureGuiExport FREECAD_DECL_EXPORT +#else +# define MeasureGuiExport FREECAD_DECL_IMPORT #endif #endif diff --git a/src/Mod/Measure/UtilsMeasure.py b/src/Mod/Measure/UtilsMeasure.py new file mode 100644 index 0000000000..d013c8d5f0 --- /dev/null +++ b/src/Mod/Measure/UtilsMeasure.py @@ -0,0 +1,52 @@ +# /*************************************************************************** +# * Copyright (c) 2023 David Friedli * +# * * +# * This file is part of FreeCAD. * +# * * +# * FreeCAD is free software: you can redistribute it and/or modify it * +# * under the terms of the GNU Lesser General Public License as * +# * published by the Free Software Foundation, either version 2.1 of the * +# * License, or (at your option) any later version. * +# * * +# * FreeCAD 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 * +# * Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Lesser General Public * +# * License along with FreeCAD. If not, see * +# * . * +# * * +# **************************************************************************/ + +from abc import ABC, abstractmethod, abstractclassmethod +from typing import List, Tuple + +class MeasureBasePython(ABC): + + @abstractclassmethod + def isValidSelection(cls, selection): + """Returns True if the given selection is valid for this measurment""" + pass + + @abstractclassmethod + def isPrioritySelection(cls, selection): + """Returns True if creation of this measurment should be priorized over other measurements for the given selection""" + pass + + @abstractclassmethod + def getInputProps(cls) -> Tuple[str]: + """Returns all properties that the measurement's result depends on""" + return () + + @abstractmethod + def getSubject(self, obj) -> Tuple: + """Returns all objects that are measured, this is used to autohide the measurement if the relevant elements are not visible""" + return [] + + @abstractmethod + def parseSelection(self, obj, selection): + """Sets the measurements properties from the given selection""" + pass + + diff --git a/src/Mod/Part/App/Measure.cpp b/src/Mod/Part/App/MeasureClient.cpp similarity index 57% rename from src/Mod/Part/App/Measure.cpp rename to src/Mod/Part/App/MeasureClient.cpp index e46b01261f..0c099a78a0 100644 --- a/src/Mod/Part/App/Measure.cpp +++ b/src/Mod/Part/App/MeasureClient.cpp @@ -1,24 +1,25 @@ /*************************************************************************** - * Copyright (c) 2023 David Friedli * + * Copyright (c) 2023 Wandererfan * + * Copyright (c) 2023 Joel Meijering (EDG5000) * + * Copyright (c) 2023 David Friedli * * * - * This file is part of the FreeCAD CAx development system. * + * This file is part of FreeCAD. * * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Library General Public * - * License as published by the Free Software Foundation; either * - * version 2 of the License, or (at your option) any later version. * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * * * - * This library 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 Library General Public License for more details. * + * FreeCAD 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 * + * Lesser General Public License for more details. * * * - * You should have received a copy of the GNU Library General Public * - * License along with this library; see the file COPYING.LIB. If not, * - * write to the Free Software Foundation, Inc., 59 Temple Place, * - * Suite 330, Boston, MA 02111-1307, USA * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * * * - ***************************************************************************/ + **************************************************************************/ #include "PreCompiled.h" @@ -39,11 +40,14 @@ #include #include #include -#include +#include +#include #include #include #include +#include +#include #include #include #include @@ -59,7 +63,9 @@ #include "VectorAdapter.h" #include "PartFeature.h" -#include "Measure.h" +#include "MeasureClient.h" + +using namespace Part; // From: https://github.com/Celemation/FreeCAD/blob/joel_selection_summary_demo/src/Gui/SelectionSummary.cpp @@ -90,11 +96,16 @@ static float getRadius(TopoDS_Shape& edge){ return 0.0; } -TopoDS_Shape getLocatedShape(const std::string& objectName, const std::string& subName) +TopoDS_Shape getLocatedShape(const App::SubObjectT& subject) { - App::DocumentObject* obj = App::GetApplication().getActiveDocument()->getObject(objectName.c_str()); - // should this be getTopoShape(obj, subName, true)? + App::DocumentObject* obj = subject.getObject(); + if (!obj) { + return {}; + } Part::TopoShape shape = Part::Feature::getTopoShape(obj); + if (shape.isNull()) { + return {}; + } auto geoFeat = dynamic_cast(obj); if (geoFeat) { shape.setPlacement(geoFeat->globalPlacement()); @@ -105,22 +116,19 @@ TopoDS_Shape getLocatedShape(const std::string& objectName, const std::string& s return shape.getShape(); } - if (subName.empty()) - { + if (!subject.getElementName()) { return shape.getShape(); } - return shape.getSubShape(subName.c_str()); + return shape.getSubShape(subject.getElementName(), true); } -App::MeasureElementType PartMeasureTypeCb(const char* objectName, const char* subName) { - App::DocumentObject* ob = App::GetApplication().getActiveDocument()->getObject(objectName); -// auto sub = ob->getSubObject(subName); - +App::MeasureElementType PartMeasureTypeCb(App::DocumentObject* ob, const char* subName) +{ TopoDS_Shape shape = Part::Feature::getShape(ob, subName, true); if (shape.IsNull()) { // failure here on loading document with existing measurement. - Base::Console().Message("Part::VectorAdapter did not retrieve shape for %s, %s\n", objectName, subName); + Base::Console().Message("Part::PartMeasureTypeCb did not retrieve shape for %s, %s\n", ob->getNameInDocument(), subName); return App::MeasureElementType(); } TopAbs_ShapeEnum shapeType = shape.ShapeType(); @@ -168,31 +176,29 @@ App::MeasureElementType PartMeasureTypeCb(const char* objectName, const char* su } - -bool getShapeFromStrings(TopoDS_Shape &shapeOut, const std::string &doc, const std::string &object, const std::string &sub, Base::Matrix4D *mat) +bool getShapeFromStrings(TopoDS_Shape &shapeOut, const App::SubObjectT& subject, Base::Matrix4D *mat) { - App::Document *docPointer = App::GetApplication().getDocument(doc.c_str()); - if (!docPointer) { - return false; - } - App::DocumentObject *objectPointer = docPointer->getObject(object.c_str()); - if (!objectPointer) { - return false; - } - shapeOut = Part::Feature::getShape(objectPointer,sub.c_str(),true,mat); - return !shapeOut.IsNull(); + App::DocumentObject *obj = subject.getObject(); + if (!obj) { + return {}; + } + shapeOut = Part::Feature::getShape(obj, subject.getElementName(), true, mat); + return !shapeOut.IsNull(); } -Part::VectorAdapter buildAdapter(const App::DocumentObject* ob, std::string* objectName, const std::string* subName) +Part::VectorAdapter buildAdapter(const App::SubObjectT& subject) { - (void) objectName; + if (!subject.getObject()) { + return Part::VectorAdapter(); + } Base::Matrix4D mat; - TopoDS_Shape shape = Part::Feature::getShape(ob, subName->c_str(), true); + TopoDS_Shape shape = Part::Feature::getShape(subject.getObject(), subject.getElementName(), true); if (shape.IsNull()) { // failure here on loading document with existing measurement. - Base::Console().Message("Part::VectorAdapter did not retrieve shape for %s, %s\n", objectName->c_str(), subName->c_str()); + Base::Console().Message("Part::buildAdapter did not retrieve shape for %s, %s\n", + subject.getObjectName(), subject.getElementName()); return Part::VectorAdapter(); } @@ -202,7 +208,7 @@ Part::VectorAdapter buildAdapter(const App::DocumentObject* ob, std::string* obj if (shapeType == TopAbs_EDGE) { TopoDS_Shape edgeShape; - if (!getShapeFromStrings(edgeShape, ob->getDocument()->getName(), ob->getNameInDocument(), *subName, &mat)) { + if (!getShapeFromStrings(edgeShape, subject, &mat)) { return {}; } TopoDS_Edge edge = TopoDS::Edge(edgeShape); @@ -233,7 +239,7 @@ Part::VectorAdapter buildAdapter(const App::DocumentObject* ob, std::string* obj if (shapeType == TopAbs_FACE) { TopoDS_Shape faceShape; - if (!getShapeFromStrings(faceShape, ob->getDocument()->getName(), ob->getNameInDocument(), *subName, &mat)) { + if (!getShapeFromStrings(faceShape, subject, &mat)) { return {}; } @@ -248,18 +254,20 @@ Part::VectorAdapter buildAdapter(const App::DocumentObject* ob, std::string* obj } -Measure::MeasureLengthInfo MeasureLengthHandler(std::string* objectName, std::string* subName){ - TopoDS_Shape shape = getLocatedShape(*objectName, *subName); +MeasureLengthInfoPtr MeasureLengthHandler(const App::SubObjectT& subject) +{ + TopoDS_Shape shape = getLocatedShape(subject); if (shape.IsNull()) { // failure here on loading document with existing measurement. - Base::Console().Message("MeasureLengthHandler did not retrieve shape for %s, %s\n", objectName->c_str(), subName->c_str()); - return {false, 0.0, Base::Matrix4D()}; + Base::Console().Message("MeasureLengthHandler did not retrieve shape for %s, %s\n", + subject.getObjectName(), subject.getElementName()); + return std::make_shared(false, 0.0, Base::Matrix4D()); } TopAbs_ShapeEnum sType = shape.ShapeType(); if (sType != TopAbs_EDGE) { - return {false, 0.0, Base::Matrix4D()}; + return std::make_shared(false, 0.0, Base::Matrix4D()); } // Get Center of mass as the attachment point of the label @@ -278,22 +286,23 @@ Measure::MeasureLengthInfo MeasureLengthHandler(std::string* objectName, std::st Base::Rotation rot(axisUp, elementDirection); Base::Placement placement(Base::Vector3d(origin.X(), origin.Y(), origin.Z()), rot); - return {true, getLength(shape), placement}; + return std::make_shared(true, getLength(shape), placement); } -Measure::MeasureRadiusInfo MeasureRadiusHandler(std::string* objectName, std::string* subName){ +MeasureRadiusInfoPtr MeasureRadiusHandler(const App::SubObjectT& subject) +{ Base::Placement placement; // curve center + orientation Base::Vector3d pointOnCurve; - TopoDS_Shape shape = getLocatedShape(*objectName, *subName); + TopoDS_Shape shape = getLocatedShape(subject); if (shape.IsNull()) { - return { false, 0.0, pointOnCurve, placement}; + return std::make_shared( false, 0.0, pointOnCurve, placement); } TopAbs_ShapeEnum sType = shape.ShapeType(); if (sType != TopAbs_EDGE) { - return { false, 0.0, pointOnCurve, placement}; + return std::make_shared( false, 0.0, pointOnCurve, placement); } // Get Center of mass as the attachment point of the label @@ -312,22 +321,24 @@ Measure::MeasureRadiusInfo MeasureRadiusHandler(std::string* objectName, std::st placement = Base::Placement(Base::Vector3d(origin.X(), origin.Y(), origin.Z()), rot); - return { true, getRadius(shape), pointOnCurve, placement}; + return std::make_shared( true, getRadius(shape), pointOnCurve, placement); } -Measure::MeasureAreaInfo MeasureAreaHandler(std::string* objectName, std::string* subName){ - TopoDS_Shape shape = getLocatedShape(*objectName, *subName); +MeasureAreaInfoPtr MeasureAreaHandler(const App::SubObjectT& subject) +{ + TopoDS_Shape shape = getLocatedShape(subject); if (shape.IsNull()) { // failure here on loading document with existing measurement. - Base::Console().Message("MeasureLengthHandler did not retrieve shape for %s, %s\n", objectName->c_str(), subName->c_str()); - return {false, 0.0, Base::Matrix4D()}; + Base::Console().Message("MeasureLengthHandler did not retrieve shape for %s, %s\n", + subject.getObjectName(), subject.getElementName()); + return std::make_shared(false, 0.0, Base::Matrix4D()); } TopAbs_ShapeEnum sType = shape.ShapeType(); if (sType != TopAbs_FACE) { - return {false, 0.0, Base::Matrix4D()}; + return std::make_shared(false, 0.0, Base::Matrix4D()); } // Get Center of mass as the attachment point of the label @@ -338,41 +349,45 @@ Measure::MeasureAreaInfo MeasureAreaHandler(std::string* objectName, std::string // TODO: Center of Mass might not lie on the surface, somehow snap to the closest point on the surface? Base::Placement placement(Base::Vector3d(origin.X(), origin.Y(), origin.Z()), Base::Rotation()); - return {true, getFaceArea(shape), placement}; + return std::make_shared(true, getFaceArea(shape), placement); } -Base::Vector3d MeasurePositionHandler(std::string* objectName, std::string* subName) { - TopoDS_Shape shape = getLocatedShape(*objectName, *subName); +MeasurePositionInfoPtr MeasurePositionHandler(const App::SubObjectT& subject) +{ + TopoDS_Shape shape = getLocatedShape(subject); if (shape.IsNull()) { - Base::Console().Message("MeasurePositionHandler did not retrieve shape for %s, %s\n", objectName->c_str(), subName->c_str()); - return Base::Vector3d(); + Base::Console().Message("MeasurePositionHandler did not retrieve shape for %s, %s\n", + subject.getObjectName(), subject.getElementName()); + return std::make_shared(false, Base::Vector3d()); } TopAbs_ShapeEnum sType = shape.ShapeType(); if (sType != TopAbs_VERTEX) { - return Base::Vector3d(); + return std::make_shared(false, Base::Vector3d()); } TopoDS_Vertex vertex = TopoDS::Vertex(shape); auto point = BRep_Tool::Pnt(vertex); - return Base::Vector3d(point.X(), point.Y(), point.Z()); + return std::make_shared( true, Base::Vector3d(point.X(), point.Y(), point.Z())); } -Measure::MeasureAngleInfo MeasureAngleHandler(std::string* objectName, std::string* subName) { - App::DocumentObject* ob = App::GetApplication().getActiveDocument()->getObject(objectName->c_str()); - TopoDS_Shape shape = getLocatedShape(*objectName, *subName); +MeasureAngleInfoPtr MeasureAngleHandler(const App::SubObjectT& subject) +{ + TopoDS_Shape shape = getLocatedShape(subject); + if (shape.IsNull()) { // failure here on loading document with existing measurement. - Base::Console().Message("MeasureAngleHandler did not retrieve shape for %s, %s\n", objectName->c_str(), subName->c_str()); - return Measure::MeasureAngleInfo(); + Base::Console().Message("MeasureAngleHandler did not retrieve shape for %s, %s\n", + subject.getObjectName(), subject.getElementName()); + return std::make_shared(); } TopAbs_ShapeEnum sType = shape.ShapeType(); - Part::VectorAdapter vAdapt = buildAdapter(ob, objectName, subName); + Part::VectorAdapter vAdapt = buildAdapter(subject); gp_Pnt vec; Base::Vector3d position; @@ -393,48 +408,89 @@ Measure::MeasureAngleInfo MeasureAngleHandler(std::string* objectName, std::stri position.Set(vec.X(), vec.Y(), vec.Z()); - Measure::MeasureAngleInfo info = {vAdapt.isValid(), (Base::Vector3d)vAdapt, position}; + auto info = std::make_shared(vAdapt.isValid(), (Base::Vector3d)vAdapt, position); return info; } -Measure::MeasureDistanceInfo MeasureDistanceHandler(std::string* objectName, std::string* subName) { - TopoDS_Shape shape = getLocatedShape(*objectName, *subName); +MeasureDistanceInfoPtr MeasureDistanceHandler(const App::SubObjectT& subject) +{ + TopoDS_Shape shape = getLocatedShape(subject); if (shape.IsNull()) { // failure here on loading document with existing measurement. - Base::Console().Message("MeasureDistanceHandler did not retrieve shape for %s, %s\n", objectName->c_str(), subName->c_str()); - return Measure::MeasureDistanceInfo(); + Base::Console().Message("MeasureDistanceHandler did not retrieve shape for %s, %s\n", + subject.getObjectName(), subject.getElementName()); + return std::make_shared(); } - // Just return the TopoDS_Shape here - return {true, shape}; + // return a persistent copy of the TopoDS_Shape here as shape will go out of scope at end + BRepBuilderAPI_Copy copy(shape); + const TopoDS_Shape* newShape = new TopoDS_Shape(copy.Shape()); + return std::make_shared(true, newShape); } -using namespace Measure; +void Part::MeasureClient::initialize() { + App::MeasureManager::addMeasureHandler("Part", PartMeasureTypeCb); -void Part::Measure::initialize() { - - App::Application& app = App::GetApplication(); - app.addMeasureHandler("Part", PartMeasureTypeCb); - - std::vector proxyList( { "Part", "PartDesign", "Sketcher" } ); - // Extend MeasureLength - MeasureLength::addGeometryHandlers(proxyList, MeasureLengthHandler); - - // Extend MeasurePosition - MeasurePosition::addGeometryHandlers(proxyList, MeasurePositionHandler); - - // Extend MeasureArea - MeasureArea::addGeometryHandlers(proxyList, MeasureAreaHandler); - - // Extend MeasureAngle - MeasureAngle::addGeometryHandlers(proxyList, MeasureAngleHandler); - - // Extend MeasureDistance - MeasureDistance::addGeometryHandlers(proxyList, MeasureDistanceHandler); - - // Extend MeasureRadius - MeasureRadius::addGeometryHandlers(proxyList, MeasureRadiusHandler); } + +Part::CallbackRegistrationList Part::MeasureClient::reportLengthCB() +{ + CallbackRegistrationList callbacks; + callbacks.emplace_back("Part", "Length", MeasureLengthHandler); + callbacks.emplace_back("PartDesign", "Length", MeasureLengthHandler); + callbacks.emplace_back("Sketcher", "Length", MeasureLengthHandler); + return callbacks; +} + +Part::CallbackRegistrationList Part::MeasureClient::reportPositionCB() +{ + CallbackRegistrationList callbacks; + callbacks.emplace_back("Part", "Position", MeasurePositionHandler); + callbacks.emplace_back("PartDesign", "Position", MeasurePositionHandler); + callbacks.emplace_back("Sketcher", "Position", MeasurePositionHandler); + return callbacks; +} + +Part::CallbackRegistrationList Part::MeasureClient::reportAreaCB() +{ + CallbackRegistrationList callbacks; + callbacks.emplace_back("Part", "Area", MeasureAreaHandler); + callbacks.emplace_back("PartDesign", "Area", MeasureAreaHandler); + callbacks.emplace_back("Sketcher", "Area", MeasureAreaHandler); + return callbacks; +} + + +Part::CallbackRegistrationList Part::MeasureClient::reportAngleCB() +{ + CallbackRegistrationList callbacks; + callbacks.emplace_back("Part", "Angle", MeasureAngleHandler); + callbacks.emplace_back("PartDesign", "Angle", MeasureAngleHandler); + callbacks.emplace_back("Sketcher", "Angle", MeasureAngleHandler); + return callbacks; +} + + +Part::CallbackRegistrationList Part::MeasureClient::reportDistanceCB() +{ + CallbackRegistrationList callbacks; + callbacks.emplace_back("Part", "Distance", MeasureDistanceHandler); + callbacks.emplace_back("PartDesign", "Distance", MeasureDistanceHandler); + callbacks.emplace_back("Sketcher", "Distance", MeasureDistanceHandler); + return callbacks; +} + + +Part::CallbackRegistrationList Part::MeasureClient::reportRadiusCB() +{ + CallbackRegistrationList callbacks; + callbacks.emplace_back("Part", "Radius", MeasureRadiusHandler); + callbacks.emplace_back("PartDesign", "Radius", MeasureRadiusHandler); + callbacks.emplace_back("Sketcher", "Radius", MeasureRadiusHandler); + return callbacks; +} + +