From 2acf5ccab081629d5ae7c396ec6563430c0e71b1 Mon Sep 17 00:00:00 2001 From: hlorus Date: Sun, 14 Jan 2024 20:58:33 +0100 Subject: [PATCH] [App] Add registration functions for unified measurement facility --- src/App/Application.cpp | 3 + src/App/CMakeLists.txt | 5 + src/App/MeasureManager.cpp | 180 ++++++++++++++++++++++++++++++++ src/App/MeasureManager.h | 112 ++++++++++++++++++++ src/App/MeasureManagerPy.xml | 43 ++++++++ src/App/MeasureManagerPyImp.cpp | 79 ++++++++++++++ 6 files changed, 422 insertions(+) create mode 100644 src/App/MeasureManager.cpp create mode 100644 src/App/MeasureManager.h create mode 100644 src/App/MeasureManagerPy.xml create mode 100644 src/App/MeasureManagerPyImp.cpp diff --git a/src/App/Application.cpp b/src/App/Application.cpp index e092f1d5c4..49646c5eb0 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -102,6 +102,7 @@ #include "VarSet.h" #include "MaterialObject.h" #include "MeasureDistance.h" +#include "MeasureManagerPy.h" #include "Origin.h" #include "OriginFeature.h" #include "OriginGroupExtension.h" @@ -314,6 +315,8 @@ void Application::setupPythonTypes() Base::Interpreter().addType(&App::MaterialPy::Type, pAppModule, "Material"); Base::Interpreter().addType(&App::MetadataPy::Type, pAppModule, "Metadata"); + Base::Interpreter().addType(&App::MeasureManagerPy::Type, pAppModule, "MeasureManager"); + Base::Interpreter().addType(&App::StringHasherPy::Type, pAppModule, "StringHasher"); Base::Interpreter().addType(&App::StringIDPy::Type, pAppModule, "StringID"); diff --git a/src/App/CMakeLists.txt b/src/App/CMakeLists.txt index c809a0aeec..e8a8dfc93a 100644 --- a/src/App/CMakeLists.txt +++ b/src/App/CMakeLists.txt @@ -97,6 +97,7 @@ generate_from_xml(StringIDPy) generate_from_xml(ComplexGeoDataPy) generate_from_xml(PropertyContainerPy) generate_from_xml(MaterialPy) +generate_from_xml(MeasureManagerPy) generate_from_py(FreeCADInit InitScript.h) generate_from_py(FreeCADTest TestScript.h) @@ -119,6 +120,7 @@ SET(FreeCADApp_XML_SRCS PropertyContainerPy.xml ComplexGeoDataPy.xml MaterialPy.xml + MeasureManagerPy.xml StringHasherPy.xml StringIDPy.xml ) @@ -282,6 +284,8 @@ SET(FreeCADApp_CPP_SRCS MappedName.cpp Material.cpp MaterialPyImp.cpp + MeasureManager.cpp + MeasureManagerPyImp.cpp Metadata.cpp MetadataPyImp.cpp ElementNamingUtils.cpp @@ -305,6 +309,7 @@ SET(FreeCADApp_HPP_SRCS MappedName.h MappedElement.h Material.h + MeasureManager.h Metadata.h ElementNamingUtils.h StringHasher.h diff --git a/src/App/MeasureManager.cpp b/src/App/MeasureManager.cpp new file mode 100644 index 0000000000..bcf996b103 --- /dev/null +++ b/src/App/MeasureManager.cpp @@ -0,0 +1,180 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * 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 +#include +#include +#include + +#include "MeasureManager.h" + +namespace App { + + std::vector MeasureManager::_mMeasureHandlers; + std::vector MeasureManager::_mMeasureTypes; + + MeasureManager::MeasureManager() + { + // Constructor implementation + } + + + void MeasureManager::addMeasureHandler(const char* module, MeasureTypeMethod typeCb) { + auto item = new MeasureHandler{module, typeCb}; + _mMeasureHandlers.push_back(*item); + } + + bool MeasureManager::hasMeasureHandler(const char* module) { + for(MeasureHandler& handler : _mMeasureHandlers) { + if (strcmp(handler.module.c_str(), module) == 0) { + return true; + } + } + return false; + } + + MeasureHandler MeasureManager::getMeasureHandler(const char* module) { + for(MeasureHandler handler : _mMeasureHandlers) { + if (!strcmp(handler.module.c_str(), module)) { + return handler; + } + } + + MeasureHandler empty; + return empty; + } + + void MeasureManager::addMeasureType(MeasureType* measureType) { + _mMeasureTypes.push_back(measureType); + } + + void MeasureManager::addMeasureType(std::string id, std::string label, std::string measureObj, MeasureValidateMethod validatorCb, MeasurePrioritizeMethod prioritizeCb) { + MeasureType* mType = new MeasureType{id, label, measureObj, validatorCb, prioritizeCb, false, nullptr}; + _mMeasureTypes.push_back(mType); + } + + void MeasureManager::addMeasureType(const char* id, const char* label, const char* measureObj, MeasureValidateMethod validatorCb, MeasurePrioritizeMethod prioritizeCb) { + addMeasureType(std::string(id), std::string(label), std::string(measureObj), validatorCb, prioritizeCb); + } + + const std::vector MeasureManager::getMeasureTypes() { + return _mMeasureTypes; + } + + + Py::Tuple MeasureManager::getSelectionPy(const App::MeasureSelection& selection) { + // Convert selection to python list + Py::Tuple selectionPy(selection.size()); + + int i = 0; + for (auto it : selection) { + + Py::Dict sel; + sel.setItem("object", Py::asObject(it.object.getObject()->getPyObject())); + sel.setItem("subName", Py::String(it.object.getSubName())); + sel.setItem("pickedPoint", Py::asObject(new Base::VectorPy(it.pickedPoint))); + + selectionPy.setItem(i, sel); + + i++; + } + return selectionPy; + } + + + std::vector MeasureManager::getValidMeasureTypes(App::MeasureSelection selection, std::string mode) { + Base::PyGILStateLocker lock; + + // Convert selection to python list + Py::Tuple selectionPy = getSelectionPy(selection); + + // Store valid measure types + std::vector validTypes; + std::pair(); + + + // Loop through measure types and check if they work with given selection + for (App::MeasureType* mType : getMeasureTypes()){ + + if (mode != "" && mType->label != mode) { + continue; + } + + + if (mType->isPython) { + // Parse Python measure types + auto measurePyClass = Py::Object(mType->pythonClass); + + Py::Tuple args(1); + args.setItem(0, selectionPy); + + Py::Object isValid; + try { + isValid = measurePyClass.callMemberFunction(std::string("isValidSelection"), args); + } catch (const Py::Exception&) { + Base::PyException e; + e.ReportException(); + isValid = Py::False(); + } + + if (isValid.as_bool()) { + + // Check priority + Py::Object isPriority; + try { + isPriority = measurePyClass.callMemberFunction("isPrioritySelection", args); + } catch (const Py::Exception&) { + Base::PyException e; + e.ReportException(); + isPriority = Py::False(); + } + + if (isPriority.as_bool()) { + validTypes.insert(validTypes.begin(), mType); + } else { + validTypes.push_back(mType); + } + } + } else { + // Parse c++ measure types + + if (mType->validatorCb && !mType->validatorCb(selection)) { + continue; + } + + // Check if the measurement type prioritizes the given selection + if (mType->prioritizeCb && mType->prioritizeCb(selection)) { + validTypes.insert(validTypes.begin(), mType); + } else { + validTypes.push_back(mType); + } + + } + } + + return validTypes; + } + + + + +} // namespace App \ No newline at end of file diff --git a/src/App/MeasureManager.h b/src/App/MeasureManager.h new file mode 100644 index 0000000000..2c93b59ebe --- /dev/null +++ b/src/App/MeasureManager.h @@ -0,0 +1,112 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * 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 MEASUREMANAGER_H +#define MEASUREMANAGER_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace App { + +// Add your class methods and member variables here +enum class MeasureElementType { + INVALID, + POINT, + LINE, + LINESEGMENT, + CIRCLE, + ARC, + CURVE, // Has a length but no radius or axis + PLANE, + CYLINDER, + Volume, +}; + + +struct MeasureSelectionItem { + App::SubObjectT object; + Base::Vector3d pickedPoint; +}; + + +using MeasureSelection = std::vector; +using MeasureValidateMethod = std::function; +using MeasurePrioritizeMethod = std::function; +using MeasureTypeMethod = std::function; + +struct MeasureType { + std::string identifier; + std::string label; + std::string measureObject; + + // Checks if the measurement works with a given selection + MeasureValidateMethod validatorCb; + + // Allows to prioritize this over other measurement types when the measurement type is picked implicitly from the selection. + // Gets called only when validatorCb returned true for the given selection + MeasurePrioritizeMethod prioritizeCb; + + bool isPython; + PyObject* pythonClass; +}; + +struct MeasureHandler { + std::string module; + MeasureTypeMethod typeCb; +}; + + +class AppExport MeasureManager { +public: + MeasureManager(); + + static void addMeasureHandler(const char* module, MeasureTypeMethod typeCb); + static bool hasMeasureHandler(const char* module); + static MeasureHandler getMeasureHandler(const char* module); + static void addMeasureType(MeasureType* measureType); + static void addMeasureType(std::string id, std::string label, std::string measureObj, MeasureValidateMethod validatorCb, MeasurePrioritizeMethod prioritizeCb); + static void addMeasureType(const char* id, const char* label, const char* measureObj, MeasureValidateMethod validatorCb, MeasurePrioritizeMethod prioritizeCb); + static const std::vector getMeasureTypes(); + static Py::Tuple getSelectionPy(const App::MeasureSelection& selection); + static std::vector getValidMeasureTypes(App::MeasureSelection selection, std::string mode); + + +private: + static std::vector _mMeasureHandlers; + static std::vector _mMeasureTypes; +}; + + +} // namespace App + +#endif // MEASUREMANAGER_H diff --git a/src/App/MeasureManagerPy.xml b/src/App/MeasureManagerPy.xml new file mode 100644 index 0000000000..e2cd74efec --- /dev/null +++ b/src/App/MeasureManagerPy.xml @@ -0,0 +1,43 @@ + + + + + + MeasureManager class. + +The MeasureManager handles measure types and geometry handler accross FreeCAD. + MeasureManager + + + + addMeasureType(id, label, measureType) -> None + +Add a new measure type. + +id : str + Unique identifier of the measure type. +label : str + Name of the module. +measureType : Measure.MeasureBasePython + The actual measure type. + + + + + getMeasureTypes() -> List[(id, label, pythonMeasureType)] + +Returns a list of all registered measure types. + + + + diff --git a/src/App/MeasureManagerPyImp.cpp b/src/App/MeasureManagerPyImp.cpp new file mode 100644 index 0000000000..46103b7f51 --- /dev/null +++ b/src/App/MeasureManagerPyImp.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (c) 2023 David Friedli * + * 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" + +// inclusion of the generated files (generated out of MeasureManagerPy.xml) +#include "MeasureManagerPy.h" +#include "MeasureManagerPy.cpp" + + +using namespace App; + + +// returns a string which represents the object e.g. when printed in python +std::string MeasureManagerPy::representation() const +{ + return ""; +} + +PyObject* MeasureManagerPy::getCustomAttributes(const char* /*attr*/) const +{ + return nullptr; +} + +int MeasureManagerPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +} + + +PyObject* MeasureManagerPy::addMeasureType(PyObject *args) +{ + PyObject *pyobj = Py_None; + char *id, *label; + + if (!PyArg_ParseTuple(args, "ssO", &id, &label, &pyobj)) + return nullptr; + + MeasureManager::addMeasureType( + new App::MeasureType{id, label, "", nullptr, nullptr, true, pyobj} + ); + + Py_Return; +} + + +PyObject* MeasureManagerPy::getMeasureTypes(PyObject *args) +{ + Py::List types; + for (auto & it : MeasureManager::getMeasureTypes()) { + Py::Tuple type(3); + type.setItem(0, Py::String(it->identifier)); + type.setItem(1, Py::String(it->label)); + type.setItem(2, Py::Object(it->pythonClass)); + + types.append(type); + } + + return Py::new_reference_to(types); +} \ No newline at end of file