[App] Add registration functions for unified measurement facility

This commit is contained in:
hlorus
2024-01-14 20:58:33 +01:00
committed by WandererFan
parent ce36dcc379
commit 2acf5ccab0
6 changed files with 422 additions and 0 deletions

View File

@@ -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");

View File

@@ -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

180
src/App/MeasureManager.cpp Normal file
View File

@@ -0,0 +1,180 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david@friedli-be.ch> *
* Copyright (c) 2023 Wandererfan <wandererfan@gmail.com> *
* *
* 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 *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include <Base/Console.h>
#include <Base/Interpreter.h>
#include <Base/VectorPy.h>
#include <App/Document.h>
#include "MeasureManager.h"
namespace App {
std::vector<MeasureHandler> MeasureManager::_mMeasureHandlers;
std::vector<MeasureType*> 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<MeasureType*> 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<MeasureType*> 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<MeasureType*> validTypes;
std::pair<int, MeasureType>();
// 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

112
src/App/MeasureManager.h Normal file
View File

@@ -0,0 +1,112 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david@friedli-be.ch> *
* Copyright (c) 2023 Wandererfan <wandererfan@gmail.com> *
* *
* 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 *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MEASUREMANAGER_H
#define MEASUREMANAGER_H
#include <vector>
#include <tuple>
#include <functional>
#include <string>
#include <Python.h>
#include <App/DocumentObject.h>
#include <Base/Vector3D.h>
#include <App/DocumentObserver.h>
#include <Base/Interpreter.h>
#include <FCGlobal.h>
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<MeasureSelectionItem>;
using MeasureValidateMethod = std::function<bool(const MeasureSelection&)>;
using MeasurePrioritizeMethod = std::function<bool(const MeasureSelection&)>;
using MeasureTypeMethod = std::function<MeasureElementType (App::DocumentObject*, const char*)>;
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<MeasureType*> getMeasureTypes();
static Py::Tuple getSelectionPy(const App::MeasureSelection& selection);
static std::vector<MeasureType*> getValidMeasureTypes(App::MeasureSelection selection, std::string mode);
private:
static std::vector<MeasureHandler> _mMeasureHandlers;
static std::vector<MeasureType*> _mMeasureTypes;
};
} // namespace App
#endif // MEASUREMANAGER_H

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="PyObjectBase"
Name="MeasureManagerPy"
Twin="MeasureManager"
TwinPointer="MeasureManager"
Include="App/MeasureManager.h"
FatherInclude="Base/PyObjectBase.h"
Namespace="App"
Constructor="false"
Delete="true"
FatherNamespace="Base">
<Documentation>
<Author Licence="LGPL" Name="David Friedli" EMail="david@friedli-be.ch" />
<UserDocu>MeasureManager class.
The MeasureManager handles measure types and geometry handler accross FreeCAD.</UserDocu>
<DeveloperDocu>MeasureManager</DeveloperDocu>
</Documentation>
<Methode Name="addMeasureType" Static="true">
<Documentation>
<UserDocu>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.</UserDocu>
</Documentation>
</Methode>
<Methode Name="getMeasureTypes" Static="true">
<Documentation>
<UserDocu>getMeasureTypes() -> List[(id, label, pythonMeasureType)]
Returns a list of all registered measure types.</UserDocu>
</Documentation>
</Methode>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,79 @@
/***************************************************************************
* Copyright (c) 2023 David Friedli <david@friedli-be.ch> *
* Copyright (c) 2023 Wandererfan <wandererfan@gmail.com> *
* *
* 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 *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#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 "<App::MeasureManager>";
}
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);
}