From 00c57a9d08d3dd2fe2caa0f58df68c66be3289b3 Mon Sep 17 00:00:00 2001 From: David Carter Date: Fri, 7 Mar 2025 10:13:56 -0500 Subject: [PATCH 1/5] Materials: External Modules Part 1 Refactored code to support local and external material sources This is the first PR in a series to support external modules. External modules allow materials to be stored in external data sources such as databases or web services. No new functionality is introduced in this PR, rather it is a refactoring of code that will allow for changes to be introduced in future PRs. Minor performance improvements have also been made in the model and material managers. The Python API has been enhanced for many data types to allow for modification within Python. --- src/Mod/Material/App/AppMaterial.cpp | 35 +- src/Mod/Material/App/Array2DPy.xml | 19 +- src/Mod/Material/App/Array2DPyImp.cpp | 51 +- src/Mod/Material/App/Array3DPy.xml | 33 +- src/Mod/Material/App/Array3DPyImp.cpp | 87 ++- src/Mod/Material/App/CMakeLists.txt | 25 +- src/Mod/Material/App/Exceptions.h | 267 +++++--- src/Mod/Material/App/FolderTree.h | 23 +- src/Mod/Material/App/Library.cpp | 143 +++++ src/Mod/Material/App/Library.h | 114 ++++ src/Mod/Material/App/MaterialConfigLoader.cpp | 9 +- src/Mod/Material/App/MaterialConfigLoader.h | 4 +- src/Mod/Material/App/MaterialFilter.cpp | 5 +- src/Mod/Material/App/MaterialFilter.h | 3 +- .../Material/App/MaterialFilterOptionsPy.xml | 49 ++ .../App/MaterialFilterOptionsPyImp.cpp | 122 ++++ src/Mod/Material/App/MaterialLibrary.cpp | 334 +++++----- src/Mod/Material/App/MaterialLibrary.h | 83 +-- src/Mod/Material/App/MaterialLibraryPy.xml | 49 ++ src/Mod/Material/App/MaterialLibraryPyImp.cpp | 125 ++++ src/Mod/Material/App/MaterialLoader.cpp | 130 +--- src/Mod/Material/App/MaterialLoader.h | 29 +- src/Mod/Material/App/MaterialManager.cpp | 445 +++++++------ src/Mod/Material/App/MaterialManager.h | 119 ++-- src/Mod/Material/App/MaterialManagerLocal.cpp | 593 ++++++++++++++++++ src/Mod/Material/App/MaterialManagerLocal.h | 131 ++++ src/Mod/Material/App/MaterialManagerPy.xml | 2 +- src/Mod/Material/App/MaterialManagerPyImp.cpp | 36 +- src/Mod/Material/App/MaterialPropertyPy.xml | 31 + .../Material/App/MaterialPropertyPyImp.cpp | 74 +++ src/Mod/Material/App/MaterialPy.xml | 15 +- src/Mod/Material/App/MaterialPyImp.cpp | 207 ++++-- src/Mod/Material/App/MaterialValue.cpp | 269 ++++++-- src/Mod/Material/App/MaterialValue.h | 37 +- src/Mod/Material/App/Materials.cpp | 252 ++++++-- src/Mod/Material/App/Materials.h | 30 +- src/Mod/Material/App/Model.cpp | 118 +++- src/Mod/Material/App/Model.h | 36 +- src/Mod/Material/App/ModelLibrary.cpp | 85 +-- src/Mod/Material/App/ModelLibrary.h | 56 +- src/Mod/Material/App/ModelManager.cpp | 151 +++-- src/Mod/Material/App/ModelManager.h | 41 +- src/Mod/Material/App/ModelManagerLocal.cpp | 219 +++++++ src/Mod/Material/App/ModelManagerLocal.h | 87 +++ src/Mod/Material/App/ModelManagerPy.xml | 8 +- src/Mod/Material/App/ModelManagerPyImp.cpp | 24 +- src/Mod/Material/App/ModelPropertyPy.xml | 41 +- src/Mod/Material/App/ModelPropertyPyImp.cpp | 86 ++- src/Mod/Material/App/ModelPy.xml | 26 +- src/Mod/Material/App/ModelPyImp.cpp | 108 ++-- src/Mod/Material/App/PropertyMaterial.cpp | 4 +- src/Mod/Material/App/PyVariants.cpp | 75 +++ src/Mod/Material/App/PyVariants.h | 42 ++ src/Mod/Material/App/UUIDsPyImp.cpp | 1 - src/Mod/Material/Gui/Array2D.cpp | 2 +- src/Mod/Material/Gui/Array2D.h | 2 +- src/Mod/Material/Gui/Array3D.cpp | 2 +- src/Mod/Material/Gui/Array3D.h | 2 +- src/Mod/Material/Gui/ArrayModel.cpp | 6 +- src/Mod/Material/Gui/ArrayModel.h | 12 +- src/Mod/Material/Gui/Command.cpp | 2 - src/Mod/Material/Gui/DlgInspectAppearance.cpp | 8 +- src/Mod/Material/Gui/DlgInspectMaterial.cpp | 24 +- src/Mod/Material/Gui/DlgInspectMaterial.h | 2 - src/Mod/Material/Gui/MaterialSave.cpp | 49 +- src/Mod/Material/Gui/MaterialSave.h | 3 +- src/Mod/Material/Gui/MaterialTreeWidget.cpp | 24 +- src/Mod/Material/Gui/MaterialTreeWidget.h | 7 +- src/Mod/Material/Gui/MaterialsEditor.cpp | 109 ++-- src/Mod/Material/Gui/MaterialsEditor.h | 8 +- src/Mod/Material/Gui/ModelSelect.cpp | 16 +- src/Mod/Material/Gui/ModelSelect.h | 5 - src/Mod/Material/materialtests/TestModels.py | 217 +++++++ tests/src/Mod/Material/App/Model.cpp | 0 .../Mod/Material/App/TestMaterialCards.cpp | 14 +- .../Mod/Material/App/TestMaterialFilter.cpp | 20 +- .../Material/App/TestMaterialProperties.cpp | 24 +- .../Mod/Material/App/TestMaterialValue.cpp | 4 +- tests/src/Mod/Material/App/TestMaterials.cpp | 14 +- tests/src/Mod/Material/App/TestModel.cpp | 4 +- 80 files changed, 4372 insertions(+), 1396 deletions(-) create mode 100644 src/Mod/Material/App/Library.cpp create mode 100644 src/Mod/Material/App/Library.h create mode 100644 src/Mod/Material/App/MaterialFilterOptionsPy.xml create mode 100644 src/Mod/Material/App/MaterialFilterOptionsPyImp.cpp create mode 100644 src/Mod/Material/App/MaterialLibraryPy.xml create mode 100644 src/Mod/Material/App/MaterialLibraryPyImp.cpp create mode 100644 src/Mod/Material/App/MaterialManagerLocal.cpp create mode 100644 src/Mod/Material/App/MaterialManagerLocal.h create mode 100644 src/Mod/Material/App/MaterialPropertyPy.xml create mode 100644 src/Mod/Material/App/MaterialPropertyPyImp.cpp create mode 100644 src/Mod/Material/App/ModelManagerLocal.cpp create mode 100644 src/Mod/Material/App/ModelManagerLocal.h create mode 100644 src/Mod/Material/App/PyVariants.cpp create mode 100644 src/Mod/Material/App/PyVariants.h delete mode 100644 tests/src/Mod/Material/App/Model.cpp diff --git a/src/Mod/Material/App/AppMaterial.cpp b/src/Mod/Material/App/AppMaterial.cpp index 556aee8b68..ddc2e16a80 100644 --- a/src/Mod/Material/App/AppMaterial.cpp +++ b/src/Mod/Material/App/AppMaterial.cpp @@ -29,15 +29,24 @@ #include -#include "MaterialFilterPy.h" #include "MaterialLoader.h" -#include "MaterialManagerPy.h" -#include "MaterialPy.h" +#include "MaterialManagerLocal.h" +#include "ModelManagerLocal.h" +#include "PropertyMaterial.h" + +#include "Array2DPy.h" +#include "Array3DPy.h" #include "ModelManagerPy.h" #include "ModelPropertyPy.h" #include "ModelPy.h" #include "UUIDsPy.h" -#include "PropertyMaterial.h" + +#include "MaterialFilterPy.h" +#include "MaterialFilterOptionsPy.h" +#include "MaterialLibraryPy.h" +#include "MaterialManagerPy.h" +#include "MaterialPropertyPy.h" +#include "MaterialPy.h" namespace Materials { @@ -74,8 +83,13 @@ PyMOD_INIT_FUNC(Materials) Base::Console().Log("Loading Material module... done\n"); - Base::Interpreter().addType(&Materials::MaterialManagerPy::Type, module, "MaterialManager"); + Base::Interpreter().addType(&Materials::Array2DPy::Type, module, "Array2D"); + Base::Interpreter().addType(&Materials::Array3DPy::Type, module, "Array3D"); Base::Interpreter().addType(&Materials::MaterialFilterPy::Type, module, "MaterialFilter"); + Base::Interpreter().addType(&Materials::MaterialFilterOptionsPy::Type, module, "MaterialFilterOptions"); + Base::Interpreter().addType(&Materials::MaterialLibraryPy::Type, module, "MaterialLibrary"); + Base::Interpreter().addType(&Materials::MaterialManagerPy::Type, module, "MaterialManager"); + Base::Interpreter().addType(&Materials::MaterialPropertyPy::Type, module, "MaterialProperty"); Base::Interpreter().addType(&Materials::MaterialPy::Type, module, "Material"); Base::Interpreter().addType(&Materials::ModelManagerPy::Type, module, "ModelManager"); Base::Interpreter().addType(&Materials::ModelPropertyPy::Type, module, "ModelProperty"); @@ -88,22 +102,25 @@ PyMOD_INIT_FUNC(Materials) Materials::Material ::init(); Materials::MaterialFilter ::init(); + Materials::MaterialFilterOptions ::init(); Materials::MaterialManager ::init(); + Materials::MaterialManagerLocal ::init(); Materials::Model ::init(); Materials::ModelManager ::init(); + Materials::ModelManagerLocal ::init(); Materials::ModelUUIDs ::init(); - Materials::LibraryBase ::init(); + Materials::Library ::init(); Materials::MaterialLibrary ::init(); + Materials::MaterialLibraryLocal ::init(); Materials::ModelLibrary ::init(); - Materials::MaterialExternalLibrary ::init(); Materials::ModelProperty ::init(); Materials::MaterialProperty ::init(); Materials::MaterialValue ::init(); - Materials::Material2DArray ::init(); - Materials::Material3DArray ::init(); + Materials::Array2D ::init(); + Materials::Array3D ::init(); Materials::PropertyMaterial ::init(); // clang-format on diff --git a/src/Mod/Material/App/Array2DPy.xml b/src/Mod/Material/App/Array2DPy.xml index c9beb5580d..af6c665945 100644 --- a/src/Mod/Material/App/Array2DPy.xml +++ b/src/Mod/Material/App/Array2DPy.xml @@ -3,8 +3,8 @@ - + + + The number of dimensions in the array, in this case 2. + + + + The number of rows in the array. - + The number of columns in the array. @@ -43,5 +49,10 @@ Get the value at the given row and column + + + Set the value at the given row and column + + diff --git a/src/Mod/Material/App/Array2DPyImp.cpp b/src/Mod/Material/App/Array2DPyImp.cpp index 3b576be1ca..50d66925f4 100644 --- a/src/Mod/Material/App/Array2DPyImp.cpp +++ b/src/Mod/Material/App/Array2DPyImp.cpp @@ -44,7 +44,7 @@ using namespace Materials; std::string Array2DPy::representation() const { std::stringstream str; - str << ""; + str << ""; return str.str(); } @@ -52,7 +52,7 @@ std::string Array2DPy::representation() const PyObject* Array2DPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper { // never create such objects with the constructor - return new Array2DPy(new Material2DArray()); + return new Array2DPy(new Array2D()); } // constructor method @@ -65,7 +65,7 @@ Py::List Array2DPy::getArray() const { Py::List list; - auto array = getMaterial2DArrayPtr()->getArray(); + auto array = getArray2DPtr()->getArray(); for (auto& row : array) { Py::List rowList; @@ -81,14 +81,29 @@ Py::List Array2DPy::getArray() const return list; } +Py::Long Array2DPy::getDimensions() const +{ + return Py::Long(2); +} + Py::Long Array2DPy::getRows() const { - return Py::Long(getMaterial2DArrayPtr()->rows()); + return Py::Long(getArray2DPtr()->rows()); +} + +void Array2DPy::setRows(Py::Long arg) +{ + getArray2DPtr()->setRows(arg); } Py::Long Array2DPy::getColumns() const { - return Py::Long(getMaterial2DArrayPtr()->columns()); + return Py::Long(getArray2DPtr()->columns()); +} + +void Array2DPy::setColumns(Py::Long arg) +{ + getArray2DPtr()->setColumns(arg); } PyObject* Array2DPy::getRow(PyObject* args) @@ -101,7 +116,7 @@ PyObject* Array2DPy::getRow(PyObject* args) try { Py::List list; - auto arrayRow = getMaterial2DArrayPtr()->getRow(row); + auto arrayRow = getArray2DPtr()->getRow(row); for (auto& column : *arrayRow) { auto quantity = new Base::QuantityPy(new Base::Quantity(column.value())); @@ -126,7 +141,7 @@ PyObject* Array2DPy::getValue(PyObject* args) } try { - auto value = getMaterial2DArrayPtr()->getValue(row, column); + auto value = getArray2DPtr()->getValue(row, column); return new Base::QuantityPy(new Base::Quantity(value.value())); } catch (const InvalidIndex&) { @@ -136,6 +151,28 @@ PyObject* Array2DPy::getValue(PyObject* args) return nullptr; } +PyObject* Array2DPy::setValue(PyObject* args) +{ + int row; + int column; + PyObject* valueObj; + if (PyArg_ParseTuple(args, "iiO!", &row, &column, &PyUnicode_Type, &valueObj)) { + Py::String item(valueObj); + try { + QVariant variant = QVariant::fromValue(Base::Quantity::parse(item.as_string())); + getArray2DPtr()->setValue(row, column, variant); + } + catch (const InvalidIndex&) { + PyErr_SetString(PyExc_IndexError, "Invalid array index"); + return nullptr; + } + Py_Return; + } + + PyErr_SetString(PyExc_TypeError, "Expected (integer, integer, string) arguments"); + return nullptr; +} + PyObject* Array2DPy::getCustomAttributes(const char* /*attr*/) const { return nullptr; diff --git a/src/Mod/Material/App/Array3DPy.xml b/src/Mod/Material/App/Array3DPy.xml index 435c61e06a..2f17c825b8 100644 --- a/src/Mod/Material/App/Array3DPy.xml +++ b/src/Mod/Material/App/Array3DPy.xml @@ -3,8 +3,8 @@ - + + + The number of dimensions in the array, in this case 3. + + + + The number of columns in the array. - + - + The depth of the array (3rd dimension). - + @@ -48,5 +54,20 @@ Get the column value at the given depth + + + Set the column value at the given depth + + + + + Set the value at the given depth, row, and column + + + + + Set the number of rows at the given depth + + diff --git a/src/Mod/Material/App/Array3DPyImp.cpp b/src/Mod/Material/App/Array3DPyImp.cpp index bb6e57ee43..7137adbcfc 100644 --- a/src/Mod/Material/App/Array3DPyImp.cpp +++ b/src/Mod/Material/App/Array3DPyImp.cpp @@ -44,7 +44,7 @@ using namespace Materials; std::string Array3DPy::representation() const { std::stringstream str; - str << ""; + str << ""; return str.str(); } @@ -52,7 +52,7 @@ std::string Array3DPy::representation() const PyObject* Array3DPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper { // never create such objects with the constructor - return new Array3DPy(new Material3DArray()); + return new Array3DPy(new Array3D()); } // constructor method @@ -64,7 +64,7 @@ int Array3DPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) Py::List Array3DPy::getArray() const { Py::List list; - auto array = getMaterial3DArrayPtr()->getArray(); + auto array = getArray3DPtr()->getArray(); for (auto& depth : array) { Py::List depthList; @@ -83,24 +83,39 @@ Py::List Array3DPy::getArray() const return list; } +Py::Long Array3DPy::getDimensions() const +{ + return Py::Long(3); +} + Py::Long Array3DPy::getColumns() const { - return Py::Long(getMaterial3DArrayPtr()->columns()); + return Py::Long(getArray3DPtr()->columns()); +} + +void Array3DPy::setColumns(Py::Long arg) +{ + getArray3DPtr()->setColumns(arg); } Py::Long Array3DPy::getDepth() const { - return Py::Long(getMaterial3DArrayPtr()->depth()); + return Py::Long(getArray3DPtr()->depth()); +} + +void Array3DPy::setDepth(Py::Long arg) +{ + getArray3DPtr()->setDepth(arg); } PyObject* Array3DPy::getRows(PyObject* args) { - int depth = getMaterial3DArrayPtr()->currentDepth(); + int depth = getArray3DPtr()->currentDepth(); if (!PyArg_ParseTuple(args, "|i", &depth)) { return nullptr; } - return PyLong_FromLong(getMaterial3DArrayPtr()->rows(depth)); + return PyLong_FromLong(getArray3DPtr()->rows(depth)); } PyObject* Array3DPy::getValue(PyObject* args) @@ -113,7 +128,7 @@ PyObject* Array3DPy::getValue(PyObject* args) } try { - auto value = getMaterial3DArrayPtr()->getValue(depth, row, column); + auto value = getArray3DPtr()->getValue(depth, row, column); return new Base::QuantityPy(new Base::Quantity(value)); } catch (const InvalidIndex&) { @@ -131,7 +146,7 @@ PyObject* Array3DPy::getDepthValue(PyObject* args) } try { - auto value = getMaterial3DArrayPtr()->getDepthValue(depth); + auto value = getArray3DPtr()->getDepthValue(depth); return new Base::QuantityPy(new Base::Quantity(value)); } catch (const InvalidIndex&) { @@ -141,6 +156,60 @@ PyObject* Array3DPy::getDepthValue(PyObject* args) return nullptr; } +PyObject* Array3DPy::setDepthValue(PyObject* args) +{ + int depth; + PyObject* valueObj; + if (PyArg_ParseTuple(args, "iO!", &depth, &PyUnicode_Type, &valueObj)) { + Py::String item(valueObj); + try { + getArray3DPtr()->setDepthValue(depth, Base::Quantity::parse(item.as_string())); + } + catch (const InvalidIndex&) { + PyErr_SetString(PyExc_IndexError, "Invalid array index"); + return nullptr; + } + Py_Return; + } + + PyErr_SetString(PyExc_TypeError, "Expected (integer, string) arguments"); + return nullptr; +} + +PyObject* Array3DPy::setValue(PyObject* args) +{ + int depth; + int row; + int column; + PyObject* valueObj; + if (PyArg_ParseTuple(args, "iiiO!", &depth, &row, &column, &PyUnicode_Type, &valueObj)) { + Py::String item(valueObj); + try { + getArray3DPtr()->setValue(depth, row, column, Base::Quantity::parse(item.as_string())); + } + catch (const InvalidIndex&) { + PyErr_SetString(PyExc_IndexError, "Invalid array index"); + return nullptr; + } + Py_Return; + } + + PyErr_SetString(PyExc_TypeError, "Expected (integer, integer, integer, string) arguments"); + return nullptr; +} + +PyObject* Array3DPy::setRows(PyObject* args) +{ + int depth; + int rows; + if (!PyArg_ParseTuple(args, "ii", &depth, &rows)) { + return nullptr; + } + + getArray3DPtr()->setRows(depth, rows); + Py_Return; +} + PyObject* Array3DPy::getCustomAttributes(const char* /*attr*/) const { return nullptr; diff --git a/src/Mod/Material/App/CMakeLists.txt b/src/Mod/Material/App/CMakeLists.txt index 9775acfec5..a38701eb69 100644 --- a/src/Mod/Material/App/CMakeLists.txt +++ b/src/Mod/Material/App/CMakeLists.txt @@ -45,10 +45,13 @@ endif() generate_from_xml(Array2DPy) generate_from_xml(Array3DPy) generate_from_xml(MaterialFilterPy) +generate_from_xml(MaterialFilterOptionsPy) +generate_from_xml(MaterialLibraryPy) generate_from_xml(MaterialManagerPy) generate_from_xml(MaterialPy) generate_from_xml(ModelManagerPy) generate_from_xml(ModelPropertyPy) +generate_from_xml(MaterialPropertyPy) generate_from_xml(ModelPy) generate_from_xml(UUIDsPy) @@ -58,12 +61,18 @@ SET(Python_SRCS Array2DPyImp.cpp Array3DPy.xml Array3DPyImp.cpp - MaterialManagerPy.xml - MaterialManagerPyImp.cpp - MaterialPy.xml - MaterialPyImp.cpp + MaterialFilterOptionsPy.xml + MaterialFilterOptionsPyImp.cpp MaterialFilterPy.xml MaterialFilterPyImp.cpp + MaterialLibraryPy.xml + MaterialLibraryPyImp.cpp + MaterialManagerPy.xml + MaterialManagerPyImp.cpp + MaterialPropertyPy.xml + MaterialPropertyPyImp.cpp + MaterialPy.xml + MaterialPyImp.cpp ModelManagerPy.xml ModelManagerPyImp.cpp ModelPropertyPy.xml @@ -79,6 +88,8 @@ SET(Materials_SRCS ${Python_SRCS} AppMaterial.cpp FolderTree.h + Library.cpp + Library.h MaterialConfigLoader.cpp MaterialConfigLoader.h MaterialFilter.cpp @@ -89,6 +100,8 @@ SET(Materials_SRCS MaterialLoader.h MaterialManager.cpp MaterialManager.h + MaterialManagerLocal.cpp + MaterialManagerLocal.h Materials.cpp Materials.h MaterialValue.cpp @@ -101,12 +114,16 @@ SET(Materials_SRCS ModelLoader.h ModelManager.cpp ModelManager.h + ModelManagerLocal.cpp + ModelManagerLocal.h ModelUuids.cpp ModelUuids.h PreCompiled.cpp PreCompiled.h PropertyMaterial.cpp PropertyMaterial.h + PyVariants.cpp + PyVariants.h trim.h ) diff --git a/src/Mod/Material/App/Exceptions.h b/src/Mod/Material/App/Exceptions.h index 97ca83c479..d21053b40c 100644 --- a/src/Mod/Material/App/Exceptions.h +++ b/src/Mod/Material/App/Exceptions.h @@ -34,15 +34,14 @@ class Uninitialized: public Base::Exception { public: Uninitialized() + : Base::Exception("Uninitalized") {} explicit Uninitialized(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit Uninitialized(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~Uninitialized() noexcept override = default; }; @@ -50,17 +49,14 @@ class ModelNotFound: public Base::Exception { public: ModelNotFound() - { - this->setMessage("Model not found"); - } + : Base::Exception("Model not found") + {} explicit ModelNotFound(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit ModelNotFound(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~ModelNotFound() noexcept override = default; }; @@ -68,15 +64,14 @@ class InvalidMaterialType: public Base::Exception { public: InvalidMaterialType() + : Base::Exception("Invalid material type") {} explicit InvalidMaterialType(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit InvalidMaterialType(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~InvalidMaterialType() noexcept override = default; }; @@ -84,17 +79,14 @@ class MaterialNotFound: public Base::Exception { public: MaterialNotFound() - { - this->setMessage("Material not found"); - } + : Base::Exception("Material not found") + {} explicit MaterialNotFound(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit MaterialNotFound(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~MaterialNotFound() noexcept override = default; }; @@ -102,15 +94,14 @@ class MaterialExists: public Base::Exception { public: MaterialExists() + : Base::Exception("Material already exists") {} explicit MaterialExists(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit MaterialExists(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~MaterialExists() noexcept override = default; }; @@ -118,15 +109,14 @@ class MaterialReadError: public Base::Exception { public: MaterialReadError() + : Base::Exception("Unable to read material") {} explicit MaterialReadError(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit MaterialReadError(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~MaterialReadError() noexcept override = default; }; @@ -134,17 +124,14 @@ class PropertyNotFound: public Base::Exception { public: PropertyNotFound() - { - this->setMessage("Property not found"); - } + : Base::Exception("Property not found") + {} explicit PropertyNotFound(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit PropertyNotFound(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~PropertyNotFound() noexcept override = default; }; @@ -152,53 +139,104 @@ class LibraryNotFound: public Base::Exception { public: LibraryNotFound() - { - this->setMessage("Library not found"); - } + : Base::Exception("Library not found") + {} explicit LibraryNotFound(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit LibraryNotFound(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~LibraryNotFound() noexcept override = default; }; +class CreationError: public Base::Exception +{ +public: + CreationError() + : Base::Exception("Unable to create object") + {} + explicit CreationError(const char* msg) + : Base::Exception(msg) + {} + explicit CreationError(const QString& msg) + : Base::Exception(msg.toStdString().c_str()) + {} + ~CreationError() noexcept override = default; +}; + class InvalidModel: public Base::Exception { public: InvalidModel() - { - this->setMessage("Invalid model"); - } + : Base::Exception("Invalid model") + {} explicit InvalidModel(const char* msg) - { - this->setMessage(msg); - } + : Base::Exception(msg) + {} explicit InvalidModel(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~InvalidModel() noexcept override = default; }; +class InvalidMaterial: public Base::Exception +{ +public: + InvalidMaterial() + : Base::Exception("Invalid material") + {} + explicit InvalidMaterial(const char* msg) + : Base::Exception(msg) + {} + explicit InvalidMaterial(const QString& msg) + : Base::Exception(msg.toStdString().c_str()) + {} + ~InvalidMaterial() noexcept override = default; +}; + +class InvalidProperty: public Base::Exception +{ +public: + InvalidProperty() + : Base::Exception("Invalid property") + {} + explicit InvalidProperty(const char* msg) + : Base::Exception(msg) + {} + explicit InvalidProperty(const QString& msg) + : Base::Exception(msg.toStdString().c_str()) + {} + ~InvalidProperty() noexcept override = default; +}; + +class InvalidLibrary: public Base::Exception +{ +public: + InvalidLibrary() + : Base::Exception("Invalid library") + {} + explicit InvalidLibrary(const char* msg) + : Base::Exception(msg) + {} + explicit InvalidLibrary(const QString& msg) + : Base::Exception(msg.toStdString().c_str()) + {} + ~InvalidLibrary() noexcept override = default; +}; + class InvalidIndex: public Base::Exception { public: InvalidIndex() - { - this->setMessage("Invalid index"); - } - explicit InvalidIndex(char* msg) - { - this->setMessage(msg); - } + : Base::Exception("Invalid index") + {} + explicit InvalidIndex(const char* msg) + : Base::Exception(msg) + {} explicit InvalidIndex(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~InvalidIndex() noexcept override = default; }; @@ -206,15 +244,14 @@ class UnknownValueType: public Base::Exception { public: UnknownValueType() + : Base::Exception("Unkown value type") + {} + explicit UnknownValueType(const char* msg) + : Base::Exception(msg) {} - explicit UnknownValueType(char* msg) - { - this->setMessage(msg); - } explicit UnknownValueType(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~UnknownValueType() noexcept override = default; }; @@ -222,18 +259,62 @@ class DeleteError: public Base::Exception { public: DeleteError() + : Base::Exception("Unable to delete object") + {} + explicit DeleteError(const char* msg) + : Base::Exception(msg) {} - explicit DeleteError(char* msg) - { - this->setMessage(msg); - } explicit DeleteError(const QString& msg) - { - this->setMessage(msg.toStdString().c_str()); - } + : Base::Exception(msg.toStdString().c_str()) + {} ~DeleteError() noexcept override = default; }; +class RenameError: public Base::Exception +{ +public: + RenameError() + : Base::Exception("Unable to rename object") + {} + explicit RenameError(const char* msg) + : Base::Exception(msg) + {} + explicit RenameError(const QString& msg) + : Base::Exception(msg.toStdString().c_str()) + {} + ~RenameError() noexcept override = default; +}; + +class ReplacementError: public Base::Exception +{ +public: + ReplacementError() + : Base::Exception("Unable to replace object") + {} + explicit ReplacementError(const char* msg) + : Base::Exception(msg) + {} + explicit ReplacementError(const QString& msg) + : Base::Exception(msg.toStdString().c_str()) + {} + ~ReplacementError() noexcept override = default; +}; + +class ConnectionError: public Base::Exception +{ +public: + ConnectionError() + : Base::Exception("Unable to connect") + {} + explicit ConnectionError(const char* msg) + : Base::Exception(msg) + {} + explicit ConnectionError(const QString& msg) + : Base::Exception(msg.toStdString().c_str()) + {} + ~ConnectionError() noexcept override = default; +}; + } // namespace Materials #endif // MATERIAL_EXCEPTIONS_H diff --git a/src/Mod/Material/App/FolderTree.h b/src/Mod/Material/App/FolderTree.h index e2c450753c..8e12302d3e 100644 --- a/src/Mod/Material/App/FolderTree.h +++ b/src/Mod/Material/App/FolderTree.h @@ -34,18 +34,21 @@ template class FolderTreeNode { public: - enum NodeType + enum class NodeType { + UnknownNode, DataNode, FolderNode }; FolderTreeNode() + : _type(NodeType::UnknownNode) {} virtual ~FolderTreeNode() = default; NodeType getType() const { + // assert(_type == NodeType::DataNode || _type == NodeType::FolderNode); return _type; } void setType(NodeType type) @@ -55,31 +58,45 @@ public: const std::shared_ptr>>> getFolder() const { + assert(_type == NodeType::FolderNode); return _folder; } std::shared_ptr>>> getFolder() { + assert(_type == NodeType::FolderNode); return _folder; } std::shared_ptr getData() const { + assert(_type == NodeType::DataNode); return _data; } + QString getUUID() const + { + assert(_type == NodeType::DataNode); + return _uuid; + } void setFolder(std::shared_ptr>>> folder) { - setType(FolderNode); + setType(NodeType::FolderNode); _folder = folder; } void setData(std::shared_ptr data) { - setType(DataNode); + setType(NodeType::DataNode); _data = data; } + void setUUID(const QString uuid) + { + setType(NodeType::DataNode); + _uuid = uuid; + } private: NodeType _type; std::shared_ptr>>> _folder; + QString _uuid; std::shared_ptr _data; }; diff --git a/src/Mod/Material/App/Library.cpp b/src/Mod/Material/App/Library.cpp new file mode 100644 index 0000000000..cb7b31244d --- /dev/null +++ b/src/Mod/Material/App/Library.cpp @@ -0,0 +1,143 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * 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 "Exceptions.h" +#include "Library.h" + + +using namespace Materials; + +TYPESYSTEM_SOURCE(Materials::Library, Base::BaseClass) + +Library::Library(const QString& libraryName, const QString& icon, bool readOnly) + : _name(libraryName) + , _iconPath(icon) + , _readOnly(readOnly) +{} + +Library::Library(const QString& libraryName, const QString& dir, const QString& icon, bool readOnly) + : _name(libraryName) + , _iconPath(icon) + , _readOnly(readOnly) + , _directory(QDir::cleanPath(dir)) +{} + +bool Library::operator==(const Library& library) const +{ + return (getName() == library.getName()) && (_directory == library._directory); +} + +void Library::validate(const Library& remote) const +{ + if (getName() != remote.getName()) { + throw InvalidLibrary("Library names don't match"); + } + if (getIconPath() != remote.getIconPath()) { + Base::Console().Log("Icon path 1 '%s'\n", getIconPath().toStdString().c_str()); + Base::Console().Log("Icon path 2 '%s'\n", remote.getIconPath().toStdString().c_str()); + throw InvalidLibrary("Library icon paths don't match"); + } + + // Local and remote paths will differ + if (!remote.getDirectory().isEmpty()) { + throw InvalidLibrary("Remote library should not have a path"); + } + + if (isReadOnly() != remote.isReadOnly()) { + throw InvalidLibrary("Library readonly settings don't match"); + } +} + +QString Library::getLocalPath(const QString& path) const +{ + QString filePath = getDirectoryPath(); + if (!(filePath.endsWith(QStringLiteral("/")) || filePath.endsWith(QStringLiteral("\\")))) { + filePath += QStringLiteral("/"); + } + + QString cleanPath = QDir::cleanPath(path); + QString prefix = QStringLiteral("/") + getName(); + if (cleanPath.startsWith(prefix)) { + // Remove the library name from the path + filePath += cleanPath.right(cleanPath.length() - prefix.length()); + } + else { + filePath += cleanPath; + } + + return filePath; +} + +bool Library::isRoot(const QString& path) const +{ + QString localPath = getLocalPath(path); + QString cleanPath = getLocalPath(QStringLiteral("")); + std::string pLocal = localPath.toStdString(); + std::string pclean = cleanPath.toStdString(); + return (cleanPath == localPath); +} + +QString Library::getRelativePath(const QString& path) const +{ + QString filePath; + QString cleanPath = QDir::cleanPath(path); + QString prefix = QStringLiteral("/") + getName(); + if (cleanPath.startsWith(prefix)) { + // Remove the library name from the path + filePath = cleanPath.right(cleanPath.length() - prefix.length()); + } + else { + filePath = cleanPath; + } + + prefix = getDirectoryPath(); + if (filePath.startsWith(prefix)) { + // Remove the library root from the path + filePath = filePath.right(filePath.length() - prefix.length()); + } + + // Remove any leading '/' + if (filePath.startsWith(QStringLiteral("/"))) { + filePath.remove(0, 1); + } + + return filePath; +} + +QString Library::getLibraryPath(const QString& path, const QString& filename) const +{ + QString filePath(path); + if (filePath.endsWith(filename)) { + filePath = filePath.left(filePath.length() - filename.length()); + } + if (filePath.endsWith(QStringLiteral("/"))) { + filePath = filePath.left(filePath.length() - 1); + } + + return filePath; +} diff --git a/src/Mod/Material/App/Library.h b/src/Mod/Material/App/Library.h new file mode 100644 index 0000000000..b2249a91a8 --- /dev/null +++ b/src/Mod/Material/App/Library.h @@ -0,0 +1,114 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * 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 MATERIAL_LIBRARY_H +#define MATERIAL_LIBRARY_H + +#include +#include + +#include + +#include + +namespace Materials +{ + +class MaterialsExport Library: public Base::BaseClass +{ + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + Library() = default; + Library(const QString& libraryName, const QString& icon, bool readOnly = true); + Library(const QString& libraryName, + const QString& dir, + const QString& icon, + bool readOnly = true); + ~Library() override = default; + + QString getName() const + { + return _name; + } + void setName(const QString& newName) + { + _name = newName; + } + bool sameName(const QString& name) + { + return (_name == name); + } + + QString getIconPath() const + { + return _iconPath; + } + void setIconPath(const QString& icon) + { + _iconPath = icon; + } + bool isReadOnly() const + { + return _readOnly; + } + void setReadOnly(bool readOnly) + { + _readOnly = readOnly; + } + + QString getDirectory() const + { + return _directory; + } + void setDirectory(const QString& directory) + { + _directory = directory; + } + QString getDirectoryPath() const + { + return QDir(_directory).absolutePath(); + } + + bool operator==(const Library& library) const; + bool operator!=(const Library& library) const + { + return !operator==(library); + } + + QString getLocalPath(const QString& path) const; + QString getRelativePath(const QString& path) const; + QString getLibraryPath(const QString& path, const QString& filename) const; + bool isRoot(const QString& path) const; + + // Validate a remote library against this one (a local library) + void validate(const Library& remote) const; + +private: + QString _name; + QString _directory; + QString _iconPath; + bool _readOnly; +}; + +} // namespace Materials + +#endif // MATERIAL_LIBRARY_H diff --git a/src/Mod/Material/App/MaterialConfigLoader.cpp b/src/Mod/Material/App/MaterialConfigLoader.cpp index 0aa582e1cf..cd241acf18 100644 --- a/src/Mod/Material/App/MaterialConfigLoader.cpp +++ b/src/Mod/Material/App/MaterialConfigLoader.cpp @@ -1022,7 +1022,7 @@ void MaterialConfigLoader::addLegacy(const QMap& fcmat, { for (auto const& legacy : fcmat.keys()) { auto name = legacy; - int last = name.lastIndexOf(QLatin1String("/")); + int last = name.lastIndexOf(QStringLiteral("/")); if (last > 0) { name = name.mid(last + 1); } @@ -1034,7 +1034,7 @@ void MaterialConfigLoader::addLegacy(const QMap& fcmat, } std::shared_ptr -MaterialConfigLoader::getMaterialFromPath(const std::shared_ptr& library, +MaterialConfigLoader::getMaterialFromPath(const std::shared_ptr& library, const QString& path) { QString author = getAuthorAndLicense(path); // Place them both in the author field @@ -1056,7 +1056,10 @@ MaterialConfigLoader::getMaterialFromPath(const std::shared_ptr QString sourceReference = value(fcmat, "ReferenceSource", ""); QString sourceURL = value(fcmat, "SourceURL", ""); - std::shared_ptr finalModel = std::make_shared(library, path, uuid, name); + auto baseLibrary = + reinterpret_cast&>(library); + std::shared_ptr finalModel = + std::make_shared(baseLibrary, path, uuid, name); finalModel->setOldFormat(true); finalModel->setAuthor(author); diff --git a/src/Mod/Material/App/MaterialConfigLoader.h b/src/Mod/Material/App/MaterialConfigLoader.h index cf2b2ab3ff..171d8a36a2 100644 --- a/src/Mod/Material/App/MaterialConfigLoader.h +++ b/src/Mod/Material/App/MaterialConfigLoader.h @@ -36,6 +36,8 @@ namespace Materials { +class MaterialLibraryLocal; + class MaterialConfigLoader { public: @@ -45,7 +47,7 @@ public: static bool isConfigStyle(const QString& path); static std::shared_ptr - getMaterialFromPath(const std::shared_ptr& library, const QString& path); + getMaterialFromPath(const std::shared_ptr& library, const QString& path); private: static QString value(const QMap& fcmat, diff --git a/src/Mod/Material/App/MaterialFilter.cpp b/src/Mod/Material/App/MaterialFilter.cpp index e01654a696..a1284a7151 100644 --- a/src/Mod/Material/App/MaterialFilter.cpp +++ b/src/Mod/Material/App/MaterialFilter.cpp @@ -33,6 +33,8 @@ using namespace Materials; +TYPESYSTEM_SOURCE(Materials::MaterialFilterOptions, Base::BaseClass) + MaterialFilterOptions::MaterialFilterOptions() { auto param = App::GetApplication().GetParameterGroupByPath( @@ -94,9 +96,8 @@ bool MaterialFilter::modelIncluded(const std::shared_ptr& material) co bool MaterialFilter::modelIncluded(const QString& uuid) const { - MaterialManager manager; try { - auto material = manager.getMaterial(uuid); + auto material = MaterialManager::getManager().getMaterial(uuid); return modelIncluded(material); } catch (const MaterialNotFound&) { diff --git a/src/Mod/Material/App/MaterialFilter.h b/src/Mod/Material/App/MaterialFilter.h index 7645d75971..c25612bd2c 100644 --- a/src/Mod/Material/App/MaterialFilter.h +++ b/src/Mod/Material/App/MaterialFilter.h @@ -41,8 +41,9 @@ class Material; * This class is used to set options for a material tree search * */ -class MaterialsExport MaterialFilterOptions +class MaterialsExport MaterialFilterOptions: public Base::BaseClass { + TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: MaterialFilterOptions(); diff --git a/src/Mod/Material/App/MaterialFilterOptionsPy.xml b/src/Mod/Material/App/MaterialFilterOptionsPy.xml new file mode 100644 index 0000000000..6f793897c2 --- /dev/null +++ b/src/Mod/Material/App/MaterialFilterOptionsPy.xml @@ -0,0 +1,49 @@ + + + + + + Material filtering options. + + + + Include materials marked as favorite. + + + + + + Include recently used materials. + + + + + + Include empty folders. + + + + + + Include empty libraries. + + + + + + Include materials using the older legacy format. + + + + + diff --git a/src/Mod/Material/App/MaterialFilterOptionsPyImp.cpp b/src/Mod/Material/App/MaterialFilterOptionsPyImp.cpp new file mode 100644 index 0000000000..3e7f21b6d5 --- /dev/null +++ b/src/Mod/Material/App/MaterialFilterOptionsPyImp.cpp @@ -0,0 +1,122 @@ +/*************************************************************************** + * Copyright (c) 2023-2024 David Carter * + * * + * 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 "MaterialFilter.h" + +#include "MaterialFilterOptionsPy.h" + +#include "MaterialFilterOptionsPy.cpp" + +using namespace Materials; + +// Forward declaration +// static PyObject* _pyObjectFromVariant(const QVariant& value); +// static Py::List getList(const QVariant& value); + +// returns a string which represents the object e.g. when printed in python +std::string MaterialFilterOptionsPy::representation() const +{ + std::stringstream str; + str << ""; + + return str.str(); +} + +PyObject* MaterialFilterOptionsPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper +{ + // never create such objects with the constructor + return new MaterialFilterOptionsPy(new MaterialFilterOptions()); +} + +// constructor method +int MaterialFilterOptionsPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) +{ + return 0; +} + +Py::Boolean MaterialFilterOptionsPy::getIncludeFavorites() const +{ + return getMaterialFilterOptionsPtr()->includeFavorites(); +} + +void MaterialFilterOptionsPy::setIncludeFavorites(const Py::Boolean value) +{ + getMaterialFilterOptionsPtr()->setIncludeFavorites(value); +} + +Py::Boolean MaterialFilterOptionsPy::getIncludeRecent() const +{ + return getMaterialFilterOptionsPtr()->includeRecent(); +} + +void MaterialFilterOptionsPy::setIncludeRecent(const Py::Boolean value) +{ + getMaterialFilterOptionsPtr()->setIncludeRecent(value); +} + +Py::Boolean MaterialFilterOptionsPy::getIncludeEmptyFolders() const +{ + return getMaterialFilterOptionsPtr()->includeEmptyFolders(); +} + +void MaterialFilterOptionsPy::setIncludeEmptyFolders(const Py::Boolean value) +{ + getMaterialFilterOptionsPtr()->setIncludeEmptyFolders(value); +} + +Py::Boolean MaterialFilterOptionsPy::getIncludeEmptyLibraries() const +{ + return getMaterialFilterOptionsPtr()->includeEmptyLibraries(); +} + +void MaterialFilterOptionsPy::setIncludeEmptyLibraries(const Py::Boolean value) +{ + getMaterialFilterOptionsPtr()->setIncludeEmptyLibraries(value); +} + +Py::Boolean MaterialFilterOptionsPy::getIncludeLegacy() const +{ + return getMaterialFilterOptionsPtr()->includeLegacy(); +} + +void MaterialFilterOptionsPy::setIncludeLegacy(const Py::Boolean value) +{ + getMaterialFilterOptionsPtr()->setIncludeLegacy(value); +} + +PyObject* MaterialFilterOptionsPy::getCustomAttributes(const char* /*attr*/) const +{ + return nullptr; +} + +int MaterialFilterOptionsPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +} diff --git a/src/Mod/Material/App/MaterialLibrary.cpp b/src/Mod/Material/App/MaterialLibrary.cpp index 1aafe2ab00..14446afeb0 100644 --- a/src/Mod/Material/App/MaterialLibrary.cpp +++ b/src/Mod/Material/App/MaterialLibrary.cpp @@ -41,18 +41,121 @@ using namespace Materials; /* TRANSLATOR Material::Materials */ -TYPESYSTEM_SOURCE(Materials::MaterialLibrary, Materials::LibraryBase) +TYPESYSTEM_SOURCE(Materials::MaterialLibrary, Base::BaseClass) + +MaterialLibrary::MaterialLibrary(const QString& libraryName, const QString& icon, bool readOnly) + : Library(libraryName, icon, readOnly) + , _local(false) +{} MaterialLibrary::MaterialLibrary(const QString& libraryName, const QString& dir, const QString& icon, bool readOnly) - : LibraryBase(libraryName, dir, icon) - , _readOnly(readOnly) - , _materialPathMap(std::make_unique>>()) + : Library(libraryName, dir, icon, readOnly) + , _local(false) {} -void MaterialLibrary::createFolder(const QString& path) +bool MaterialLibrary::isLocal() const +{ + return _local; +} + +void MaterialLibrary::setLocal(bool local) +{ + _local = local; +} + +std::shared_ptr>> +MaterialLibrary::getMaterialTree(const std::shared_ptr& filter, + const Materials::MaterialFilterOptions& options) const +{ + std::shared_ptr>> materialTree = + std::make_shared>>(); + + auto materials = MaterialManager::getManager().libraryMaterials(getName(), filter, options); + for (auto& it : *materials) { + auto uuid = std::get<0>(it); + auto path = std::get<1>(it); + auto filename = std::get<2>(it); + + QStringList list = path.split(QStringLiteral("/")); + + // Start at the root + std::shared_ptr>> node = + materialTree; + for (auto& itp : list) { + if (!itp.isEmpty()) { + // Add the folder only if it's not already there + if (node->count(itp) == 0) { + auto mapPtr = std::make_shared< + std::map>>(); + std::shared_ptr child = + std::make_shared(); + child->setFolder(mapPtr); + (*node)[itp] = child; + node = mapPtr; + } + else { + node = (*node)[itp]->getFolder(); + } + } + } + std::shared_ptr child = std::make_shared(); + child->setUUID(uuid); + (*node)[filename] = child; + } + + // // Empty folders aren't included in _materialPathMap, so we add them by looking at the file + // // system + // if (!filter || options.includeEmptyFolders()) { + // if (isLocal()) { + // auto& materialLibrary = + // *(reinterpret_cast(this)); + // auto folderList = MaterialLoader::getMaterialFolders(materialLibrary); + // for (auto& folder : *folderList) { + // QStringList list = folder.split(QStringLiteral("/")); + + // // Start at the root + // auto node = materialTree; + // for (auto& itp : list) { + // // Add the folder only if it's not already there + // if (node->count(itp) == 0) { + // std::shared_ptr>> + // mapPtr = std::make_shared< + // std::map>>(); + // std::shared_ptr child = + // std::make_shared(); + // child->setFolder(mapPtr); + // (*node)[itp] = child; + // node = mapPtr; + // } + // else { + // node = (*node)[itp]->getFolder(); + // } + // } + // } + // } + // } + + return materialTree; +} + +/* TRANSLATOR Material::Materials */ + +TYPESYSTEM_SOURCE(Materials::MaterialLibraryLocal, Materials::MaterialLibrary) + +MaterialLibraryLocal::MaterialLibraryLocal(const QString& libraryName, + const QString& dir, + const QString& icon, + bool readOnly) + : MaterialLibrary(libraryName, dir, icon, readOnly) + , _materialPathMap(std::make_unique>>()) +{ + setLocal(true); +} + +void MaterialLibraryLocal::createFolder(const QString& path) { QString filePath = getLocalPath(path); @@ -65,8 +168,42 @@ void MaterialLibrary::createFolder(const QString& path) } } +void MaterialLibraryLocal::renameFolder(const QString& oldPath, const QString& newPath) +{ + QString filePath = getLocalPath(oldPath); + QString newFilePath = getLocalPath(newPath); + + QDir fileDir(filePath); + if (fileDir.exists()) { + if (!fileDir.rename(filePath, newFilePath)) { + Base::Console().Error("Unable to rename directory path '%s'\n", + filePath.toStdString().c_str()); + } + } + + updatePaths(oldPath, newPath); +} + +void MaterialLibraryLocal::deleteRecursive(const QString& path) +{ + if (isRoot(path)) { + return; + } + + QString filePath = getLocalPath(path); + auto manager = MaterialManager::getManager(); + + QFileInfo info(filePath); + if (info.isDir()) { + deleteDir(manager, filePath); + } + else { + deleteFile(manager, filePath); + } +} + // This accepts the filesystem path as returned from getLocalPath -void MaterialLibrary::deleteDir(MaterialManager& manager, const QString& path) +void MaterialLibraryLocal::deleteDir(MaterialManager& manager, const QString& path) { // Remove the children first QDirIterator it(path, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); @@ -105,7 +242,7 @@ void MaterialLibrary::deleteDir(MaterialManager& manager, const QString& path) } // This accepts the filesystem path as returned from getLocalPath -void MaterialLibrary::deleteFile(MaterialManager& manager, const QString& path) +void MaterialLibraryLocal::deleteFile(MaterialManager& manager, const QString& path) { if (QFile::remove(path)) { // Remove from the map @@ -125,25 +262,7 @@ void MaterialLibrary::deleteFile(MaterialManager& manager, const QString& path) } } -void MaterialLibrary::deleteRecursive(const QString& path) -{ - if (isRoot(path)) { - return; - } - - QString filePath = getLocalPath(path); - MaterialManager manager; - - QFileInfo info(filePath); - if (info.isDir()) { - deleteDir(manager, filePath); - } - else { - deleteFile(manager, filePath); - } -} - -void MaterialLibrary::updatePaths(const QString& oldPath, const QString& newPath) +void MaterialLibraryLocal::updatePaths(const QString& oldPath, const QString& newPath) { // Update the path map QString op = getRelativePath(oldPath); @@ -162,27 +281,12 @@ void MaterialLibrary::updatePaths(const QString& oldPath, const QString& newPath _materialPathMap = std::move(pathMap); } -void MaterialLibrary::renameFolder(const QString& oldPath, const QString& newPath) -{ - QString filePath = getLocalPath(oldPath); - QString newFilePath = getLocalPath(newPath); - - QDir fileDir(filePath); - if (fileDir.exists()) { - if (!fileDir.rename(filePath, newFilePath)) { - Base::Console().Error("Unable to rename directory path '%s'\n", - filePath.toStdString().c_str()); - } - } - - updatePaths(oldPath, newPath); -} - -std::shared_ptr MaterialLibrary::saveMaterial(const std::shared_ptr& material, - const QString& path, - bool overwrite, - bool saveAsCopy, - bool saveInherited) +std::shared_ptr +MaterialLibraryLocal::saveMaterial(const std::shared_ptr& material, + const QString& path, + bool overwrite, + bool saveAsCopy, + bool saveInherited) { QString filePath = getLocalPath(path); QFile file(filePath); @@ -220,7 +324,7 @@ std::shared_ptr MaterialLibrary::saveMaterial(const std::shared_ptr MaterialLibrary::addMaterial(const std::shared_ptr& material, - const QString& path) +std::shared_ptr +MaterialLibraryLocal::addMaterial(const std::shared_ptr& material, const QString& path) { QString filePath = getRelativePath(path); + QFileInfo info(filePath); std::shared_ptr newMaterial = std::make_shared(*material); newMaterial->setLibrary(getptr()); - newMaterial->setDirectory(filePath); + newMaterial->setDirectory(getLibraryPath(filePath, info.fileName())); + newMaterial->setFilename(info.fileName()); (*_materialPathMap)[filePath] = newMaterial; return newMaterial; } -std::shared_ptr MaterialLibrary::getMaterialByPath(const QString& path) const +std::shared_ptr MaterialLibraryLocal::getMaterialByPath(const QString& path) const { QString filePath = getRelativePath(path); - try { - auto material = _materialPathMap->at(filePath); - return material; - } - catch (std::out_of_range&) { - throw MaterialNotFound(); + + auto search = _materialPathMap->find(filePath); + if (search != _materialPathMap->end()) { + return search->second; } + + throw MaterialNotFound(); } -QString MaterialLibrary::getUUIDFromPath(const QString& path) const +QString MaterialLibraryLocal::getUUIDFromPath(const QString& path) const { QString filePath = getRelativePath(path); - try { - auto material = _materialPathMap->at(filePath); - return material->getUUID(); - } - catch (std::out_of_range&) { - throw MaterialNotFound(); + + auto search = _materialPathMap->find(filePath); + if (search != _materialPathMap->end()) { + return search->second->getUUID(); } + + throw MaterialNotFound(); } - -bool MaterialLibrary::materialInTree(const std::shared_ptr& material, - const std::shared_ptr& filter, - const Materials::MaterialFilterOptions& options) const -{ - if (!filter) { - // If there's no filter we always include - return true; - } - - // filter out old format files - if (material->isOldFormat() && !options.includeLegacy()) { - return false; - } - - // filter based on models - return filter->modelIncluded(material); -} - -std::shared_ptr>> -MaterialLibrary::getMaterialTree(const std::shared_ptr& filter, - const Materials::MaterialFilterOptions& options) const -{ - std::shared_ptr>> materialTree = - std::make_shared>>(); - - for (auto& it : *_materialPathMap) { - auto filename = it.first; - auto material = it.second; - - if (materialInTree(material, filter, options)) { - QStringList list = filename.split(QStringLiteral("/")); - - // Start at the root - std::shared_ptr>> node = - materialTree; - for (auto& itp : list) { - if (itp.endsWith(QStringLiteral(".FCMat"))) { - std::shared_ptr child = std::make_shared(); - child->setData(material); - (*node)[itp] = child; - } - else { - // Add the folder only if it's not already there - if (node->count(itp) == 0) { - auto mapPtr = std::make_shared< - std::map>>(); - std::shared_ptr child = - std::make_shared(); - child->setFolder(mapPtr); - (*node)[itp] = child; - node = mapPtr; - } - else { - node = (*node)[itp]->getFolder(); - } - } - } - } - } - - // Empty folders aren't included in _materialPathMap, so we add them by looking at the file - // system - if (!filter || options.includeEmptyFolders()) { - auto folderList = MaterialLoader::getMaterialFolders(*this); - for (auto& folder : *folderList) { - QStringList list = folder.split(QStringLiteral("/")); - - // Start at the root - auto node = materialTree; - for (auto& itp : list) { - // Add the folder only if it's not already there - if (node->count(itp) == 0) { - std::shared_ptr>> mapPtr = - std::make_shared>>(); - std::shared_ptr child = std::make_shared(); - child->setFolder(mapPtr); - (*node)[itp] = child; - node = mapPtr; - } - else { - node = (*node)[itp]->getFolder(); - } - } - } - } - - return materialTree; -} - -TYPESYSTEM_SOURCE(Materials::MaterialExternalLibrary, Materials::MaterialLibrary) - -MaterialExternalLibrary::MaterialExternalLibrary(const QString& libraryName, - const QString& dir, - const QString& icon, - bool readOnly) - : MaterialLibrary(libraryName, dir, icon, readOnly) -{} diff --git a/src/Mod/Material/App/MaterialLibrary.h b/src/Mod/Material/App/MaterialLibrary.h index bb40951258..b1f4fc428c 100644 --- a/src/Mod/Material/App/MaterialLibrary.h +++ b/src/Mod/Material/App/MaterialLibrary.h @@ -31,6 +31,7 @@ #include #include +#include "Library.h" #include "Materials.h" #include "Model.h" #include "ModelLibrary.h" @@ -43,29 +44,50 @@ class MaterialManager; class MaterialFilter; class MaterialFilterOptions; -class MaterialsExport MaterialLibrary: public LibraryBase, - public std::enable_shared_from_this +class MaterialsExport MaterialLibrary + : public Library, + public std::enable_shared_from_this { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: MaterialLibrary() = default; - MaterialLibrary(const MaterialLibrary&) = delete; + MaterialLibrary(const QString& libraryName, const QString& icon, bool readOnly = true); MaterialLibrary(const QString& libraryName, const QString& dir, const QString& icon, bool readOnly = true); + MaterialLibrary(const MaterialLibrary&) = delete; ~MaterialLibrary() override = default; - bool operator==(const MaterialLibrary& library) const + bool isLocal() const; + void setLocal(bool local); + + virtual std::shared_ptr>> + getMaterialTree(const std::shared_ptr& filter, + const Materials::MaterialFilterOptions& options) const; + + // Use this to get a shared_ptr for *this + std::shared_ptr getptr() { - return LibraryBase::operator==(library); + return shared_from_this(); } - bool operator!=(const MaterialLibrary& library) const - { - return !operator==(library); - } - std::shared_ptr getMaterialByPath(const QString& path) const; + +protected: + bool _local; +}; + +class MaterialsExport MaterialLibraryLocal: public MaterialLibrary +{ + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + MaterialLibraryLocal() = default; + MaterialLibraryLocal(const QString& libraryName, + const QString& dir, + const QString& icon, + bool readOnly = true); + ~MaterialLibraryLocal() override = default; void createFolder(const QString& path); void renameFolder(const QString& oldPath, const QString& newPath); @@ -79,50 +101,39 @@ public: bool fileExists(const QString& path) const; std::shared_ptr addMaterial(const std::shared_ptr& material, const QString& path); - std::shared_ptr>> - getMaterialTree(const std::shared_ptr& filter, - const Materials::MaterialFilterOptions& options) const; + std::shared_ptr getMaterialByPath(const QString& path) const; - bool isReadOnly() const + bool operator==(const MaterialLibrary& library) const { - return _readOnly; + return library.isLocal() ? Library::operator==(library) : false; + } + bool operator!=(const MaterialLibrary& library) const + { + return !operator==(library); } - // Use this to get a shared_ptr for *this - std::shared_ptr getptr() + bool operator==(const MaterialLibraryLocal& library) const { - return shared_from_this(); + return Library::operator==(library); + } + bool operator!=(const MaterialLibraryLocal& library) const + { + return !operator==(library); } protected: void deleteDir(MaterialManager& manager, const QString& path); void deleteFile(MaterialManager& manager, const QString& path); - void updatePaths(const QString& oldPath, const QString& newPath); + QString getUUIDFromPath(const QString& path) const; - bool materialInTree(const std::shared_ptr& material, - const std::shared_ptr& filter, - const Materials::MaterialFilterOptions& options) const; - bool _readOnly; std::unique_ptr>> _materialPathMap; }; -class MaterialsExport MaterialExternalLibrary: public MaterialLibrary -{ - TYPESYSTEM_HEADER_WITH_OVERRIDE(); - -public: - MaterialExternalLibrary() = default; - MaterialExternalLibrary(const QString& libraryName, - const QString& dir, - const QString& icon, - bool readOnly = true); - ~MaterialExternalLibrary() override = default; -}; - } // namespace Materials Q_DECLARE_METATYPE(std::shared_ptr) +Q_DECLARE_METATYPE(std::shared_ptr) #endif // MATERIAL_MATERIALLIBRARY_H diff --git a/src/Mod/Material/App/MaterialLibraryPy.xml b/src/Mod/Material/App/MaterialLibraryPy.xml new file mode 100644 index 0000000000..2441a4eeaf --- /dev/null +++ b/src/Mod/Material/App/MaterialLibraryPy.xml @@ -0,0 +1,49 @@ + + + + + + Material library. + + + + Name of the library + + + + + + String value of the icon. + + + + + + Local directory where the library is located. For non-local libraries this will be empty + + + + + + True if the library is local. + + + + + + True if the library is local. + + + + + diff --git a/src/Mod/Material/App/MaterialLibraryPyImp.cpp b/src/Mod/Material/App/MaterialLibraryPyImp.cpp new file mode 100644 index 0000000000..58c788a674 --- /dev/null +++ b/src/Mod/Material/App/MaterialLibraryPyImp.cpp @@ -0,0 +1,125 @@ +/*************************************************************************** + * Copyright (c) 2023-2024 David Carter * + * * + * 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 "MaterialLibrary.h" + +#include "MaterialLibraryPy.h" + +#include "MaterialLibraryPy.cpp" + +using namespace Materials; + +// Forward declaration +// static PyObject* _pyObjectFromVariant(const QVariant& value); +// static Py::List getList(const QVariant& value); + +// returns a string which represents the object e.g. when printed in python +std::string MaterialLibraryPy::representation() const +{ + std::stringstream str; + str << ""; + + return str.str(); +} + +PyObject* MaterialLibraryPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper +{ + // never create such objects with the constructor + return new MaterialLibraryPy(new MaterialLibrary()); +} + +// constructor method +int MaterialLibraryPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) +{ + return 0; +} + +Py::String MaterialLibraryPy::getName() const +{ + auto filterName = getMaterialLibraryPtr()->getName(); + return {filterName.toStdString()}; +} + +void MaterialLibraryPy::setName(const Py::String value) +{ + getMaterialLibraryPtr()->setName(QString::fromStdString(value)); +} + +Py::String MaterialLibraryPy::getIcon() const +{ + auto path = getMaterialLibraryPtr()->getIconPath(); + return {path.toStdString()}; +} + +void MaterialLibraryPy::setIcon(const Py::String value) +{ + getMaterialLibraryPtr()->setIconPath(QString::fromStdString(value)); +} + +Py::String MaterialLibraryPy::getDirectory() const +{ + auto path = getMaterialLibraryPtr()->getDirectory(); + return {path.toStdString()}; +} + +void MaterialLibraryPy::setDirectory(const Py::String value) +{ + getMaterialLibraryPtr()->setDirectory(QString::fromStdString(value)); +} + +Py::Boolean MaterialLibraryPy::getReadOnly() const +{ + return getMaterialLibraryPtr()->isReadOnly(); +} + +void MaterialLibraryPy::setReadOnly(Py::Boolean value) +{ + getMaterialLibraryPtr()->setReadOnly(value); +} + +Py::Boolean MaterialLibraryPy::getLocal() const +{ + return getMaterialLibraryPtr()->isLocal(); +} + +void MaterialLibraryPy::setLocal(Py::Boolean value) +{ + getMaterialLibraryPtr()->setLocal(value); +} + +PyObject* MaterialLibraryPy::getCustomAttributes(const char* /*attr*/) const +{ + return nullptr; +} + +int MaterialLibraryPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +} diff --git a/src/Mod/Material/App/MaterialLoader.cpp b/src/Mod/Material/App/MaterialLoader.cpp index a4dd528002..7bdc4a881f 100644 --- a/src/Mod/Material/App/MaterialLoader.cpp +++ b/src/Mod/Material/App/MaterialLoader.cpp @@ -45,7 +45,7 @@ using namespace Materials; -MaterialEntry::MaterialEntry(const std::shared_ptr& library, +MaterialEntry::MaterialEntry(const std::shared_ptr& library, const QString& modelName, const QString& dir, const QString& modelUuid) @@ -55,7 +55,7 @@ MaterialEntry::MaterialEntry(const std::shared_ptr& library, , _uuid(modelUuid) {} -MaterialYamlEntry::MaterialYamlEntry(const std::shared_ptr& library, +MaterialYamlEntry::MaterialYamlEntry(const std::shared_ptr& library, const QString& modelName, const QString& dir, const QString& modelUuid, @@ -98,9 +98,9 @@ std::shared_ptr> MaterialYamlEntry::readImageList(const YAML::No return readList(node, true); } -std::shared_ptr MaterialYamlEntry::read2DArray(const YAML::Node& node, int columns) +std::shared_ptr MaterialYamlEntry::read2DArray(const YAML::Node& node, int columns) { - auto array2d = std::make_shared(); + auto array2d = std::make_shared(); array2d->setColumns(columns); if (node.size() == 1 || node.size() == 2) { @@ -126,9 +126,9 @@ std::shared_ptr MaterialYamlEntry::read2DArray(const YAML::Node return array2d; } -std::shared_ptr MaterialYamlEntry::read3DArray(const YAML::Node& node, int columns) +std::shared_ptr MaterialYamlEntry::read3DArray(const YAML::Node& node, int columns) { - auto array3d = std::make_shared(); + auto array3d = std::make_shared(); array3d->setColumns(columns - 1); // First column is third dimension if (node.size() == 1 || node.size() == 2) { @@ -347,16 +347,16 @@ MaterialLoader::MaterialLoader( : _materialMap(materialMap) , _libraryList(libraryList) { - loadLibraries(); + loadLibraries(libraryList); } -void MaterialLoader::addLibrary(const std::shared_ptr& model) +void MaterialLoader::addLibrary(const std::shared_ptr& model) { _libraryList->push_back(model); } std::shared_ptr -MaterialLoader::getMaterialFromYAML(const std::shared_ptr& library, +MaterialLoader::getMaterialFromYAML(const std::shared_ptr& library, YAML::Node& yamlroot, const QString& path) { @@ -387,18 +387,20 @@ MaterialLoader::getMaterialFromYAML(const std::shared_ptr& libr } std::shared_ptr -MaterialLoader::getMaterialFromPath(const std::shared_ptr& library, +MaterialLoader::getMaterialFromPath(const std::shared_ptr& library, const QString& path) const { std::shared_ptr model = nullptr; + auto materialLibrary = + reinterpret_cast&>(library); // Used for debugging std::string pathName = path.toStdString(); if (MaterialConfigLoader::isConfigStyle(path)) { - auto material = MaterialConfigLoader::getMaterialFromPath(library, path); + auto material = MaterialConfigLoader::getMaterialFromPath(materialLibrary, path); if (material) { - (*_materialMap)[material->getUUID()] = library->addMaterial(material, path); + (*_materialMap)[material->getUUID()] = materialLibrary->addMaterial(material, path); } // Return the nullptr as there are no intermediate steps to take, such @@ -417,7 +419,7 @@ MaterialLoader::getMaterialFromPath(const std::shared_ptr& libr try { yamlroot = YAML::Load(fin); - model = getMaterialFromYAML(library, yamlroot, path); + model = getMaterialFromYAML(materialLibrary, yamlroot, path); } catch (YAML::Exception const& e) { Base::Console().Error("YAML parsing error: '%s'\n", pathName.c_str()); @@ -511,7 +513,7 @@ void MaterialLoader::dereference(const std::shared_ptr& material) dereference(_materialMap, material); } -void MaterialLoader::loadLibrary(const std::shared_ptr& library) +void MaterialLoader::loadLibrary(const std::shared_ptr& library) { if (_materialEntryMap == nullptr) { _materialEntryMap = std::make_unique>>(); @@ -541,12 +543,16 @@ void MaterialLoader::loadLibrary(const std::shared_ptr& library } } -void MaterialLoader::loadLibraries() +void MaterialLoader::loadLibraries( + const std::shared_ptr>>& libraryList) { - auto _libraryList = getMaterialLibraries(); - if (_libraryList) { - for (auto& it : *_libraryList) { - loadLibrary(it); + if (libraryList) { + for (auto& it : *libraryList) { + if (it->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(it); + loadLibrary(materialLibrary); + } } } @@ -555,92 +561,8 @@ void MaterialLoader::loadLibraries() } } -std::shared_ptr>> MaterialLoader::getMaterialLibraries() -{ - auto param = App::GetApplication().GetParameterGroupByPath( - "User parameter:BaseApp/Preferences/Mod/Material/Resources"); - bool useBuiltInMaterials = param->GetBool("UseBuiltInMaterials", true); - bool useMatFromModules = param->GetBool("UseMaterialsFromWorkbenches", true); - bool useMatFromConfigDir = param->GetBool("UseMaterialsFromConfigDir", true); - bool useMatFromCustomDir = param->GetBool("UseMaterialsFromCustomDir", true); - - if (useBuiltInMaterials) { - QString resourceDir = QString::fromStdString(App::Application::getResourceDir() - + "/Mod/Material/Resources/Materials"); - auto libData = - std::make_shared(QStringLiteral("System"), - resourceDir, - QStringLiteral(":/icons/freecad.svg"), - true); - _libraryList->push_back(libData); - } - - if (useMatFromModules) { - auto moduleParam = App::GetApplication().GetParameterGroupByPath( - "User parameter:BaseApp/Preferences/Mod/Material/Resources/Modules"); - for (auto& group : moduleParam->GetGroups()) { - // auto module = moduleParam->GetGroup(group->GetGroupName()); - auto moduleName = QString::fromStdString(group->GetGroupName()); - auto materialDir = QString::fromStdString(group->GetASCII("ModuleDir", "")); - auto materialIcon = QString::fromStdString(group->GetASCII("ModuleIcon", "")); - auto materialReadOnly = group->GetBool("ModuleReadOnly", true); - - if (materialDir.length() > 0) { - QDir dir(materialDir); - if (dir.exists()) { - auto libData = std::make_shared(moduleName, - materialDir, - materialIcon, - materialReadOnly); - _libraryList->push_back(libData); - } - } - } - } - - if (useMatFromConfigDir) { - QString resourceDir = - QString::fromStdString(App::Application::getUserAppDataDir() + "/Material"); - if (!resourceDir.isEmpty()) { - QDir materialDir(resourceDir); - if (!materialDir.exists()) { - // Try creating the user dir if it doesn't exist - if (!materialDir.mkpath(resourceDir)) { - Base::Console().Log("Unable to create user library '%s'\n", - resourceDir.toStdString().c_str()); - } - } - if (materialDir.exists()) { - auto libData = std::make_shared( - QStringLiteral("User"), - resourceDir, - QStringLiteral(":/icons/preferences-general.svg"), - false); - _libraryList->push_back(libData); - } - } - } - - if (useMatFromCustomDir) { - QString resourceDir = QString::fromStdString(param->GetASCII("CustomMaterialsDir", "")); - if (!resourceDir.isEmpty()) { - QDir materialDir(resourceDir); - if (materialDir.exists()) { - auto libData = - std::make_shared(QStringLiteral("Custom"), - resourceDir, - QStringLiteral(":/icons/user.svg"), - false); - _libraryList->push_back(libData); - } - } - } - - return _libraryList; -} - std::shared_ptr> -MaterialLoader::getMaterialFolders(const MaterialLibrary& library) +MaterialLoader::getMaterialFolders(const MaterialLibraryLocal& library) { std::shared_ptr> pathList = std::make_shared>(); QDirIterator it(library.getDirectory(), QDirIterator::Subdirectories); diff --git a/src/Mod/Material/App/MaterialLoader.h b/src/Mod/Material/App/MaterialLoader.h index 1d1eb9d7bb..cf48ff443b 100644 --- a/src/Mod/Material/App/MaterialLoader.h +++ b/src/Mod/Material/App/MaterialLoader.h @@ -33,12 +33,14 @@ namespace Materials { +class MaterialLibrary; +class MaterialLibraryLocal; class MaterialEntry { public: MaterialEntry() = default; - MaterialEntry(const std::shared_ptr& library, + MaterialEntry(const std::shared_ptr& library, const QString& modelName, const QString& dir, const QString& modelUuid); @@ -47,7 +49,7 @@ public: virtual void addToTree(std::shared_ptr>> materialMap) = 0; - std::shared_ptr getLibrary() const + std::shared_ptr getLibrary() const { return _library; } @@ -65,7 +67,7 @@ public: } protected: - std::shared_ptr _library; + std::shared_ptr _library; QString _name; QString _directory; QString _uuid; @@ -74,7 +76,7 @@ protected: class MaterialYamlEntry: public MaterialEntry { public: - MaterialYamlEntry(const std::shared_ptr& library, + MaterialYamlEntry(const std::shared_ptr& library, const QString& modelName, const QString& dir, const QString& modelUuid, @@ -101,8 +103,8 @@ private: static std::shared_ptr> readList(const YAML::Node& node, bool isImageList = false); static std::shared_ptr> readImageList(const YAML::Node& node); - static std::shared_ptr read2DArray(const YAML::Node& node, int columns); - static std::shared_ptr read3DArray(const YAML::Node& node, int columns); + static std::shared_ptr read2DArray(const YAML::Node& node, int columns); + static std::shared_ptr read3DArray(const YAML::Node& node, int columns); YAML::Node _model; }; @@ -114,14 +116,14 @@ public: const std::shared_ptr>>& libraryList); ~MaterialLoader() = default; - std::shared_ptr>> getMaterialLibraries(); - static std::shared_ptr> getMaterialFolders(const MaterialLibrary& library); + static std::shared_ptr> + getMaterialFolders(const MaterialLibraryLocal& library); static void showYaml(const YAML::Node& yaml); static void dereference(const std::shared_ptr>>& materialMap, const std::shared_ptr& material); static std::shared_ptr - getMaterialFromYAML(const std::shared_ptr& library, + getMaterialFromYAML(const std::shared_ptr& library, YAML::Node& yamlroot, const QString& path); @@ -131,10 +133,11 @@ private: void addToTree(std::shared_ptr model); void dereference(const std::shared_ptr& material); std::shared_ptr - getMaterialFromPath(const std::shared_ptr& library, const QString& path) const; - void addLibrary(const std::shared_ptr& model); - void loadLibrary(const std::shared_ptr& library); - void loadLibraries(); + getMaterialFromPath(const std::shared_ptr& library, const QString& path) const; + void addLibrary(const std::shared_ptr& model); + void loadLibrary(const std::shared_ptr& library); + void loadLibraries( + const std::shared_ptr>>& libraryList); static std::unique_ptr>> _materialEntryMap; std::shared_ptr>> _materialMap; diff --git a/src/Mod/Material/App/MaterialManager.cpp b/src/Mod/Material/App/MaterialManager.cpp index 5b23f288bb..eabe92a15b 100644 --- a/src/Mod/Material/App/MaterialManager.cpp +++ b/src/Mod/Material/App/MaterialManager.cpp @@ -24,8 +24,8 @@ #include #endif -#include #include +#include #include #include @@ -35,6 +35,7 @@ #include "MaterialConfigLoader.h" #include "MaterialLoader.h" #include "MaterialManager.h" +#include "MaterialManagerLocal.h" #include "ModelManager.h" #include "ModelUuids.h" @@ -45,100 +46,56 @@ using namespace Materials; /* TRANSLATOR Material::Materials */ -std::shared_ptr>> MaterialManager::_libraryList = - nullptr; -std::shared_ptr>> MaterialManager::_materialMap = - nullptr; -QMutex MaterialManager::_mutex; - TYPESYSTEM_SOURCE(Materials::MaterialManager, Base::BaseClass) +QMutex MaterialManager::_mutex; +MaterialManager* MaterialManager::_manager = nullptr; +std::unique_ptr MaterialManager::_localManager; + MaterialManager::MaterialManager() +{} + +MaterialManager::~MaterialManager() +{} + +MaterialManager& MaterialManager::getManager() { - // TODO: Add a mutex or similar - initLibraries(); + if (!_manager) { + initManagers(); + } + return *_manager; } -void MaterialManager::initLibraries() +void MaterialManager::initManagers() { QMutexLocker locker(&_mutex); - if (_materialMap == nullptr) { - // Load the models first - auto manager = std::make_unique(); - Q_UNUSED(manager) - - _materialMap = std::make_shared>>(); - - if (_libraryList == nullptr) { - _libraryList = std::make_shared>>(); - } - - // Load the libraries - MaterialLoader loader(_materialMap, _libraryList); + if (!_manager) { + // Can't use smart pointers for this since the constructor is private + _manager = new MaterialManager(); + } + if (!_localManager) { + _localManager = std::make_unique(); } } void MaterialManager::cleanup() { - QMutexLocker locker(&_mutex); - - if (_libraryList) { - _libraryList->clear(); - _libraryList = nullptr; - } - - if (_materialMap) { - for (auto& it : *_materialMap) { - // This is needed to resolve cyclic dependencies - it.second->setLibrary(nullptr); - } - _materialMap->clear(); - _materialMap = nullptr; + if (_localManager) { + _localManager->cleanup(); } } void MaterialManager::refresh() { - // This is very expensive and can be improved using observers? - cleanup(); - initLibraries(); + _localManager->refresh(); } -void MaterialManager::saveMaterial(const std::shared_ptr& library, - const std::shared_ptr& material, - const QString& path, - bool overwrite, - bool saveAsCopy, - bool saveInherited) const -{ - auto newMaterial = library->saveMaterial(material, path, overwrite, saveAsCopy, saveInherited); - (*_materialMap)[newMaterial->getUUID()] = newMaterial; -} - -bool MaterialManager::isMaterial(const fs::path& p) const -{ - if (!fs::is_regular_file(p)) { - return false; - } - // check file extension - if (p.extension() == ".FCMat") { - return true; - } - return false; -} - -bool MaterialManager::isMaterial(const QFileInfo& file) const -{ - if (!file.isFile()) { - return false; - } - // check file extension - if (file.suffix() == QStringLiteral("FCMat")) { - return true; - } - return false; -} +//===== +// +// Defaults +// +//===== std::shared_ptr MaterialManager::defaultAppearance() { @@ -149,7 +106,7 @@ std::shared_ptr MaterialManager::defaultAppearance() uint32_t packed = color.getPackedRGB(); packed = hGrp->GetUnsigned(parameter, packed); color.setPackedRGB(packed); - color.a = 1.0; // The default color sets fully transparent, not opaque + color.a = 1.0; // The default color sets fully transparent, not opaque }; auto intRandom = [](int min, int max) -> int { static std::mt19937 generator; @@ -187,9 +144,9 @@ std::shared_ptr MaterialManager::defaultMaterial() MaterialManager manager; auto mat = defaultAppearance(); - auto material = manager.getMaterial(defaultMaterialUUID()); + auto material = getManager().getMaterial(defaultMaterialUUID()); if (!material) { - material = manager.getMaterial(QLatin1String("7f9fd73b-50c9-41d8-b7b2-575a030c1eeb")); + material = getManager().getMaterial(QStringLiteral("7f9fd73b-50c9-41d8-b7b2-575a030c1eeb")); } if (material->hasAppearanceModel(ModelUUIDs::ModelUUID_Rendering_Basic)) { material->getAppearanceProperty(QStringLiteral("DiffuseColor")) @@ -218,14 +175,178 @@ QString MaterialManager::defaultMaterialUUID() return QString::fromStdString(uuid); } +//===== +// +// Library management +// +//===== + +std::shared_ptr>> MaterialManager::getLibraries() +{ + auto libraries = std::make_shared>>(); + auto localLibraries = _localManager->getLibraries(); + for (auto& local : *localLibraries) { + libraries->push_back(local); + } + + return libraries; +} + +std::shared_ptr>> MaterialManager::getLocalLibraries() +{ + return _localManager->getLibraries(); +} + +std::shared_ptr MaterialManager::getLibrary(const QString& name) const +{ + return _localManager->getLibrary(name); +} + +void MaterialManager::createLibrary(const QString& libraryName, const QString& icon, bool readOnly) +{ + throw CreationError("Local library requires a path"); +} + +void MaterialManager::createLocalLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly) +{ + _localManager->createLibrary(libraryName, directory, icon, readOnly); +} + +void MaterialManager::renameLibrary(const QString& libraryName, const QString& newName) +{ + _localManager->renameLibrary(libraryName, newName); +} + +void MaterialManager::changeIcon(const QString& libraryName, const QString& icon) +{ + _localManager->changeIcon(libraryName, icon); +} + +void MaterialManager::removeLibrary(const QString& libraryName) +{ + _localManager->removeLibrary(libraryName); +} + +std::shared_ptr>> +MaterialManager::libraryMaterials(const QString& libraryName) +{ + return _localManager->libraryMaterials(libraryName); +} + +std::shared_ptr>> +MaterialManager::libraryMaterials(const QString& libraryName, + const std::shared_ptr& filter, + const MaterialFilterOptions& options) +{ + return _localManager->libraryMaterials(libraryName, filter, options); +} + +bool MaterialManager::isLocalLibrary(const QString& libraryName) +{ + return true; +} + +//===== +// +// Folder management +// +//===== + +std::shared_ptr> +MaterialManager::getMaterialFolders(const std::shared_ptr& library) const +{ + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + + return _localManager->getMaterialFolders(materialLibrary); + } + + return std::make_shared>(); +} + +void MaterialManager::createFolder(const std::shared_ptr& library, + const QString& path) +{ + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + + _localManager->createFolder(materialLibrary, path); + } +} + +void MaterialManager::renameFolder(const std::shared_ptr& library, + const QString& oldPath, + const QString& newPath) +{ + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + + _localManager->renameFolder(materialLibrary, oldPath, newPath); + } +} + +void MaterialManager::deleteRecursive(const std::shared_ptr& library, + const QString& path) +{ + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + + _localManager->deleteRecursive(materialLibrary, path); + } +} + +//===== +// +// Tree management +// +//===== + +std::shared_ptr>> +MaterialManager::getMaterialTree(const std::shared_ptr& library, + const std::shared_ptr& filter) const +{ + MaterialFilterOptions options; + return library->getMaterialTree(filter, options); +} + +std::shared_ptr>> +MaterialManager::getMaterialTree(const std::shared_ptr& library, + const std::shared_ptr& filter, + const MaterialFilterOptions& options) const +{ + return library->getMaterialTree(filter, options); +} + +std::shared_ptr>> +MaterialManager::getMaterialTree(const std::shared_ptr& library) const +{ + std::shared_ptr filter; + MaterialFilterOptions options; + return library->getMaterialTree(filter, options); +} + +//===== +// +// Material management +// +//===== + +std::shared_ptr>> +MaterialManager::getLocalMaterials() const +{ + return _localManager->getLocalMaterials(); +} + std::shared_ptr MaterialManager::getMaterial(const QString& uuid) const { - try { - return _materialMap->at(uuid); - } - catch (std::out_of_range&) { - throw MaterialNotFound(); - } + return _localManager->getMaterial(uuid); } std::shared_ptr MaterialManager::getMaterial(const App::Material& material) @@ -237,65 +358,13 @@ std::shared_ptr MaterialManager::getMaterial(const App::Material& mate std::shared_ptr MaterialManager::getMaterialByPath(const QString& path) const { - QString cleanPath = QDir::cleanPath(path); - - for (auto& library : *_libraryList) { - if (cleanPath.startsWith(library->getDirectory())) { - try { - return library->getMaterialByPath(cleanPath); - } - catch (const MaterialNotFound&) { - } - - // See if it's a new file saved by the old editor - { - QMutexLocker locker(&_mutex); - - if (MaterialConfigLoader::isConfigStyle(path)) { - auto material = MaterialConfigLoader::getMaterialFromPath(library, path); - if (material) { - (*_materialMap)[material->getUUID()] = library->addMaterial(material, path); - } - - return material; - } - } - } - } - - // Older workbenches may try files outside the context of a library - { - QMutexLocker locker(&_mutex); - - if (MaterialConfigLoader::isConfigStyle(path)) { - auto material = MaterialConfigLoader::getMaterialFromPath(nullptr, path); - - return material; - } - } - - throw MaterialNotFound(); + return _localManager->getMaterialByPath(path); } std::shared_ptr MaterialManager::getMaterialByPath(const QString& path, const QString& lib) const { - auto library = getLibrary(lib); // May throw LibraryNotFound - return library->getMaterialByPath(path); // May throw MaterialNotFound -} - -bool MaterialManager::exists(const QString& uuid) const -{ - try { - auto material = getMaterial(uuid); - if (material) { - return true; - } - } - catch (const MaterialNotFound&) { - } - - return false; + return _localManager->getMaterialByPath(path, lib); } std::shared_ptr @@ -308,105 +377,69 @@ MaterialManager::getParent(const std::shared_ptr& material) const return getMaterial(material->getParentUUID()); } +bool MaterialManager::exists(const QString& uuid) const +{ + return _localManager->exists(uuid); +} + bool MaterialManager::exists(const std::shared_ptr& library, const QString& uuid) const { - try { - auto material = getMaterial(uuid); - if (material) { - return (*material->getLibrary() == *library); - } - } - catch (const MaterialNotFound&) { - } + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + return _localManager->exists(materialLibrary, uuid); + } return false; } -std::shared_ptr MaterialManager::getLibrary(const QString& name) const +void MaterialManager::remove(const QString& uuid) const { - for (auto& library : *_libraryList) { - if (library->getName() == name) { - return library; - } - } - - throw LibraryNotFound(); + _localManager->remove(uuid); } -std::shared_ptr>> -MaterialManager::getMaterialLibraries() const +void MaterialManager::saveMaterial(const std::shared_ptr& library, + const std::shared_ptr& material, + const QString& path, + bool overwrite, + bool saveAsCopy, + bool saveInherited) const { - if (_libraryList == nullptr) { - if (_materialMap == nullptr) { - _materialMap = std::make_shared>>(); - } - _libraryList = std::make_shared>>(); - - // Load the libraries - MaterialLoader loader(_materialMap, _libraryList); - } - return _libraryList; + auto materialLibrary = + reinterpret_cast&>(library); + _localManager + ->saveMaterial(materialLibrary, material, path, overwrite, saveAsCopy, saveInherited); } -std::shared_ptr> -MaterialManager::getMaterialFolders(const std::shared_ptr& library) const +bool MaterialManager::isMaterial(const fs::path& p) const { - return MaterialLoader::getMaterialFolders(*library); + return _localManager->isMaterial(p); +} + +bool MaterialManager::isMaterial(const QFileInfo& file) const +{ + return _localManager->isMaterial(file); } std::shared_ptr>> MaterialManager::materialsWithModel(const QString& uuid) const { - std::shared_ptr>> dict = - std::make_shared>>(); - - for (auto& it : *_materialMap) { - QString key = it.first; - auto material = it.second; - - if (material->hasModel(uuid)) { - (*dict)[key] = material; - } - } - - return dict; + return _localManager->materialsWithModel(uuid); } std::shared_ptr>> MaterialManager::materialsWithModelComplete(const QString& uuid) const { - std::shared_ptr>> dict = - std::make_shared>>(); - - for (auto& it : *_materialMap) { - QString key = it.first; - auto material = it.second; - - if (material->isModelComplete(uuid)) { - (*dict)[key] = material; - } - } - - return dict; + return _localManager->materialsWithModelComplete(uuid); } void MaterialManager::dereference() const { - // First clear the inheritences - for (auto& it : *_materialMap) { - auto material = it.second; - material->clearDereferenced(); - material->clearInherited(); - } - - // Run the dereference again - for (auto& it : *_materialMap) { - dereference(it.second); - } + _localManager->dereference(); } void MaterialManager::dereference(std::shared_ptr material) const { - MaterialLoader::dereference(_materialMap, material); + _localManager->dereference(material); } diff --git a/src/Mod/Material/App/MaterialManager.h b/src/Mod/Material/App/MaterialManager.h index 63f06dc772..c3ec6bb517 100644 --- a/src/Mod/Material/App/MaterialManager.h +++ b/src/Mod/Material/App/MaterialManager.h @@ -26,13 +26,14 @@ #include +#include #include #include "FolderTree.h" #include "Materials.h" -#include "MaterialLibrary.h" #include "MaterialFilter.h" +#include "MaterialLibrary.h" namespace fs = std::filesystem; @@ -45,78 +46,78 @@ class Material; namespace Materials { +class MaterialManagerExternal; +class MaterialManagerLocal; +class MaterialFilter; +class MaterialFilterOptions; class MaterialsExport MaterialManager: public Base::BaseClass { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: - MaterialManager(); - ~MaterialManager() override = default; + ~MaterialManager() override; + + static MaterialManager& getManager(); static void cleanup(); static void refresh(); + + // Defaults static std::shared_ptr defaultAppearance(); static std::shared_ptr defaultMaterial(); static QString defaultMaterialUUID(); - std::shared_ptr>> getMaterials() const - { - return _materialMap; - } + // Library management + std::shared_ptr>> getLibraries(); + std::shared_ptr>> getLocalLibraries(); + std::shared_ptr getLibrary(const QString& name) const; + void createLibrary(const QString& libraryName, const QString& icon, bool readOnly = true); + void createLocalLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly = true); + void renameLibrary(const QString& libraryName, const QString& newName); + void changeIcon(const QString& libraryName, const QString& icon); + void removeLibrary(const QString& libraryName); + std::shared_ptr>> + libraryMaterials(const QString& libraryName); + std::shared_ptr>> + libraryMaterials(const QString& libraryName, + const std::shared_ptr& filter, + const MaterialFilterOptions& options); + bool isLocalLibrary(const QString& libraryName); + + // Folder management + std::shared_ptr> + getMaterialFolders(const std::shared_ptr& library) const; + void createFolder(const std::shared_ptr& library, const QString& path); + void renameFolder(const std::shared_ptr& library, + const QString& oldPath, + const QString& newPath); + void deleteRecursive(const std::shared_ptr& library, const QString& path); + + // Tree management + std::shared_ptr>> + getMaterialTree(const std::shared_ptr& library, + const std::shared_ptr& filter) const; + std::shared_ptr>> + getMaterialTree(const std::shared_ptr& library, + const std::shared_ptr& filter, + const MaterialFilterOptions& options) const; + std::shared_ptr>> + getMaterialTree(const std::shared_ptr& library) const; + + // Material management + std::shared_ptr>> getLocalMaterials() const; std::shared_ptr getMaterial(const QString& uuid) const; static std::shared_ptr getMaterial(const App::Material& material); std::shared_ptr getMaterialByPath(const QString& path) const; std::shared_ptr getMaterialByPath(const QString& path, const QString& library) const; std::shared_ptr getParent(const std::shared_ptr& material) const; - std::shared_ptr getLibrary(const QString& name) const; bool exists(const QString& uuid) const; bool exists(const std::shared_ptr& library, const QString& uuid) const; - - // Library management - std::shared_ptr>> getMaterialLibraries() const; - std::shared_ptr>> - getMaterialTree(const std::shared_ptr& library, - const std::shared_ptr& filter) const - { - MaterialFilterOptions options; - return library->getMaterialTree(filter, options); - } - std::shared_ptr>> - getMaterialTree(const std::shared_ptr& library, - const std::shared_ptr& filter, - const MaterialFilterOptions& options) const - { - return library->getMaterialTree(filter, options); - } - std::shared_ptr>> - getMaterialTree(const std::shared_ptr& library) const - { - std::shared_ptr filter; - MaterialFilterOptions options; - return library->getMaterialTree(filter, options); - } - std::shared_ptr> - getMaterialFolders(const std::shared_ptr& library) const; - void createFolder(const std::shared_ptr& library, const QString& path) const - { - library->createFolder(path); - } - void renameFolder(const std::shared_ptr& library, - const QString& oldPath, - const QString& newPath) const - { - library->renameFolder(oldPath, newPath); - } - void deleteRecursive(const std::shared_ptr& library, const QString& path) const - { - library->deleteRecursive(path); - dereference(); - } - void remove(const QString& uuid) const - { - _materialMap->erase(uuid); - } + void remove(const QString& uuid) const; void saveMaterial(const std::shared_ptr& library, const std::shared_ptr& material, @@ -136,13 +137,15 @@ public: void dereference() const; private: - static std::shared_ptr>> _libraryList; - static std::shared_ptr>> _materialMap; - static QMutex _mutex; + MaterialManager(); + static void initManagers(); - static void initLibraries(); + static MaterialManager* _manager; + + static std::unique_ptr _localManager; + static QMutex _mutex; }; } // namespace Materials -#endif // MATERIAL_MATERIALMANAGER_H +#endif // MATERIAL_MATERIALMANAGER_H \ No newline at end of file diff --git a/src/Mod/Material/App/MaterialManagerLocal.cpp b/src/Mod/Material/App/MaterialManagerLocal.cpp new file mode 100644 index 0000000000..42b3daabe1 --- /dev/null +++ b/src/Mod/Material/App/MaterialManagerLocal.cpp @@ -0,0 +1,593 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * 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 + +#include "Exceptions.h" +#include "MaterialConfigLoader.h" +#include "MaterialFilter.h" +#include "MaterialLibrary.h" +#include "MaterialLoader.h" +#include "MaterialManagerLocal.h" +#include "ModelManager.h" +#include "ModelUuids.h" + + +using namespace Materials; + +/* TRANSLATOR Material::Materials */ + +std::shared_ptr>> + MaterialManagerLocal::_libraryList = nullptr; +std::shared_ptr>> MaterialManagerLocal::_materialMap = + nullptr; +QMutex MaterialManagerLocal::_mutex; + +TYPESYSTEM_SOURCE(Materials::MaterialManagerLocal, Base::BaseClass) + +MaterialManagerLocal::MaterialManagerLocal() +{ + // TODO: Add a mutex or similar + initLibraries(); +} + +void MaterialManagerLocal::initLibraries() +{ + QMutexLocker locker(&_mutex); + + if (_materialMap == nullptr) { + // Load the models first + ModelManager::getManager(); + + _materialMap = std::make_shared>>(); + + if (_libraryList == nullptr) { + _libraryList = getConfiguredLibraries(); + } + + // Load the libraries + MaterialLoader loader(_materialMap, _libraryList); + } +} + +void MaterialManagerLocal::cleanup() +{ + QMutexLocker locker(&_mutex); + + if (_libraryList) { + _libraryList->clear(); + _libraryList = nullptr; + } + + if (_materialMap) { + for (auto& it : *_materialMap) { + // This is needed to resolve cyclic dependencies + it.second->setLibrary(nullptr); + } + _materialMap->clear(); + _materialMap = nullptr; + } +} + +void MaterialManagerLocal::refresh() +{ + // This is very expensive and can be improved using observers? + cleanup(); + initLibraries(); +} + +//===== +// +// Library management +// +//===== + +std::shared_ptr>> MaterialManagerLocal::getLibraries() +{ + if (_libraryList == nullptr) { + initLibraries(); + } + return _libraryList; +} + +std::shared_ptr>> +MaterialManagerLocal::getMaterialLibraries() +{ + if (_libraryList == nullptr) { + initLibraries(); + } + return _libraryList; +} + +std::shared_ptr MaterialManagerLocal::getLibrary(const QString& name) const +{ + for (auto& library : *_libraryList) { + if (library->isLocal() && library->sameName(name)) { + return library; + } + } + + throw LibraryNotFound(); +} + +void MaterialManagerLocal::createLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly) +{ + QDir dir; + if (!dir.exists(directory)) { + if (!dir.mkpath(directory)) { + throw CreationError("Unable to create library path"); + } + } + + auto materialLibrary = + std::make_shared(libraryName, directory, icon, readOnly); + _libraryList->push_back(materialLibrary); + + // This needs to be persisted somehow +} + +void MaterialManagerLocal::renameLibrary(const QString& libraryName, const QString& newName) +{ + for (auto& library : *_libraryList) { + if (library->isLocal() && library->sameName(libraryName)) { + auto materialLibrary = + reinterpret_cast&>(library); + materialLibrary->setName(newName); + return; + } + } + + throw LibraryNotFound(); +} + +void MaterialManagerLocal::changeIcon(const QString& libraryName, const QString& icon) +{ + for (auto& library : *_libraryList) { + if (library->isLocal() && library->sameName(libraryName)) { + auto materialLibrary = + reinterpret_cast&>(library); + materialLibrary->setIconPath(icon); + return; + } + } + + throw LibraryNotFound(); +} + +void MaterialManagerLocal::removeLibrary(const QString& libraryName) +{ + for (auto& library : *_libraryList) { + if (library->isLocal() && library->sameName(libraryName)) { + _libraryList->remove(library); + + // At this point we should rebuild the material map + return; + } + } + + throw LibraryNotFound(); +} + +std::shared_ptr>> +MaterialManagerLocal::libraryMaterials(const QString& libraryName) +{ + auto materials = std::make_shared>>(); + + for (auto& it : *_materialMap) { + // This is needed to resolve cyclic dependencies + auto library = it.second->getLibrary(); + if (library->sameName(libraryName)) { + materials->push_back(std::tuple(it.first, + it.second->getDirectory(), + it.second->getName())); + } + } + + return materials; +} + +bool MaterialManagerLocal::passFilter(const std::shared_ptr& material, + const std::shared_ptr& filter, + const Materials::MaterialFilterOptions& options) const +{ + if (!filter) { + // If there's no filter we always include + return true; + } + + // filter out old format files + if (material->isOldFormat() && !options.includeLegacy()) { + return false; + } + + // filter based on models + return filter->modelIncluded(material); +} + +std::shared_ptr>> +MaterialManagerLocal::libraryMaterials(const QString& libraryName, + const std::shared_ptr& filter, + const MaterialFilterOptions& options) +{ + auto materials = std::make_shared>>(); + + for (auto& it : *_materialMap) { + // This is needed to resolve cyclic dependencies + auto library = it.second->getLibrary(); + if (library->sameName(libraryName)) { + if (passFilter(it.second, filter, options)) { + materials->push_back(std::tuple(it.first, + it.second->getDirectory(), + it.second->getName())); + } + } + } + + return materials; +} + +//===== +// +// Folder management +// +//===== + +std::shared_ptr> +MaterialManagerLocal::getMaterialFolders(const std::shared_ptr& library) const +{ + // auto materialLibrary = + // reinterpret_cast&>(library); + return MaterialLoader::getMaterialFolders(*library); +} + +void MaterialManagerLocal::createFolder(const std::shared_ptr& library, + const QString& path) +{ + library->createFolder(path); +} + +void MaterialManagerLocal::renameFolder(const std::shared_ptr& library, + const QString& oldPath, + const QString& newPath) +{ + library->renameFolder(oldPath, newPath); +} + +void MaterialManagerLocal::deleteRecursive(const std::shared_ptr& library, + const QString& path) +{ + library->deleteRecursive(path); + dereference(); +} + +//===== +// +// Material management +// +//===== + +std::shared_ptr>> +MaterialManagerLocal::getLocalMaterials() const +{ + return _materialMap; +} + +std::shared_ptr MaterialManagerLocal::getMaterial(const QString& uuid) const +{ + try { + return _materialMap->at(uuid); + } + catch (std::out_of_range&) { + throw MaterialNotFound(); + } +} + +std::shared_ptr MaterialManagerLocal::getMaterialByPath(const QString& path) const +{ + QString cleanPath = QDir::cleanPath(path); + + for (auto& library : *_libraryList) { + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + if (cleanPath.startsWith(materialLibrary->getDirectory())) { + try { + return materialLibrary->getMaterialByPath(cleanPath); + } + catch (const MaterialNotFound&) { + } + + // See if it's a new file saved by the old editor + { + QMutexLocker locker(&_mutex); + + if (MaterialConfigLoader::isConfigStyle(path)) { + auto material = + MaterialConfigLoader::getMaterialFromPath(materialLibrary, path); + if (material) { + (*_materialMap)[material->getUUID()] = + materialLibrary->addMaterial(material, path); + } + + return material; + } + } + } + } + } + + // Older workbenches may try files outside the context of a library + { + QMutexLocker locker(&_mutex); + + if (MaterialConfigLoader::isConfigStyle(path)) { + auto material = MaterialConfigLoader::getMaterialFromPath(nullptr, path); + + return material; + } + } + + throw MaterialNotFound(); +} + +std::shared_ptr MaterialManagerLocal::getMaterialByPath(const QString& path, + const QString& lib) const +{ + auto library = getLibrary(lib); // May throw LibraryNotFound + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + return materialLibrary->getMaterialByPath(path); // May throw MaterialNotFound + } + + throw LibraryNotFound(); +} + +bool MaterialManagerLocal::exists(const QString& uuid) const +{ + try { + auto material = getMaterial(uuid); + if (material) { + return true; + } + } + catch (const MaterialNotFound&) { + } + + return false; +} + +bool MaterialManagerLocal::exists(const std::shared_ptr& library, + const QString& uuid) const +{ + try { + auto material = getMaterial(uuid); + if (material && material->getLibrary()->isLocal()) { + auto materialLibrary = + reinterpret_cast&>( + *(material->getLibrary())); + return (*materialLibrary == *library); + } + } + catch (const MaterialNotFound&) { + } + + return false; +} + +void MaterialManagerLocal::remove(const QString& uuid) +{ + _materialMap->erase(uuid); +} + +void MaterialManagerLocal::saveMaterial(const std::shared_ptr& library, + const std::shared_ptr& material, + const QString& path, + bool overwrite, + bool saveAsCopy, + bool saveInherited) const +{ + if (library->isLocal()) { + auto newMaterial = + library->saveMaterial(material, path, overwrite, saveAsCopy, saveInherited); + (*_materialMap)[newMaterial->getUUID()] = newMaterial; + } +} + +bool MaterialManagerLocal::isMaterial(const fs::path& p) const +{ + if (!fs::is_regular_file(p)) { + return false; + } + // check file extension + if (p.extension() == ".FCMat") { + return true; + } + return false; +} + +bool MaterialManagerLocal::isMaterial(const QFileInfo& file) const +{ + if (!file.isFile()) { + return false; + } + // check file extension + if (file.suffix() == QStringLiteral("FCMat")) { + return true; + } + return false; +} + +std::shared_ptr>> +MaterialManagerLocal::materialsWithModel(const QString& uuid) const +{ + std::shared_ptr>> dict = + std::make_shared>>(); + + for (auto& it : *_materialMap) { + QString key = it.first; + auto material = it.second; + + if (material->hasModel(uuid)) { + (*dict)[key] = material; + } + } + + return dict; +} + +std::shared_ptr>> +MaterialManagerLocal::materialsWithModelComplete(const QString& uuid) const +{ + std::shared_ptr>> dict = + std::make_shared>>(); + + for (auto& it : *_materialMap) { + QString key = it.first; + auto material = it.second; + + if (material->isModelComplete(uuid)) { + (*dict)[key] = material; + } + } + + return dict; +} + +void MaterialManagerLocal::dereference() const +{ + // First clear the inheritences + for (auto& it : *_materialMap) { + auto material = it.second; + material->clearDereferenced(); + material->clearInherited(); + } + + // Run the dereference again + for (auto& it : *_materialMap) { + dereference(it.second); + } +} + +void MaterialManagerLocal::dereference(std::shared_ptr material) const +{ + MaterialLoader::dereference(_materialMap, material); +} + +std::shared_ptr>> +MaterialManagerLocal::getConfiguredLibraries() +{ + auto libraryList = std::make_shared>>(); + + auto param = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Material/Resources"); + bool useBuiltInMaterials = param->GetBool("UseBuiltInMaterials", true); + bool useMatFromModules = param->GetBool("UseMaterialsFromWorkbenches", true); + bool useMatFromConfigDir = param->GetBool("UseMaterialsFromConfigDir", true); + bool useMatFromCustomDir = param->GetBool("UseMaterialsFromCustomDir", true); + + if (useBuiltInMaterials) { + QString resourceDir = QString::fromStdString(App::Application::getResourceDir() + + "/Mod/Material/Resources/Materials"); + auto libData = + std::make_shared(QStringLiteral("System"), + resourceDir, + QStringLiteral(":/icons/freecad.svg"), + true); + libraryList->push_back(libData); + } + + if (useMatFromModules) { + auto moduleParam = App::GetApplication().GetParameterGroupByPath( + "User parameter:BaseApp/Preferences/Mod/Material/Resources/Modules"); + for (auto& group : moduleParam->GetGroups()) { + // auto module = moduleParam->GetGroup(group->GetGroupName()); + auto moduleName = QString::fromStdString(group->GetGroupName()); + auto materialDir = QString::fromStdString(group->GetASCII("ModuleDir", "")); + auto materialIcon = QString::fromStdString(group->GetASCII("ModuleIcon", "")); + auto materialReadOnly = group->GetBool("ModuleReadOnly", true); + + if (materialDir.length() > 0) { + QDir dir(materialDir); + if (dir.exists()) { + auto libData = std::make_shared(moduleName, + materialDir, + materialIcon, + materialReadOnly); + libraryList->push_back(libData); + } + } + } + } + + if (useMatFromConfigDir) { + QString resourceDir = + QString::fromStdString(App::Application::getUserAppDataDir() + "/Material"); + if (!resourceDir.isEmpty()) { + QDir materialDir(resourceDir); + if (!materialDir.exists()) { + // Try creating the user dir if it doesn't exist + if (!materialDir.mkpath(resourceDir)) { + Base::Console().Log("Unable to create user library '%s'\n", + resourceDir.toStdString().c_str()); + } + } + if (materialDir.exists()) { + auto libData = std::make_shared( + QStringLiteral("User"), + resourceDir, + QStringLiteral(":/icons/preferences-general.svg"), + false); + libraryList->push_back(libData); + } + } + } + + if (useMatFromCustomDir) { + QString resourceDir = QString::fromStdString(param->GetASCII("CustomMaterialsDir", "")); + if (!resourceDir.isEmpty()) { + QDir materialDir(resourceDir); + if (materialDir.exists()) { + auto libData = std::make_shared( + QStringLiteral("Custom"), + resourceDir, + QStringLiteral(":/icons/user.svg"), + false); + libraryList->push_back(libData); + } + } + } + + return libraryList; +} diff --git a/src/Mod/Material/App/MaterialManagerLocal.h b/src/Mod/Material/App/MaterialManagerLocal.h new file mode 100644 index 0000000000..ffb0cf2147 --- /dev/null +++ b/src/Mod/Material/App/MaterialManagerLocal.h @@ -0,0 +1,131 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * 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 MATERIAL_MATERIALMANAGERLOCAL_H +#define MATERIAL_MATERIALMANAGERLOCAL_H + +#include + +#include + +#include + +#include "FolderTree.h" +#include "Materials.h" + +namespace fs = std::filesystem; + +class QMutex; + +namespace App +{ +class Material; +} + +namespace Materials +{ + +class MaterialLibrary; +class MaterialLibraryLocal; +class MaterialFilter; +class MaterialFilterOptions; + +class MaterialsExport MaterialManagerLocal: public Base::BaseClass +{ + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + MaterialManagerLocal(); + ~MaterialManagerLocal() override = default; + + static void cleanup(); + static void refresh(); + + // Library management + std::shared_ptr>> getLibraries(); + std::shared_ptr>> getMaterialLibraries(); + std::shared_ptr getLibrary(const QString& name) const; + void createLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly = true); + void renameLibrary(const QString& libraryName, const QString& newName); + void changeIcon(const QString& libraryName, const QString& icon); + void removeLibrary(const QString& libraryName); + std::shared_ptr>> + libraryMaterials(const QString& libraryName); + std::shared_ptr>> + libraryMaterials(const QString& libraryName, + const std::shared_ptr& filter, + const MaterialFilterOptions& options); + + // Folder management + std::shared_ptr> + getMaterialFolders(const std::shared_ptr& library) const; + void createFolder(const std::shared_ptr& library, const QString& path); + void renameFolder(const std::shared_ptr& library, + const QString& oldPath, + const QString& newPath); + void deleteRecursive(const std::shared_ptr& library, const QString& path); + + // Material management + std::shared_ptr>> getLocalMaterials() const; + std::shared_ptr getMaterial(const QString& uuid) const; + std::shared_ptr getMaterialByPath(const QString& path) const; + std::shared_ptr getMaterialByPath(const QString& path, const QString& library) const; + bool exists(const QString& uuid) const; + bool exists(const std::shared_ptr& library, const QString& uuid) const; + void remove(const QString& uuid); + + void saveMaterial(const std::shared_ptr& library, + const std::shared_ptr& material, + const QString& path, + bool overwrite, + bool saveAsCopy, + bool saveInherited) const; + + bool isMaterial(const fs::path& p) const; + bool isMaterial(const QFileInfo& file) const; + + std::shared_ptr>> + materialsWithModel(const QString& uuid) const; + std::shared_ptr>> + materialsWithModelComplete(const QString& uuid) const; + void dereference(std::shared_ptr material) const; + void dereference() const; + +protected: + static std::shared_ptr>> getConfiguredLibraries(); + bool passFilter(const std::shared_ptr& material, + const std::shared_ptr& filter, + const Materials::MaterialFilterOptions& options) const; + +private: + static std::shared_ptr>> _libraryList; + static std::shared_ptr>> _materialMap; + static QMutex _mutex; + + static void initLibraries(); +}; + +} // namespace Materials + +#endif // MATERIAL_MATERIALMANAGERLOCAL_H \ No newline at end of file diff --git a/src/Mod/Material/App/MaterialManagerPy.xml b/src/Mod/Material/App/MaterialManagerPy.xml index 354c403351..49cb6e1ff6 100644 --- a/src/Mod/Material/App/MaterialManagerPy.xml +++ b/src/Mod/Material/App/MaterialManagerPy.xml @@ -10,7 +10,7 @@ FatherInclude="Base/BaseClassPy.h" FatherNamespace="Base" Constructor="true" - Delete="true"> + Delete="false"> Material descriptions. diff --git a/src/Mod/Material/App/MaterialManagerPyImp.cpp b/src/Mod/Material/App/MaterialManagerPyImp.cpp index c84075d1cb..61afb574f1 100644 --- a/src/Mod/Material/App/MaterialManagerPyImp.cpp +++ b/src/Mod/Material/App/MaterialManagerPyImp.cpp @@ -47,7 +47,7 @@ std::string MaterialManagerPy::representation() const PyObject* MaterialManagerPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper { // never create such objects with the constructor - return new MaterialManagerPy(new MaterialManager()); + return new MaterialManagerPy(&(MaterialManager::getManager())); } // constructor method @@ -136,15 +136,25 @@ PyObject* MaterialManagerPy::inheritMaterial(PyObject* args) Py::List MaterialManagerPy::getMaterialLibraries() const { - auto libraries = getMaterialManagerPtr()->getMaterialLibraries(); + auto libraries = getMaterialManagerPtr()->getLibraries(); Py::List list; for (auto it = libraries->begin(); it != libraries->end(); it++) { auto lib = *it; Py::Tuple libTuple(3); - libTuple.setItem(0, Py::String(lib->getName().toStdString())); - libTuple.setItem(1, Py::String(lib->getDirectoryPath().toStdString())); - libTuple.setItem(2, Py::String(lib->getIconPath().toStdString())); + if (lib->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(lib); + libTuple.setItem(0, Py::String(materialLibrary->getName().toStdString())); + libTuple.setItem(1, Py::String(materialLibrary->getDirectoryPath().toStdString())); + libTuple.setItem(2, Py::String(materialLibrary->getIconPath().toStdString())); + } + else + { + libTuple.setItem(0, Py::String()); + libTuple.setItem(1, Py::String()); + libTuple.setItem(2, Py::String()); + } list.append(libTuple); } @@ -156,7 +166,7 @@ Py::Dict MaterialManagerPy::getMaterials() const { Py::Dict dict; - auto materials = getMaterialManagerPtr()->getMaterials(); + auto materials = getMaterialManagerPtr()->getLocalMaterials(); for (auto it = materials->begin(); it != materials->end(); it++) { QString key = it->first; @@ -284,17 +294,19 @@ PyObject* MaterialManagerPy::save(PyObject* args, PyObject* kwds) return Py_None; } -void addMaterials(Py::List& list, +void addMaterials(MaterialManager *manager, + Py::List& list, const std::shared_ptr>>& tree) { for (auto& node : *tree) { - if (node.second->getType() == MaterialTreeNode::DataNode) { - auto material = node.second->getData(); + if (node.second->getType() == MaterialTreeNode::NodeType::DataNode) { + auto uuid = node.second->getUUID(); + auto material = manager->getMaterial(uuid); PyObject* materialPy = new MaterialPy(new Material(*material)); list.append(Py::Object(materialPy, true)); } else { - addMaterials(list, node.second->getFolder()); + addMaterials(manager, list, node.second->getFolder()); } } } @@ -327,13 +339,13 @@ PyObject* MaterialManagerPy::filterMaterials(PyObject* args, PyObject* kwds) auto filter = std::make_shared(*(static_cast(filterPy)->getMaterialFilterPtr())); - auto libraries = getMaterialManagerPtr()->getMaterialLibraries(); + auto libraries = getMaterialManagerPtr()->getLibraries(); Py::List list; for (auto lib : *libraries) { auto tree = getMaterialManagerPtr()->getMaterialTree(lib, filter, options); if (tree->size() > 0) { - addMaterials(list, tree); + addMaterials(getMaterialManagerPtr(), list, tree); } } diff --git a/src/Mod/Material/App/MaterialPropertyPy.xml b/src/Mod/Material/App/MaterialPropertyPy.xml new file mode 100644 index 0000000000..ddc7a06ee0 --- /dev/null +++ b/src/Mod/Material/App/MaterialPropertyPy.xml @@ -0,0 +1,31 @@ + + + + + + Material property descriptions. + + + + The value of the material property. + + + + + + The property value is undefined. + + + + + \ No newline at end of file diff --git a/src/Mod/Material/App/MaterialPropertyPyImp.cpp b/src/Mod/Material/App/MaterialPropertyPyImp.cpp new file mode 100644 index 0000000000..d6bced53c8 --- /dev/null +++ b/src/Mod/Material/App/MaterialPropertyPyImp.cpp @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * 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 "Model.h" +#include "PyVariants.h" +#include "ModelPropertyPy.h" +#include "MaterialPropertyPy.h" + +#include "MaterialPropertyPy.cpp" + +using namespace Materials; + +// returns a string which represents the object e.g. when printed in python +std::string MaterialPropertyPy::representation() const +{ + std::stringstream str; + str << ""; + + return str.str(); +} + +PyObject* MaterialPropertyPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper +{ + // never create such objects with the constructor + return new MaterialPropertyPy(new MaterialProperty()); +} + +// constructor method +int MaterialPropertyPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) +{ + return 0; +} + +Py::Object MaterialPropertyPy::getValue() const +{ + auto value = getMaterialPropertyPtr()->getValue(); + + return Py::Object(_pyObjectFromVariant(value), true); +} + +Py::Boolean MaterialPropertyPy::getEmpty() const +{ + return getMaterialPropertyPtr()->isEmpty(); +} + +PyObject* MaterialPropertyPy::getCustomAttributes(const char* /*attr*/) const +{ + return nullptr; +} + +int MaterialPropertyPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +} \ No newline at end of file diff --git a/src/Mod/Material/App/MaterialPy.xml b/src/Mod/Material/App/MaterialPy.xml index e6998690f1..1681dc6eda 100644 --- a/src/Mod/Material/App/MaterialPy.xml +++ b/src/Mod/Material/App/MaterialPy.xml @@ -39,7 +39,7 @@ - + Model directory relative to the library root. @@ -69,7 +69,7 @@ - + Parent material UUID. @@ -210,6 +210,17 @@ Set the value associated with the property + + + Set the value associated with the property + + + + + Dictionary of MaterialProperty objects. + + + Property keys diff --git a/src/Mod/Material/App/MaterialPyImp.cpp b/src/Mod/Material/App/MaterialPyImp.cpp index f650df64a5..5a267d29ed 100644 --- a/src/Mod/Material/App/MaterialPyImp.cpp +++ b/src/Mod/Material/App/MaterialPyImp.cpp @@ -36,15 +36,15 @@ #include "MaterialLibrary.h" #include "MaterialPy.h" #include "MaterialValue.h" +#include "PyVariants.h" + +#include "ModelPropertyPy.h" +#include "MaterialPropertyPy.h" #include "MaterialPy.cpp" using namespace Materials; -// Forward declaration -static PyObject* _pyObjectFromVariant(const QVariant& value); -static Py::List getList(const QVariant& value); - // returns a string which represents the object e.g. when printed in python std::string MaterialPy::representation() const { @@ -68,19 +68,34 @@ int MaterialPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) Py::String MaterialPy::getLibraryName() const { auto library = getMaterialPtr()->getLibrary(); - return {library ? library->getName().toStdString() : ""}; + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + return {materialLibrary ? materialLibrary->getName().toStdString() : ""}; + } + return ""; } Py::String MaterialPy::getLibraryRoot() const { auto library = getMaterialPtr()->getLibrary(); - return {library ? library->getDirectoryPath().toStdString() : ""}; + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + return {materialLibrary ? materialLibrary->getDirectoryPath().toStdString() : ""}; + } + return ""; } Py::String MaterialPy::getLibraryIcon() const { auto library = getMaterialPtr()->getLibrary(); - return {library ? library->getIconPath().toStdString() : ""}; + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + return {materialLibrary ? materialLibrary->getIconPath().toStdString() : ""}; + } + return ""; } Py::String MaterialPy::getName() const @@ -98,6 +113,11 @@ Py::String MaterialPy::getDirectory() const return {getMaterialPtr()->getDirectory().toStdString()}; } +void MaterialPy::setDirectory(Py::String arg) +{ + getMaterialPtr()->setDirectory(QString::fromStdString(arg)); +} + Py::String MaterialPy::getUUID() const { return {getMaterialPtr()->getUUID().toStdString()}; @@ -138,6 +158,11 @@ Py::String MaterialPy::getParent() const return {getMaterialPtr()->getParentUUID().toStdString()}; } +void MaterialPy::setParent(Py::String arg) +{ + getMaterialPtr()->setParentUUID(QString::fromStdString(arg)); +} + Py::String MaterialPy::getAuthorAndLicense() const { return {getMaterialPtr()->getAuthorAndLicense().toStdString()}; @@ -438,52 +463,6 @@ Py::Dict MaterialPy::getLegacyProperties() const return dict; } -static Py::List getList(const QVariant& value) -{ - auto listValue = value.value>(); - Py::List list; - - for (auto& it : listValue) { - list.append(Py::Object(_pyObjectFromVariant(it))); - } - - return list; -} - -static PyObject* _pyObjectFromVariant(const QVariant& value) -{ - if (value.isNull()) { - Py_RETURN_NONE; - } - - if (value.userType() == qMetaTypeId()) { - return new Base::QuantityPy(new Base::Quantity(value.value())); - } - if (value.userType() == QMetaType::Double) { - return PyFloat_FromDouble(value.toDouble()); - } - if (value.userType() == QMetaType::Float) { - return PyFloat_FromDouble(value.toFloat()); - } - if (value.userType() == QMetaType::Int) { - return PyLong_FromLong(value.toInt()); - } - if (value.userType() == QMetaType::Long) { - return PyLong_FromLong(value.toInt()); - } - if (value.userType() == QMetaType::Bool) { - return Py::new_reference_to(Py::Boolean(value.toBool())); - } - if (value.userType() == QMetaType::QString) { - return PyUnicode_FromString(value.toString().toStdString().c_str()); - } - if (value.userType() == qMetaTypeId>()) { - return Py::new_reference_to(getList(value)); - } - - throw UnknownValueType(); -} - PyObject* MaterialPy::getPhysicalValue(PyObject* args) { char* name; @@ -502,13 +481,13 @@ PyObject* MaterialPy::getPhysicalValue(PyObject* args) if (property->getType() == MaterialValue::Array2D) { auto value = - std::static_pointer_cast(property->getMaterialValue()); - return new Array2DPy(new Material2DArray(*value)); + std::static_pointer_cast(property->getMaterialValue()); + return new Array2DPy(new Array2D(*value)); } if (property->getType() == MaterialValue::Array3D) { auto value = - std::static_pointer_cast(property->getMaterialValue()); - return new Array3DPy(new Material3DArray(*value)); + std::static_pointer_cast(property->getMaterialValue()); + return new Array3DPy(new Array3D(*value)); } QVariant value = property->getValue(); @@ -523,8 +502,7 @@ PyObject* MaterialPy::setPhysicalValue(PyObject* args) return nullptr; } - getMaterialPtr()->setPhysicalValue(QString::fromStdString(name), - QString::fromStdString(value)); + getMaterialPtr()->setPhysicalValue(QString::fromStdString(name), QString::fromStdString(value)); Py_INCREF(Py_None); return Py_None; } @@ -536,7 +514,27 @@ PyObject* MaterialPy::getAppearanceValue(PyObject* args) return nullptr; } - QVariant value = getMaterialPtr()->getAppearanceValue(QString::fromStdString(name)); + if (!getMaterialPtr()->hasAppearanceProperty(QString::fromStdString(name))) { + Py_RETURN_NONE; + } + + auto property = getMaterialPtr()->getAppearanceProperty(QString::fromStdString(name)); + if (!property) { + Py_RETURN_NONE; + } + + if (property->getType() == MaterialValue::Array2D) { + auto value = + std::static_pointer_cast(property->getMaterialValue()); + return new Array2DPy(new Array2D(*value)); + } + if (property->getType() == MaterialValue::Array3D) { + auto value = + std::static_pointer_cast(property->getMaterialValue()); + return new Array3DPy(new Array3D(*value)); + } + + QVariant value = property->getValue(); return _pyObjectFromVariant(value); } @@ -553,6 +551,93 @@ PyObject* MaterialPy::setAppearanceValue(PyObject* args) Py_INCREF(Py_None); return Py_None; } +PyObject* MaterialPy::setValue(PyObject* args) +{ + char* name; + char* value; + PyObject* listObj; + PyObject* arrayObj; + if (PyArg_ParseTuple(args, "ss", &name, &value)) { + getMaterialPtr()->setValue(QString::fromStdString(name), QString::fromStdString(value)); + Py_Return; + } + + PyErr_Clear(); + if (PyArg_ParseTuple(args, "sO!", &name, &PyList_Type, &listObj)) { + QList variantList; + Py::List list(listObj); + for (auto itemObj : list) { + Py::String item(itemObj); + QString value(QString::fromStdString(item.as_string())); + QVariant variant = QVariant::fromValue(value); + variantList.append(variant); + } + + getMaterialPtr()->setValue(QString::fromStdString(name), variantList); + Py_Return; + } + + PyErr_Clear(); + if (PyArg_ParseTuple(args, "sO!", &name, &(Array2DPy::Type), &arrayObj)) { + auto array = static_cast(arrayObj); + auto shared = std::make_shared(*array->getArray2DPtr()); + + getMaterialPtr()->setValue(QString::fromStdString(name), shared); + Py_Return; + } + + PyErr_Clear(); + if (PyArg_ParseTuple(args, "sO!", &name, &(Array3DPy::Type), &arrayObj)) { + auto array = static_cast(arrayObj); + auto shared = std::make_shared(*array->getArray3DPtr()); + + getMaterialPtr()->setValue(QString::fromStdString(name), shared); + Py_Return; + } + + PyErr_SetString(PyExc_TypeError, "Either a string, a list, or an array are expected"); + return nullptr; +} + +Py::Dict MaterialPy::getPropertyObjects() const +{ + Py::Dict dict; + + auto properties = getMaterialPtr()->getPhysicalProperties(); + for (auto& it : properties) { + QString key = it.first; + auto materialProperty = it.second; + + // if (materialProperty->getType() == MaterialValue::Array2D) { + // auto value = std::static_pointer_cast( + // materialProperty->getMaterialValue()); + // dict.setItem(Py::String(key.toStdString()), + // Py::Object(new Array2DPy(new Array2D(*value)), true)); + // } + // else if (materialProperty->getType() == MaterialValue::Array3D) { + // auto value = std::static_pointer_cast( + // materialProperty->getMaterialValue()); + // dict.setItem(Py::String(key.toStdString()), + // Py::Object(new Array3DPy(new Array3D(*value)), true)); + // } + // else { + dict.setItem( + Py::String(key.toStdString()), + Py::Object(new MaterialPropertyPy(new MaterialProperty(materialProperty)), true)); + // } + } + + properties = getMaterialPtr()->getAppearanceProperties(); + for (auto& it : properties) { + QString key = it.first; + auto materialProperty = it.second; + dict.setItem( + Py::String(key.toStdString()), + Py::Object(new MaterialPropertyPy(new MaterialProperty(materialProperty)), true)); + } + + return dict; +} PyObject* MaterialPy::keys() { @@ -584,4 +669,4 @@ PyObject* MaterialPy::mapping_subscript(PyObject* self, PyObject* key) { Py::Dict dict = static_cast(self)->getProperties(); return Py::new_reference_to(dict.getItem(Py::Object(key))); -} +} \ No newline at end of file diff --git a/src/Mod/Material/App/MaterialValue.cpp b/src/Mod/Material/App/MaterialValue.cpp index af84945adf..ba20eeb1c0 100644 --- a/src/Mod/Material/App/MaterialValue.cpp +++ b/src/Mod/Material/App/MaterialValue.cpp @@ -102,6 +102,44 @@ bool MaterialValue::operator==(const MaterialValue& other) const return (_valueType == other._valueType) && (_value == other._value); } +void MaterialValue::validate(const MaterialValue& other) const +{ + if (_valueType != other._valueType) { + throw InvalidProperty("Material property value types don't match"); + } + if (_valueType == Quantity) { + auto q1 = _value.value(); + auto q2 = other._value.value(); + if (q1.isValid()) { + if (!q2.isValid()) { + throw InvalidProperty("Invalid remote Material property quantity value"); + } + if (q1.getUserString() != q2.getUserString()) { + // Direct comparisons of the quantities may have precision issues + // throw InvalidProperty("Material property quantity values don't match"); + } + } + else { + if (q2.isValid()) { + throw InvalidProperty("Remote Material property quantity should not have a value"); + } + } + } + else if (_valueType == Array2D) { + auto a1 = static_cast(this); + auto a2 = static_cast(&other); + a1->validate(*a2); + } + else if (_valueType == Array3D) { + auto a1 = static_cast(this); + auto a2 = static_cast(&other); + a1->validate(*a2); + } + else if (!(_value.isNull() && other._value.isNull()) && (_value != other._value)) { + throw InvalidProperty("Material property values don't match"); + } +} + QString MaterialValue::escapeString(const QString& source) { QString res = source; @@ -205,6 +243,11 @@ void MaterialValue::setList(const QList& value) } bool MaterialValue::isNull() const +{ + return isEmpty(); +} + +bool MaterialValue::isEmpty() const { if (_value.isNull()) { return true; @@ -317,24 +360,24 @@ const Base::QuantityFormat MaterialValue::getQuantityFormat() //=== -TYPESYSTEM_SOURCE(Materials::Material2DArray, Materials::MaterialValue) +TYPESYSTEM_SOURCE(Materials::Array2D, Materials::MaterialValue) -Material2DArray::Material2DArray() - : MaterialValue(Array2D, Array2D) +Array2D::Array2D() + : MaterialValue(MaterialValue::Array2D, MaterialValue::Array2D) , _columns(0) { // Initialize separatelt to prevent recursion // setType(Array2D); } -Material2DArray::Material2DArray(const Material2DArray& other) +Array2D::Array2D(const Array2D& other) : MaterialValue(other) , _columns(other._columns) { deepCopy(other); } -Material2DArray& Material2DArray::operator=(const Material2DArray& other) +Array2D& Array2D::operator=(const Array2D& other) { if (this == &other) { return *this; @@ -348,7 +391,7 @@ Material2DArray& Material2DArray::operator=(const Material2DArray& other) return *this; } -void Material2DArray::deepCopy(const Material2DArray& other) +void Array2D::deepCopy(const Array2D& other) { // Deep copy for (auto& row : other._rows) { @@ -361,26 +404,55 @@ void Material2DArray::deepCopy(const Material2DArray& other) } } -bool Material2DArray::isNull() const +bool Array2D::isNull() const +{ + return isEmpty(); +} + +bool Array2D::isEmpty() const { return rows() <= 0; } -void Material2DArray::validateRow(int row) const +void Array2D::validateRow(int row) const { if (row < 0 || row >= rows()) { throw InvalidIndex(); } } -void Material2DArray::validateColumn(int column) const +void Array2D::validateColumn(int column) const { if (column < 0 || column >= columns()) { throw InvalidIndex(); } } -std::shared_ptr> Material2DArray::getRow(int row) const +void Array2D::validate(const Array2D& other) const +{ + if (rows() != other.rows()) { + Base::Console().Log("Local row count %d, remote %d\n", rows(), other.rows()); + throw InvalidProperty("Material property value row counts don't match"); + } + if (columns() != other.columns()) { + Base::Console().Log("Local column count %d, remote %d\n", columns(), other.columns()); + throw InvalidProperty("Material property value column counts don't match"); + } + try { + for (int i = 0; i < rows(); i++) { + for (int j = 0; j < columns(); j++) { + if (getValue(i, j) != other.getValue(i, j)) { + throw InvalidProperty("Material property values don't match"); + } + } + } + } + catch (const InvalidIndex&) { + throw InvalidProperty("Material property value invalid array index"); + } +} + +std::shared_ptr> Array2D::getRow(int row) const { validateRow(row); @@ -392,7 +464,7 @@ std::shared_ptr> Material2DArray::getRow(int row) const } } -std::shared_ptr> Material2DArray::getRow(int row) +std::shared_ptr> Array2D::getRow(int row) { validateRow(row); @@ -404,17 +476,17 @@ std::shared_ptr> Material2DArray::getRow(int row) } } -void Material2DArray::addRow(const std::shared_ptr>& row) +void Array2D::addRow(const std::shared_ptr>& row) { _rows.push_back(row); } -void Material2DArray::insertRow(int index, const std::shared_ptr>& row) +void Array2D::insertRow(int index, const std::shared_ptr>& row) { _rows.insert(_rows.begin() + index, row); } -void Material2DArray::deleteRow(int row) +void Array2D::deleteRow(int row) { if (row >= static_cast(_rows.size()) || row < 0) { throw InvalidIndex(); @@ -422,7 +494,18 @@ void Material2DArray::deleteRow(int row) _rows.erase(_rows.begin() + row); } -void Material2DArray::setValue(int row, int column, const QVariant& value) +void Array2D::setRows(int rowCount) +{ + while (rows() < rowCount) { + auto row = std::make_shared>(); + for (int i = 0; i < columns(); i++) { + row->append(QVariant()); + } + addRow(row); + } +} + +void Array2D::setValue(int row, int column, const QVariant& value) { validateRow(row); validateColumn(column); @@ -436,7 +519,7 @@ void Material2DArray::setValue(int row, int column, const QVariant& value) } } -QVariant Material2DArray::getValue(int row, int column) const +QVariant Array2D::getValue(int row, int column) const { validateColumn(column); @@ -449,7 +532,7 @@ QVariant Material2DArray::getValue(int row, int column) const } } -void Material2DArray::dumpRow(const std::shared_ptr>& row) +void Array2D::dumpRow(const std::shared_ptr>& row) { Base::Console().Log("row: "); for (auto& column : *row) { @@ -458,14 +541,14 @@ void Material2DArray::dumpRow(const std::shared_ptr>& row) Base::Console().Log("\n"); } -void Material2DArray::dump() const +void Array2D::dump() const { for (auto& row : _rows) { dumpRow(row); } } -QString Material2DArray::getYAMLString() const +QString Array2D::getYAMLString() const { if (isNull()) { return QString(); @@ -511,10 +594,10 @@ QString Material2DArray::getYAMLString() const //=== -TYPESYSTEM_SOURCE(Materials::Material3DArray, Materials::MaterialValue) +TYPESYSTEM_SOURCE(Materials::Array3D, Materials::MaterialValue) -Material3DArray::Material3DArray() - : MaterialValue(Array3D, Array3D) +Array3D::Array3D() + : MaterialValue(MaterialValue::Array3D, MaterialValue::Array3D) , _currentDepth(0) , _columns(0) { @@ -522,26 +605,71 @@ Material3DArray::Material3DArray() // setType(Array3D); } -bool Material3DArray::isNull() const +Array3D::Array3D(const Array3D& other) + : MaterialValue(other) + , _currentDepth(other._currentDepth) + , _columns(other._columns) +{ + deepCopy(other); +} + +Array3D& Array3D::operator=(const Array3D& other) +{ + if (this == &other) { + return *this; + } + + MaterialValue::operator=(other); + _columns = other._columns; + _currentDepth = other._currentDepth; + + deepCopy(other); + + return *this; +} + +void Array3D::deepCopy(const Array3D& other) +{ + // Deep copy + _rowMap.clear(); + for (auto& depthTable : other._rowMap) { + auto depth = addDepth(depthTable.first); + auto rows = depthTable.second; + for (auto row : *rows) { + auto newRow = std::make_shared>(); + for (auto column : *row) { + newRow->append(column); + } + addRow(depth, newRow); + } + } +} + +bool Array3D::isNull() const +{ + return isEmpty(); +} + +bool Array3D::isEmpty() const { return depth() <= 0; } -void Material3DArray::validateDepth(int level) const +void Array3D::validateDepth(int level) const { if (level < 0 || level >= depth()) { throw InvalidIndex(); } } -void Material3DArray::validateColumn(int column) const +void Array3D::validateColumn(int column) const { if (column < 0 || column >= columns()) { throw InvalidIndex(); } } -void Material3DArray::validateRow(int level, int row) const +void Array3D::validateRow(int level, int row) const { validateDepth(level); @@ -550,8 +678,18 @@ void Material3DArray::validateRow(int level, int row) const } } +void Array3D::validate(const Array3D& other) const +{ + if (depth() != other.depth()) { + throw InvalidProperty("Material property value row counts don't match"); + } + if (columns() != other.columns()) { + throw InvalidProperty("Material property value column counts don't match"); + } +} + const std::shared_ptr>>>& -Material3DArray::getTable(const Base::Quantity& depth) const +Array3D::getTable(const Base::Quantity& depth) const { for (auto& it : _rowMap) { if (std::get<0>(it) == depth) { @@ -563,7 +701,7 @@ Material3DArray::getTable(const Base::Quantity& depth) const } const std::shared_ptr>>>& -Material3DArray::getTable(int depthIndex) const +Array3D::getTable(int depthIndex) const { try { return std::get<1>(_rowMap.at(depthIndex)); @@ -573,7 +711,7 @@ Material3DArray::getTable(int depthIndex) const } } -std::shared_ptr> Material3DArray::getRow(int depth, int row) const +std::shared_ptr> Array3D::getRow(int depth, int row) const { validateRow(depth, row); @@ -585,13 +723,13 @@ std::shared_ptr> Material3DArray::getRow(int depth, int ro } } -std::shared_ptr> Material3DArray::getRow(int row) const +std::shared_ptr> Array3D::getRow(int row) const { // Check if we can convert otherwise throw error return getRow(_currentDepth, row); } -std::shared_ptr> Material3DArray::getRow(int depth, int row) +std::shared_ptr> Array3D::getRow(int depth, int row) { validateRow(depth, row); @@ -603,12 +741,12 @@ std::shared_ptr> Material3DArray::getRow(int depth, int ro } } -std::shared_ptr> Material3DArray::getRow(int row) +std::shared_ptr> Array3D::getRow(int row) { return getRow(_currentDepth, row); } -void Material3DArray::addRow(int depth, const std::shared_ptr>& row) +void Array3D::addRow(int depth, const std::shared_ptr>& row) { try { getTable(depth)->push_back(row); @@ -618,12 +756,12 @@ void Material3DArray::addRow(int depth, const std::shared_ptr>& row) +void Array3D::addRow(const std::shared_ptr>& row) { addRow(_currentDepth, row); } -int Material3DArray::addDepth(int depth, const Base::Quantity& value) +int Array3D::addDepth(int depth, const Base::Quantity& value) { if (depth == this->depth()) { // Append to the end @@ -639,7 +777,7 @@ int Material3DArray::addDepth(int depth, const Base::Quantity& value) return depth; } -int Material3DArray::addDepth(const Base::Quantity& value) +int Array3D::addDepth(const Base::Quantity& value) { auto rowVector = std::make_shared>>>(); auto entry = std::make_pair(value, rowVector); @@ -648,13 +786,22 @@ int Material3DArray::addDepth(const Base::Quantity& value) return depth() - 1; } -void Material3DArray::deleteDepth(int depth) +void Array3D::deleteDepth(int depth) { deleteRows(depth); // This may throw an InvalidIndex _rowMap.erase(_rowMap.begin() + depth); } -void Material3DArray::insertRow(int depth, +void Array3D::setDepth(int depthCount) +{ + Base::Quantity dummy; + dummy.setInvalid(); + while (depth() < depthCount) { + addDepth(dummy); + } +} + +void Array3D::insertRow(int depth, int row, const std::shared_ptr>& rowData) { @@ -667,12 +814,12 @@ void Material3DArray::insertRow(int depth, } } -void Material3DArray::insertRow(int row, const std::shared_ptr>& rowData) +void Array3D::insertRow(int row, const std::shared_ptr>& rowData) { insertRow(_currentDepth, row, rowData); } -void Material3DArray::deleteRow(int depth, int row) +void Array3D::deleteRow(int depth, int row) { auto table = getTable(depth); if (row >= static_cast(table->size()) || row < 0) { @@ -681,23 +828,23 @@ void Material3DArray::deleteRow(int depth, int row) table->erase(table->begin() + row); } -void Material3DArray::deleteRow(int row) +void Array3D::deleteRow(int row) { deleteRow(_currentDepth, row); } -void Material3DArray::deleteRows(int depth) +void Array3D::deleteRows(int depth) { auto table = getTable(depth); table->clear(); } -void Material3DArray::deleteRows() +void Array3D::deleteRows() { deleteRows(_currentDepth); } -int Material3DArray::rows(int depth) const +int Array3D::rows(int depth) const { if (depth < 0 || (depth == 0 && this->depth() == 0)) { return 0; @@ -707,7 +854,21 @@ int Material3DArray::rows(int depth) const return getTable(depth)->size(); } -void Material3DArray::setValue(int depth, int row, int column, const Base::Quantity& value) +void Array3D::setRows(int depth, int rowCount) +{ + Base::Quantity dummy; + dummy.setInvalid(); + + while (rows(depth) < rowCount) { + auto row = std::make_shared>(); + for (int i = 0; i < columns(); i++) { + row->append(dummy); + } + addRow(depth, row); + } +} + +void Array3D::setValue(int depth, int row, int column, const Base::Quantity& value) { validateRow(depth, row); validateColumn(column); @@ -721,12 +882,12 @@ void Material3DArray::setValue(int depth, int row, int column, const Base::Quant } } -void Material3DArray::setValue(int row, int column, const Base::Quantity& value) +void Array3D::setValue(int row, int column, const Base::Quantity& value) { setValue(_currentDepth, row, column, value); } -void Material3DArray::setDepthValue(int depth, const Base::Quantity& value) +void Array3D::setDepthValue(int depth, const Base::Quantity& value) { try { auto oldRows = getTable(depth); @@ -737,13 +898,13 @@ void Material3DArray::setDepthValue(int depth, const Base::Quantity& value) } } -void Material3DArray::setDepthValue(const Base::Quantity& value) +void Array3D::setDepthValue(const Base::Quantity& value) { setDepthValue(_currentDepth, value); } -Base::Quantity Material3DArray::getValue(int depth, int row, int column) const +Base::Quantity Array3D::getValue(int depth, int row, int column) const { // getRow validates depth and row. Do that first auto val = getRow(depth, row); @@ -757,12 +918,12 @@ Base::Quantity Material3DArray::getValue(int depth, int row, int column) const } } -Base::Quantity Material3DArray::getValue(int row, int column) const +Base::Quantity Array3D::getValue(int row, int column) const { return getValue(_currentDepth, row, column); } -Base::Quantity Material3DArray::getDepthValue(int depth) const +Base::Quantity Array3D::getDepthValue(int depth) const { validateDepth(depth); @@ -774,12 +935,12 @@ Base::Quantity Material3DArray::getDepthValue(int depth) const } } -int Material3DArray::currentDepth() const +int Array3D::currentDepth() const { return _currentDepth; } -void Material3DArray::setCurrentDepth(int depth) +void Array3D::setCurrentDepth(int depth) { validateDepth(depth); @@ -794,7 +955,7 @@ void Material3DArray::setCurrentDepth(int depth) } } -QString Material3DArray::getYAMLString() const +QString Array3D::getYAMLString() const { if (isNull()) { return QString(); diff --git a/src/Mod/Material/App/MaterialValue.h b/src/Mod/Material/App/MaterialValue.h index 7dbebfe734..ffe88e537e 100644 --- a/src/Mod/Material/App/MaterialValue.h +++ b/src/Mod/Material/App/MaterialValue.h @@ -91,6 +91,7 @@ public: return _value.value>(); } virtual bool isNull() const; + virtual bool isEmpty() const; virtual const QVariant getValueAt(const QVariant& value) const { @@ -111,6 +112,8 @@ public: // The precision is based on the value from the original materials editor static const int PRECISION = 6; + + void validate(const MaterialValue& other) const; protected: MaterialValue(ValueType type, ValueType inherited); @@ -133,18 +136,19 @@ private: static QMap _typeMap; }; -class MaterialsExport Material2DArray: public MaterialValue +class MaterialsExport Array2D: public MaterialValue { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: - Material2DArray(); - Material2DArray(const Material2DArray& other); - ~Material2DArray() override = default; + Array2D(); + Array2D(const Array2D& other); + ~Array2D() override = default; - Material2DArray& operator=(const Material2DArray& other); + Array2D& operator=(const Array2D& other); bool isNull() const override; + bool isEmpty() const override; const QList>>& getArray() const { @@ -153,6 +157,7 @@ public: void validateRow(int row) const; void validateColumn(int column) const; + void validate(const Array2D& other) const; std::shared_ptr> getRow(int row) const; std::shared_ptr> getRow(int row); @@ -171,6 +176,7 @@ public: void addRow(const std::shared_ptr>& row); void insertRow(int index, const std::shared_ptr>& row); void deleteRow(int row); + void setRows(int rowCount); void setValue(int row, int column, const QVariant& value); QVariant getValue(int row, int column) const; @@ -178,7 +184,7 @@ public: QString getYAMLString() const override; protected: - void deepCopy(const Material2DArray& other); + void deepCopy(const Array2D& other); QList>> _rows; int _columns; @@ -188,15 +194,19 @@ private: void dump() const; }; -class MaterialsExport Material3DArray: public MaterialValue +class MaterialsExport Array3D: public MaterialValue { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: - Material3DArray(); - ~Material3DArray() override = default; + Array3D(); + Array3D(const Array3D& other); + ~Array3D() override = default; + + Array3D& operator=(const Array3D& other); bool isNull() const override; + bool isEmpty() const override; const QList< std::pair>>>>>& @@ -208,6 +218,7 @@ public: void validateDepth(int level) const; void validateColumn(int column) const; void validateRow(int level, int row) const; + void validate(const Array3D& other) const; const std::shared_ptr>>>& getTable(const Base::Quantity& depth) const; @@ -245,6 +256,8 @@ public: { _columns = size; } + void setDepth(int depthCount); + void setRows(int depth, int rowCount); void setValue(int depth, int row, int column, const Base::Quantity& value); void setValue(int row, int column, const Base::Quantity& value); @@ -260,6 +273,8 @@ public: QString getYAMLString() const override; protected: + void deepCopy(const Array3D& other); + QList>>>>> _rowMap; int _currentDepth; @@ -269,7 +284,7 @@ protected: } // namespace Materials Q_DECLARE_METATYPE(Materials::MaterialValue) -Q_DECLARE_METATYPE(std::shared_ptr) -Q_DECLARE_METATYPE(std::shared_ptr) +Q_DECLARE_METATYPE(std::shared_ptr) +Q_DECLARE_METATYPE(std::shared_ptr) #endif // MATERIAL_MATERIALVALUE_H diff --git a/src/Mod/Material/App/Materials.cpp b/src/Mod/Material/App/Materials.cpp index 02ba167a30..03b3a9a34d 100644 --- a/src/Mod/Material/App/Materials.cpp +++ b/src/Mod/Material/App/Materials.cpp @@ -65,11 +65,11 @@ void MaterialProperty::copyValuePtr(const std::shared_ptr& value) { if (value->getType() == MaterialValue::Array2D) { _valuePtr = - std::make_shared(*(std::static_pointer_cast(value))); + std::make_shared(*(std::static_pointer_cast(value))); } else if (value->getType() == MaterialValue::Array3D) { _valuePtr = - std::make_shared(*(std::static_pointer_cast(value))); + std::make_shared(*(std::static_pointer_cast(value))); } else { _valuePtr = std::make_shared(*value); @@ -132,7 +132,7 @@ QString MaterialProperty::getString() const if (value.isNull()) { return {}; } - return QString(QLatin1String("%L1")).arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION); + return QString(QStringLiteral("%L1")).arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION); } return getValue().toString(); } @@ -177,7 +177,7 @@ QString MaterialProperty::getDictionaryString() const } if (getType() == MaterialValue::Quantity) { auto quantity = getValue().value(); - auto string = QString(QLatin1String("%1 %2")) + auto string = QString(QStringLiteral("%1 %2")) .arg(quantity.getValue(), 0, 'g', MaterialValue::PRECISION) .arg(QString::fromStdString(quantity.getUnit().getString())); return string; @@ -187,7 +187,7 @@ QString MaterialProperty::getDictionaryString() const if (value.isNull()) { return {}; } - return QString(QLatin1String("%1")).arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION); + return QString(QStringLiteral("%1")).arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION); } return getValue().toString(); } @@ -205,12 +205,12 @@ void MaterialProperty::setType(const QString& type) throw UnknownValueType(); } if (mappedType == MaterialValue::Array2D) { - auto arrayPtr = std::make_shared(); + auto arrayPtr = std::make_shared(); arrayPtr->setColumns(columns()); _valuePtr = arrayPtr; } else if (mappedType == MaterialValue::Array3D) { - auto arrayPtr = std::make_shared(); + auto arrayPtr = std::make_shared(); // First column is third dimension arrayPtr->setColumns(columns() - 1); _valuePtr = arrayPtr; @@ -448,6 +448,17 @@ bool MaterialProperty::operator==(const MaterialProperty& other) const return false; } +void MaterialProperty::validate(const MaterialProperty& other) const { + _valuePtr->validate(*other._valuePtr); + + if (_columns.size() != other._columns.size()) { + throw InvalidProperty("Model property column counts don't match"); + } + for (int i = 0; i < _columns.size(); i++) { + _columns[i].validate(other._columns[i]); + } +} + TYPESYSTEM_SOURCE(Materials::Material, Base::BaseClass) Material::Material() @@ -476,6 +487,7 @@ Material::Material(const std::shared_ptr& library, Material::Material(const Material& other) : _library(other._library) , _directory(other._directory) + , _filename(other._filename) , _uuid(other._uuid) , _name(other._name) , _author(other._author) @@ -513,6 +525,31 @@ Material::Material(const Material& other) } } +QString Material::getDirectory() const +{ + return _directory; +} + +void Material::setDirectory(const QString& directory) +{ + _directory = directory; +} + +QString Material::getFilename() const +{ + return _filename; +} + +void Material::setFilename(const QString& filename) +{ + _filename = filename; +} + +QString Material::getFilePath() const +{ + return QDir(_directory + QStringLiteral("/") + _filename).absolutePath(); +} + QString Material::getAuthorAndLicense() const { QString authorAndLicense; @@ -521,7 +558,7 @@ QString Material::getAuthorAndLicense() const if (!_author.isNull()) { authorAndLicense = _author; if (!_license.isNull()) { - authorAndLicense += QLatin1String(" ") + _license; + authorAndLicense += QStringLiteral(" ") + _license; } } else if (!_license.isNull()) { @@ -541,7 +578,7 @@ void Material::addModel(const QString& uuid) _allUuids << uuid; - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -641,7 +678,7 @@ void Material::addPhysical(const QString& uuid) return; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -688,7 +725,7 @@ void Material::removePhysical(const QString& uuid) return; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -718,7 +755,7 @@ void Material::addAppearance(const QString& uuid) return; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -759,7 +796,7 @@ void Material::removeAppearance(const QString& uuid) return; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -938,6 +975,22 @@ void Material::setValue(const QString& name, const QVariant& value) if (hasPhysicalProperty(name)) { setPhysicalValue(name, value); } + else if (hasAppearanceProperty(name)) { + setAppearanceValue(name, value); + } + else { + throw PropertyNotFound(); + } +} + +void Material::setValue(const QString& name, const std::shared_ptr& value) +{ + if (hasPhysicalProperty(name)) { + setPhysicalValue(name, value); + } + else if (hasAppearanceProperty(name)) { + setAppearanceValue(name, value); + } else { throw PropertyNotFound(); } @@ -1045,7 +1098,7 @@ Material::getValueString(const std::mapgetValue().toString(); @@ -1141,7 +1194,7 @@ bool Material::hasPhysicalModel(const QString& uuid) const return false; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -1161,7 +1214,7 @@ bool Material::hasAppearanceModel(const QString& uuid) const return false; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -1181,7 +1234,7 @@ bool Material::isPhysicalModelComplete(const QString& uuid) const return false; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -1207,7 +1260,7 @@ bool Material::isAppearanceModelComplete(const QString& uuid) const return false; } - ModelManager manager; + auto manager = ModelManager::getManager(); try { auto model = manager.getModel(uuid); @@ -1252,10 +1305,8 @@ void Material::saveGeneral(QTextStream& stream) const void Material::saveInherits(QTextStream& stream) const { if (!_parentUuid.isEmpty()) { - MaterialManager manager; - try { - auto material = manager.getMaterial(_parentUuid); + auto material = MaterialManager::getManager().getMaterial(_parentUuid); stream << "Inherits:\n"; stream << " " << material->getName() << ":\n"; @@ -1314,8 +1365,8 @@ void Material::saveModels(QTextStream& stream, bool saveInherited) const return; } - ModelManager modelManager; - MaterialManager materialManager; + auto modelManager = ModelManager::getManager(); + auto materialManager = MaterialManager::getManager(); bool inherited = saveInherited && (_parentUuid.size() > 0); std::shared_ptr parent; @@ -1368,8 +1419,8 @@ void Material::saveAppearanceModels(QTextStream& stream, bool saveInherited) con return; } - ModelManager modelManager; - MaterialManager materialManager; + auto modelManager = ModelManager::getManager(); + auto materialManager = MaterialManager::getManager(); bool inherited = saveInherited && (_parentUuid.size() > 0); std::shared_ptr parent; @@ -1421,7 +1472,7 @@ void Material::newUuid() QString Material::getModelByName(const QString& name) const { - ModelManager manager; + auto manager = ModelManager::getManager(); for (auto& it : _allUuids) { try { @@ -1442,8 +1493,7 @@ void Material::save(QTextStream& stream, bool overwrite, bool saveAsCopy, bool s if (saveInherited && !saveAsCopy) { // Check to see if we're an original or if we're already in the list of // models - MaterialManager materialManager; - if (materialManager.exists(_uuid) && !overwrite) { + if (MaterialManager::getManager().exists(_uuid) && !overwrite) { // Make a new version based on the current setParentUUID(_uuid); } @@ -1494,6 +1544,7 @@ Material& Material::operator=(const Material& other) _library = other._library; _directory = other._directory; + _filename = other._filename; _uuid = other._uuid; _name = other._name; _author = other._author; @@ -1548,20 +1599,20 @@ Material& Material::operator=(const App::Material& other) addAppearance(ModelUUIDs::ModelUUID_Rendering_Basic); } - getAppearanceProperty(QLatin1String("AmbientColor"))->setColor(other.ambientColor); - getAppearanceProperty(QLatin1String("DiffuseColor"))->setColor(other.diffuseColor); - getAppearanceProperty(QLatin1String("SpecularColor"))->setColor(other.specularColor); - getAppearanceProperty(QLatin1String("EmissiveColor"))->setColor(other.emissiveColor); - getAppearanceProperty(QLatin1String("Shininess"))->setFloat(other.shininess); - getAppearanceProperty(QLatin1String("Transparency"))->setFloat(other.transparency); + getAppearanceProperty(QStringLiteral("AmbientColor"))->setColor(other.ambientColor); + getAppearanceProperty(QStringLiteral("DiffuseColor"))->setColor(other.diffuseColor); + getAppearanceProperty(QStringLiteral("SpecularColor"))->setColor(other.specularColor); + getAppearanceProperty(QStringLiteral("EmissiveColor"))->setColor(other.emissiveColor); + getAppearanceProperty(QStringLiteral("Shininess"))->setFloat(other.shininess); + getAppearanceProperty(QStringLiteral("Transparency"))->setFloat(other.transparency); if (!other.image.empty() || !other.imagePath.empty()) { if (!hasAppearanceModel(ModelUUIDs::ModelUUID_Rendering_Texture)) { addAppearance(ModelUUIDs::ModelUUID_Rendering_Texture); } - getAppearanceProperty(QLatin1String("TextureImage"))->setString(other.image); - getAppearanceProperty(QLatin1String("TexturePath"))->setString(other.imagePath); + getAppearanceProperty(QStringLiteral("TextureImage"))->setString(other.image); + getAppearanceProperty(QStringLiteral("TexturePath"))->setString(other.imagePath); } return *this; @@ -1574,7 +1625,7 @@ QStringList Material::normalizeModels(const QStringList& models) { QStringList normalized; - ModelManager manager; + auto manager = ModelManager::getManager(); for (auto& uuid : models) { auto model = manager.getModel(uuid); @@ -1652,32 +1703,32 @@ App::Material Material::getMaterialAppearance() const App::Material material(App::Material::DEFAULT); bool custom = false; - if (hasAppearanceProperty(QLatin1String("AmbientColor"))) { - material.ambientColor = getAppearanceProperty(QLatin1String("AmbientColor"))->getColor(); + if (hasAppearanceProperty(QStringLiteral("AmbientColor"))) { + material.ambientColor = getAppearanceProperty(QStringLiteral("AmbientColor"))->getColor(); custom = true; } - if (hasAppearanceProperty(QLatin1String("DiffuseColor"))) { - material.diffuseColor = getAppearanceProperty(QLatin1String("DiffuseColor"))->getColor(); + if (hasAppearanceProperty(QStringLiteral("DiffuseColor"))) { + material.diffuseColor = getAppearanceProperty(QStringLiteral("DiffuseColor"))->getColor(); custom = true; } - if (hasAppearanceProperty(QLatin1String("SpecularColor"))) { - material.specularColor = getAppearanceProperty(QLatin1String("SpecularColor"))->getColor(); + if (hasAppearanceProperty(QStringLiteral("SpecularColor"))) { + material.specularColor = getAppearanceProperty(QStringLiteral("SpecularColor"))->getColor(); custom = true; } - if (hasAppearanceProperty(QLatin1String("EmissiveColor"))) { - material.emissiveColor = getAppearanceProperty(QLatin1String("EmissiveColor"))->getColor(); + if (hasAppearanceProperty(QStringLiteral("EmissiveColor"))) { + material.emissiveColor = getAppearanceProperty(QStringLiteral("EmissiveColor"))->getColor(); custom = true; } - if (hasAppearanceProperty(QLatin1String("Shininess"))) { - material.shininess = getAppearanceProperty(QLatin1String("Shininess"))->getFloat(); + if (hasAppearanceProperty(QStringLiteral("Shininess"))) { + material.shininess = getAppearanceProperty(QStringLiteral("Shininess"))->getFloat(); custom = true; } - if (hasAppearanceProperty(QLatin1String("Transparency"))) { - material.transparency = getAppearanceProperty(QLatin1String("Transparency"))->getFloat(); + if (hasAppearanceProperty(QStringLiteral("Transparency"))) { + material.transparency = getAppearanceProperty(QStringLiteral("Transparency"))->getFloat(); custom = true; } - if (hasAppearanceProperty(QLatin1String("TextureImage"))) { - auto property = getAppearanceProperty(QLatin1String("TextureImage")); + if (hasAppearanceProperty(QStringLiteral("TextureImage"))) { + auto property = getAppearanceProperty(QStringLiteral("TextureImage")); if (!property->isNull()) { Base::Console().Log("Has 'TextureImage'\n"); material.image = property->getString().toStdString(); @@ -1685,8 +1736,8 @@ App::Material Material::getMaterialAppearance() const custom = true; } - else if (hasAppearanceProperty(QLatin1String("TexturePath"))) { - auto property = getAppearanceProperty(QLatin1String("TexturePath")); + else if (hasAppearanceProperty(QStringLiteral("TexturePath"))) { + auto property = getAppearanceProperty(QStringLiteral("TexturePath")); if (!property->isNull()) { Base::Console().Log("Has 'TexturePath'\n"); material.imagePath = property->getString().toStdString(); @@ -1702,3 +1753,98 @@ App::Material Material::getMaterialAppearance() const return material; } + +void Material::validate(const std::shared_ptr& other) const +{ + + try { + _library->validate(*(other->_library)); + } + catch (const InvalidLibrary& e) { + throw InvalidMaterial(e.what()); + } + + if (_directory != other->_directory) { + throw InvalidMaterial("Model directories don't match"); + } + if (!other->_filename.isEmpty()) { + throw InvalidMaterial("Remote filename is not empty"); + } + if (_uuid != other->_uuid) { + throw InvalidMaterial("Model UUIDs don't match"); + } + if (_name != other->_name) { + throw InvalidMaterial("Model names don't match"); + } + if (_author != other->_author) { + throw InvalidMaterial("Model authors don't match"); + } + if (_license != other->_license) { + throw InvalidMaterial("Model licenses don't match"); + } + if (_parentUuid != other->_parentUuid) { + throw InvalidMaterial("Model parents don't match"); + } + if (_description != other->_description) { + throw InvalidMaterial("Model descriptions don't match"); + } + if (_url != other->_url) { + throw InvalidMaterial("Model URLs don't match"); + } + if (_reference != other->_reference) { + throw InvalidMaterial("Model references don't match"); + } + + if (_tags.size() != other->_tags.size()) { + Base::Console().Log("Local tags count %d\n", _tags.size()); + Base::Console().Log("Remote tags count %d\n", other->_tags.size()); + throw InvalidMaterial("Material tags counts don't match"); + } + if (!other->_tags.contains(_tags)) { + throw InvalidMaterial("Material tags don't match"); + } + + if (_physicalUuids.size() != other->_physicalUuids.size()) { + Base::Console().Log("Local physical model count %d\n", _physicalUuids.size()); + Base::Console().Log("Remote physical model count %d\n", other->_physicalUuids.size()); + throw InvalidMaterial("Material physical model counts don't match"); + } + if (!other->_physicalUuids.contains(_physicalUuids)) { + throw InvalidMaterial("Material physical models don't match"); + } + + if (_physicalUuids.size() != other->_physicalUuids.size()) { + Base::Console().Log("Local appearance model count %d\n", _physicalUuids.size()); + Base::Console().Log("Remote appearance model count %d\n", other->_physicalUuids.size()); + throw InvalidMaterial("Material appearance model counts don't match"); + } + if (!other->_physicalUuids.contains(_physicalUuids)) { + throw InvalidMaterial("Material appearance models don't match"); + } + + if (_allUuids.size() != other->_allUuids.size()) { + Base::Console().Log("Local model count %d\n", _allUuids.size()); + Base::Console().Log("Remote model count %d\n", other->_allUuids.size()); + throw InvalidMaterial("Material model counts don't match"); + } + if (!other->_allUuids.contains(_allUuids)) { + throw InvalidMaterial("Material models don't match"); + } + + // Need to compare properties + if (_physical.size() != other->_physical.size()) { + throw InvalidMaterial("Material physical property counts don't match"); + } + for (auto& property : _physical) { + auto& remote = other->_physical[property.first]; + property.second->validate(*remote); + } + + if (_appearance.size() != other->_appearance.size()) { + throw InvalidMaterial("Material appearance property counts don't match"); + } + for (auto& property : _appearance) { + auto& remote = other->_appearance[property.first]; + property.second->validate(*remote); + } +} diff --git a/src/Mod/Material/App/Materials.h b/src/Mod/Material/App/Materials.h index e94201dd22..4d57c70259 100644 --- a/src/Mod/Material/App/Materials.h +++ b/src/Mod/Material/App/Materials.h @@ -80,6 +80,10 @@ public: { return _valuePtr->isNull(); } + bool isEmpty() const + { + return _valuePtr->isEmpty(); + } std::shared_ptr getMaterialValue(); std::shared_ptr getMaterialValue() const; QString getString() const; @@ -109,6 +113,10 @@ public: MaterialValue::ValueType getColumnType(int column) const; QString getColumnUnits(int column) const; QVariant getColumnNull(int column) const; + const std::vector& getColumns() const + { + return _columns; + } void setModelUUID(const QString& uuid); void setPropertyType(const QString& type) override; @@ -140,6 +148,11 @@ public: return !operator==(other); } + void validate(const MaterialProperty& other) const; + + // Define precision for displaying floating point values + static int const PRECISION; + protected: void setType(const QString& type); // void setType(MaterialValue::ValueType type) { _valueType = type; } @@ -180,10 +193,9 @@ public: { return _library; } - QString getDirectory() const - { - return _directory; - } + QString getDirectory() const; + QString getFilename() const; + QString getFilePath() const; QString getUUID() const { return _uuid; @@ -240,10 +252,8 @@ public: { _library = library; } - void setDirectory(const QString& directory) - { - _directory = directory; - } + void setDirectory(const QString& directory); + void setFilename(const QString& filename); void setUUID(const QString& uuid) { _uuid = uuid; @@ -303,6 +313,7 @@ public: void setValue(const QString& name, const QString& value); void setValue(const QString& name, const QVariant& value); + void setValue(const QString& name, const std::shared_ptr& value); /* * Legacy values are thosed contained in old format files that don't fit in the new @@ -432,6 +443,8 @@ public: return getTypeId() == other.getTypeId() && _uuid == other._uuid; } + void validate(const std::shared_ptr& other) const; + protected: void addModel(const QString& uuid); static void removeUUID(QSet& uuidList, const QString& uuid); @@ -455,6 +468,7 @@ protected: private: std::shared_ptr _library; QString _directory; + QString _filename; QString _uuid; QString _name; QString _author; diff --git a/src/Mod/Material/App/Model.cpp b/src/Mod/Material/App/Model.cpp index fddc67b9d2..6de9a2fa89 100644 --- a/src/Mod/Material/App/Model.cpp +++ b/src/Mod/Material/App/Model.cpp @@ -101,11 +101,49 @@ bool ModelProperty::operator==(const ModelProperty& other) const return true; } - return (_name == other._name) && (_displayName == other._displayName) && (_propertyType == other._propertyType) - && (_units == other._units) && (_url == other._url) && (_description == other._description) + return (_name == other._name) && (_displayName == other._displayName) + && (_propertyType == other._propertyType) && (_units == other._units) + && (_url == other._url) && (_description == other._description) && (_inheritance == other._inheritance); } +void ModelProperty::validate(const ModelProperty& other) const +{ + if (_name != other._name) { + throw InvalidProperty("Model names don't match"); + } + if (getDisplayName() != other.getDisplayName()) { + Base::Console().Log("Local display name '%s'\n", getDisplayName().toStdString().c_str()); + Base::Console().Log("Remote display name '%s'\n", + other.getDisplayName().toStdString().c_str()); + throw InvalidProperty("Model display names don't match"); + } + if (_propertyType != other._propertyType) { + throw InvalidProperty("Model property types don't match"); + } + if (_units != other._units) { + throw InvalidProperty("Model units don't match"); + } + if (_url != other._url) { + throw InvalidProperty("Model URLs don't match"); + } + if (_description != other._description) { + throw InvalidProperty("Model descriptions don't match"); + } + if (_inheritance != other._inheritance) { + throw InvalidProperty("Model inheritance don't match"); + } + + if (_columns.size() != other._columns.size()) { + Base::Console().Log("Local property column count %d\n", _columns.size()); + Base::Console().Log("Remote property column count %d\n", other._columns.size()); + throw InvalidProperty("Model property column counts don't match"); + } + for (int i = 0; i < _columns.size(); i++) { + _columns[i].validate(other._columns[i]); + } +} + TYPESYSTEM_SOURCE(Materials::Model, Base::BaseClass) Model::Model() @@ -129,6 +167,31 @@ Model::Model(std::shared_ptr library, , _doi(doi) {} +QString Model::getDirectory() const +{ + return _directory; +} + +void Model::setDirectory(const QString& directory) +{ + _directory = directory; +} + +QString Model::getFilename() const +{ + return _filename; +} + +void Model::setFilename(const QString& filename) +{ + _filename = filename; +} + +QString Model::getFilePath() const +{ + return QDir(_directory + QStringLiteral("/") + _filename).absolutePath(); +} + ModelProperty& Model::operator[](const QString& key) { try { @@ -138,3 +201,54 @@ ModelProperty& Model::operator[](const QString& key) throw PropertyNotFound(); } } + +void Model::validate(const std::shared_ptr& other) const +{ + try { + _library->validate(*(other->_library)); + } + catch (const InvalidLibrary& e) + { + throw InvalidModel(e.what()); + } + + // std::map _properties; + if (_type != other->_type) { + throw InvalidModel("Model types don't match"); + } + if (_name != other->_name) { + throw InvalidModel("Model names don't match"); + } + if (_directory != other->_directory) { + throw InvalidModel("Model directories don't match"); + } + if (!other->_filename.isEmpty()) { + throw InvalidModel("Remote filename is not empty"); + } + if (_uuid != other->_uuid) { + throw InvalidModel("Model UUIDs don't match"); + } + if (_description != other->_description) { + throw InvalidModel("Model descriptions don't match"); + } + if (_url != other->_url) { + throw InvalidModel("Model URLs don't match"); + } + if (_doi != other->_doi) { + throw InvalidModel("Model DOIs don't match"); + } + if (_inheritedUuids != other->_inheritedUuids) { + throw InvalidModel("Model inherited UUIDs don't match"); + } + + // Need to compare properties + if (_properties.size() != other->_properties.size()) { + // Base::Console().Log("Local property count %d\n", _properties.size()); + // Base::Console().Log("Remote property count %d\n", other->_properties.size()); + throw InvalidModel("Model property counts don't match"); + } + for (auto& property : _properties) { + auto& remote = other->_properties[property.first]; + property.second.validate(remote); + } +} diff --git a/src/Mod/Material/App/Model.h b/src/Mod/Material/App/Model.h index 86b039ec33..517651e7d5 100644 --- a/src/Mod/Material/App/Model.h +++ b/src/Mod/Material/App/Model.h @@ -98,7 +98,7 @@ public: { _name = name; } - void setColumnHeader(const QString& header) + void setDisplayName(const QString& header) { _displayName = header; } @@ -143,6 +143,8 @@ public: return !operator==(other); } + void validate(const ModelProperty& other) const; + private: QString _name; QString _displayName; @@ -180,12 +182,12 @@ public: { return _library; } - const QString getBase() const + QString getBase() const { return (_type == ModelType_Physical) ? QStringLiteral("Model") : QStringLiteral("AppearanceModel"); } - const QString getName() const + QString getName() const { return _name; } @@ -193,27 +195,22 @@ public: { return _type; } - const QString getDirectory() const - { - return _directory; - } - const QString getDirectoryPath() const - { - return QDir(_directory).absolutePath(); - } - const QString getUUID() const + QString getDirectory() const; + QString getFilename() const; + QString getFilePath() const; + QString getUUID() const { return _uuid; } - const QString getDescription() const + QString getDescription() const { return _description; } - const QString getURL() const + QString getURL() const { return _url; } - const QString getDOI() const + QString getDOI() const { return _doi; } @@ -230,10 +227,8 @@ public: { _name = name; } - void setDirectory(const QString& directory) - { - _directory = directory; - } + void setDirectory(const QString& directory); + void setFilename(const QString& filename); void setUUID(const QString& uuid) { _uuid = uuid; @@ -306,11 +301,14 @@ public: return _properties.cend(); } + void validate(const std::shared_ptr& other) const; + private: std::shared_ptr _library; ModelType _type; QString _name; QString _directory; + QString _filename; QString _uuid; QString _description; QString _url; diff --git a/src/Mod/Material/App/ModelLibrary.cpp b/src/Mod/Material/App/ModelLibrary.cpp index 2388fe740e..acc118a653 100644 --- a/src/Mod/Material/App/ModelLibrary.cpp +++ b/src/Mod/Material/App/ModelLibrary.cpp @@ -24,6 +24,8 @@ #include #endif +#include + #include #include "Exceptions.h" @@ -34,79 +36,13 @@ using namespace Materials; -TYPESYSTEM_SOURCE(Materials::LibraryBase, Base::BaseClass) +TYPESYSTEM_SOURCE(Materials::ModelLibrary, Materials::Library) -LibraryBase::LibraryBase(const QString& libraryName, const QString& dir, const QString& icon) - : _name(libraryName) - , _directory(QDir::cleanPath(dir)) - , _iconPath(icon) -{} - -bool LibraryBase::operator==(const LibraryBase& library) const -{ - return (_name == library._name) && (_directory == library._directory); -} - -QString LibraryBase::getLocalPath(const QString& path) const -{ - QString filePath = getDirectoryPath(); - if (!(filePath.endsWith(QLatin1String("/")) || filePath.endsWith(QLatin1String("\\")))) { - filePath += QLatin1String("/"); - } - - QString cleanPath = QDir::cleanPath(path); - QString prefix = QStringLiteral("/") + getName(); - if (cleanPath.startsWith(prefix)) { - // Remove the library name from the path - filePath += cleanPath.right(cleanPath.length() - prefix.length()); - } - else { - filePath += cleanPath; - } - - return filePath; -} - -bool LibraryBase::isRoot(const QString& path) const -{ - QString localPath = getLocalPath(path); - QString cleanPath = getLocalPath(QStringLiteral("")); - std::string pLocal = localPath.toStdString(); - std::string pclean = cleanPath.toStdString(); - return (cleanPath == localPath); -} - -QString LibraryBase::getRelativePath(const QString& path) const -{ - QString filePath; - QString cleanPath = QDir::cleanPath(path); - QString prefix = QStringLiteral("/") + getName(); - if (cleanPath.startsWith(prefix)) { - // Remove the library name from the path - filePath = cleanPath.right(cleanPath.length() - prefix.length()); - } - else { - filePath = cleanPath; - } - - prefix = getDirectoryPath(); - if (filePath.startsWith(prefix)) { - // Remove the library root from the path - filePath = filePath.right(filePath.length() - prefix.length()); - } - - // Remove any leading '/' - if (filePath.startsWith(QStringLiteral("/"))) { - filePath.remove(0, 1); - } - - return filePath; -} - -TYPESYSTEM_SOURCE(Materials::ModelLibrary, Materials::LibraryBase) - -ModelLibrary::ModelLibrary(const QString& libraryName, const QString& dir, const QString& icon) - : LibraryBase(libraryName, dir, icon) +ModelLibrary::ModelLibrary(const QString& libraryName, + const QString& dir, + const QString& icon, + bool readOnly) + : Library(libraryName, dir, icon, readOnly) { _modelPathMap = std::make_unique>>(); } @@ -131,9 +67,11 @@ std::shared_ptr ModelLibrary::getModelByPath(const QString& path) const std::shared_ptr ModelLibrary::addModel(const Model& model, const QString& path) { QString filePath = getRelativePath(path); + QFileInfo info(filePath); std::shared_ptr newModel = std::make_shared(model); newModel->setLibrary(getptr()); - newModel->setDirectory(filePath); + newModel->setDirectory(getLibraryPath(filePath, info.fileName())); + newModel->setFilename(info.fileName()); (*_modelPathMap)[filePath] = newModel; @@ -158,6 +96,7 @@ ModelLibrary::getModelTree(ModelFilter filter) const for (auto& itp : list) { if (ModelManager::isModel(itp)) { std::shared_ptr child = std::make_shared(); + child->setUUID(model->getUUID()); child->setData(model); (*node)[itp] = child; } diff --git a/src/Mod/Material/App/ModelLibrary.h b/src/Mod/Material/App/ModelLibrary.h index e77b2c9da1..ae31c982f0 100644 --- a/src/Mod/Material/App/ModelLibrary.h +++ b/src/Mod/Material/App/ModelLibrary.h @@ -32,68 +32,28 @@ #include +#include "Library.h" #include "MaterialValue.h" #include "Model.h" namespace Materials { -// class Model; - -class MaterialsExport LibraryBase: public Base::BaseClass -{ - TYPESYSTEM_HEADER_WITH_OVERRIDE(); - -public: - LibraryBase() = default; - LibraryBase(const QString& libraryName, const QString& dir, const QString& icon); - ~LibraryBase() override = default; - - const QString getName() const - { - return _name; - } - const QString getDirectory() const - { - return _directory; - } - const QString getDirectoryPath() const - { - return QDir(_directory).absolutePath(); - } - const QString getIconPath() const - { - return _iconPath; - } - bool operator==(const LibraryBase& library) const; - bool operator!=(const LibraryBase& library) const - { - return !operator==(library); - } - QString getLocalPath(const QString& path) const; - QString getRelativePath(const QString& path) const; - bool isRoot(const QString& path) const; - -private: - LibraryBase(const LibraryBase&); - - QString _name; - QString _directory; - QString _iconPath; -}; - -class MaterialsExport ModelLibrary: public LibraryBase, +class MaterialsExport ModelLibrary: public Library, public std::enable_shared_from_this { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: ModelLibrary(); - ModelLibrary(const QString& libraryName, const QString& dir, const QString& icon); + ModelLibrary(const QString& libraryName, + const QString& dir, + const QString& icon, + bool readOnly = true); ~ModelLibrary() override = default; bool operator==(const ModelLibrary& library) const { - return LibraryBase::operator==(library); + return Library::operator==(library); } bool operator!=(const ModelLibrary& library) const { @@ -119,4 +79,6 @@ private: } // namespace Materials +Q_DECLARE_METATYPE(std::shared_ptr) + #endif // MATERIAL_MODELLIBRARY_H diff --git a/src/Mod/Material/App/ModelManager.cpp b/src/Mod/Material/App/ModelManager.cpp index 8ca8a8a477..40b077836c 100644 --- a/src/Mod/Material/App/ModelManager.cpp +++ b/src/Mod/Material/App/ModelManager.cpp @@ -26,118 +26,141 @@ #include #include +#include #include #include "Model.h" #include "ModelLoader.h" #include "ModelManager.h" +#include "ModelManagerLocal.h" using namespace Materials; -std::shared_ptr>> ModelManager::_libraryList = nullptr; -std::shared_ptr>> ModelManager::_modelMap = nullptr; -QMutex ModelManager::_mutex; - TYPESYSTEM_SOURCE(Materials::ModelManager, Base::BaseClass) +QMutex ModelManager::_mutex; +ModelManager* ModelManager::_manager = nullptr; +std::unique_ptr ModelManager::_localManager; + ModelManager::ModelManager() +{} + +ModelManager::~ModelManager() +{} + +ModelManager& ModelManager::getManager() { - initLibraries(); + if (!_manager) { + initManagers(); + } + + return *_manager; } -void ModelManager::initLibraries() +void ModelManager::initManagers() { QMutexLocker locker(&_mutex); - if (_modelMap == nullptr) { - _modelMap = std::make_shared>>(); - if (_libraryList == nullptr) { - _libraryList = std::make_shared>>(); - } - - // Load the libraries - ModelLoader loader(_modelMap, _libraryList); + if (!_manager) { + // Can't use smart pointers for this since the constructor is private + _manager = new ModelManager(); + } + if (!_localManager) { + _localManager = std::make_unique(); } } bool ModelManager::isModel(const QString& file) { - // if (!fs::is_regular_file(p)) - // return false; - // check file extension - if (file.endsWith(QStringLiteral(".yml"))) { - return true; - } - return false; + return ModelManagerLocal::isModel(file); } void ModelManager::cleanup() { - if (_libraryList) { - _libraryList->clear(); - } - - if (_modelMap) { - for (auto& it : *_modelMap) { - // This is needed to resolve cyclic dependencies - it.second->setLibrary(nullptr); - } - _modelMap->clear(); - } + return ModelManagerLocal::cleanup(); } void ModelManager::refresh() { - _modelMap->clear(); - _libraryList->clear(); + _localManager->refresh(); +} - // Load the libraries - ModelLoader loader(_modelMap, _libraryList); +std::shared_ptr>> ModelManager::getLibraries() +{ + return _localManager->getLibraries(); +} + +std::shared_ptr>> ModelManager::getLocalLibraries() +{ + return _localManager->getLibraries(); +} + +void ModelManager::createLibrary(const QString& libraryName, const QString& icon, bool readOnly) +{} + +void ModelManager::createLocalLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly) +{ + _localManager->createLibrary(libraryName, directory, icon, readOnly); +} + +void ModelManager::renameLibrary(const QString& libraryName, const QString& newName) +{ + _localManager->renameLibrary(libraryName, newName); +} + +void ModelManager::changeIcon(const QString& libraryName, const QString& icon) +{ + _localManager->changeIcon(libraryName, icon); +} + +void ModelManager::removeLibrary(const QString& libraryName) +{ + _localManager->removeLibrary(libraryName); +} + +std::shared_ptr>> +ModelManager::libraryModels(const QString& libraryName) +{ + return _localManager->libraryModels(libraryName); +} + +bool ModelManager::isLocalLibrary(const QString& libraryName) +{ + return true; +} + +std::shared_ptr>> ModelManager::getModels() +{ + return _localManager->getModels(); +} + +std::shared_ptr>> ModelManager::getLocalModels() +{ + return _localManager->getModels(); } std::shared_ptr ModelManager::getModel(const QString& uuid) const { - try { - if (_modelMap == nullptr) { - throw Uninitialized(); - } - - return _modelMap->at(uuid); - } - catch (std::out_of_range const&) { - throw ModelNotFound(); - } + return _localManager->getModel(uuid); } std::shared_ptr ModelManager::getModelByPath(const QString& path) const { - QString cleanPath = QDir::cleanPath(path); - - for (auto& library : *_libraryList) { - if (cleanPath.startsWith(library->getDirectory())) { - return library->getModelByPath(cleanPath); - } - } - - throw MaterialNotFound(); + return _localManager->getModelByPath(path); } std::shared_ptr ModelManager::getModelByPath(const QString& path, const QString& lib) const { - auto library = getLibrary(lib); // May throw LibraryNotFound - return library->getModelByPath(path); // May throw ModelNotFound + return _localManager->getModelByPath(path, lib); } std::shared_ptr ModelManager::getLibrary(const QString& name) const { - for (auto& library : *_libraryList) { - if (library->getName() == name) { - return library; - } - } - - throw LibraryNotFound(); + return _localManager->getLibrary(name); } bool ModelManager::passFilter(ModelFilter filter, Model::ModelType modelType) diff --git a/src/Mod/Material/App/ModelManager.h b/src/Mod/Material/App/ModelManager.h index 15b433930d..958f0f5fb9 100644 --- a/src/Mod/Material/App/ModelManager.h +++ b/src/Mod/Material/App/ModelManager.h @@ -24,6 +24,7 @@ #include +#include #include #include @@ -35,26 +36,37 @@ namespace Materials { +class ModelManagerLocal; +class ModelManagerExternal; class MaterialsExport ModelManager: public Base::BaseClass { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: - ModelManager(); - ~ModelManager() override = default; + ~ModelManager() override; + + static ModelManager& getManager(); static void cleanup(); void refresh(); - std::shared_ptr>> getModelLibraries() - { - return _libraryList; - } - std::shared_ptr>> getModels() - { - return _modelMap; - } + std::shared_ptr>> getLibraries(); + std::shared_ptr>> getLocalLibraries(); + void createLibrary(const QString& libraryName, const QString& icon, bool readOnly = true); + void createLocalLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly = true); + void renameLibrary(const QString& libraryName, const QString& newName); + void changeIcon(const QString& libraryName, const QString& icon); + void removeLibrary(const QString& libraryName); + std::shared_ptr>> + libraryModels(const QString& libraryName); + bool isLocalLibrary(const QString& libraryName); + + std::shared_ptr>> getModels(); + std::shared_ptr>> getLocalModels(); std::shared_ptr>> getModelTree(std::shared_ptr library, ModelFilter filter = ModelFilter_None) const { @@ -69,13 +81,14 @@ public: static bool passFilter(ModelFilter filter, Model::ModelType modelType); private: - static void initLibraries(); + ModelManager(); + static void initManagers(); - static std::shared_ptr>> _libraryList; - static std::shared_ptr>> _modelMap; + static ModelManager* _manager; + static std::unique_ptr _localManager; static QMutex _mutex; }; } // namespace Materials -#endif // MATERIAL_MODELMANAGER_H +#endif // MATERIAL_MODELMANAGER_H \ No newline at end of file diff --git a/src/Mod/Material/App/ModelManagerLocal.cpp b/src/Mod/Material/App/ModelManagerLocal.cpp new file mode 100644 index 0000000000..798736c467 --- /dev/null +++ b/src/Mod/Material/App/ModelManagerLocal.cpp @@ -0,0 +1,219 @@ +/*************************************************************************** + * Copyright (c) 2024 David Carter * + * * + * 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_ +#endif + +#include +#include + +#include + +#include "Model.h" +#include "ModelLoader.h" +#include "ModelManagerLocal.h" + +using namespace Materials; + +std::shared_ptr>> ModelManagerLocal::_libraryList = nullptr; +std::shared_ptr>> ModelManagerLocal::_modelMap = nullptr; +QMutex ModelManagerLocal::_mutex; + + +TYPESYSTEM_SOURCE(Materials::ModelManagerLocal, Base::BaseClass) + +ModelManagerLocal::ModelManagerLocal() +{ + initLibraries(); +} + +void ModelManagerLocal::initLibraries() +{ + QMutexLocker locker(&_mutex); + + if (_modelMap == nullptr) { + _modelMap = std::make_shared>>(); + if (_libraryList == nullptr) { + _libraryList = std::make_shared>>(); + } + + // Load the libraries + ModelLoader loader(_modelMap, _libraryList); + } +} + +bool ModelManagerLocal::isModel(const QString& file) +{ + // if (!fs::is_regular_file(p)) + // return false; + // check file extension + if (file.endsWith(QStringLiteral(".yml"))) { + return true; + } + return false; +} + +void ModelManagerLocal::cleanup() +{ + if (_libraryList) { + _libraryList->clear(); + } + + if (_modelMap) { + for (auto& it : *_modelMap) { + // This is needed to resolve cyclic dependencies + it.second->setLibrary(nullptr); + } + _modelMap->clear(); + } +} + +void ModelManagerLocal::refresh() +{ + _modelMap->clear(); + _libraryList->clear(); + + // Load the libraries + ModelLoader loader(_modelMap, _libraryList); +} + +std::shared_ptr>> ModelManagerLocal::getLibraries() +{ + return _libraryList; +} + +void ModelManagerLocal::createLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly) +{ + QDir dir; + if (!dir.exists(directory)) { + if (!dir.mkpath(directory)) { + throw CreationError("Unable to create library path"); + } + } + + auto modelLibrary = std::make_shared(libraryName, directory, icon, readOnly); + _libraryList->push_back(modelLibrary); + + // This needs to be persisted somehow +} + +void ModelManagerLocal::renameLibrary(const QString& libraryName, const QString& newName) +{ + for (auto& library : *_libraryList) { + if (library->getName() == libraryName) { + library->setName(newName); + return; + } + } + + throw LibraryNotFound(); +} + +void ModelManagerLocal::changeIcon(const QString& libraryName, const QString& icon) +{ + for (auto& library : *_libraryList) { + if (library->getName() == libraryName) { + library->setIconPath(icon); + return; + } + } + + throw LibraryNotFound(); +} + +void ModelManagerLocal::removeLibrary(const QString& libraryName) +{ + for (auto& library : *_libraryList) { + if (library->getName() == libraryName) { + _libraryList->remove(library); + + // At this point we should rebuild the model map + return; + } + } + + throw LibraryNotFound(); +} + +std::shared_ptr>> +ModelManagerLocal::libraryModels(const QString& libraryName) +{ + auto models = std::make_shared>>(); + + for (auto& it : *_modelMap) { + // This is needed to resolve cyclic dependencies + if (it.second->getLibrary()->getName() == libraryName) { + models->push_back( + std::tuple(it.first, it.second->getDirectory(), it.second->getName())); + } + } + + return models; +} + +std::shared_ptr ModelManagerLocal::getModel(const QString& uuid) const +{ + try { + if (_modelMap == nullptr) { + throw Uninitialized(); + } + + return _modelMap->at(uuid); + } + catch (std::out_of_range const&) { + throw ModelNotFound(); + } +} + +std::shared_ptr ModelManagerLocal::getModelByPath(const QString& path) const +{ + QString cleanPath = QDir::cleanPath(path); + + for (auto& library : *_libraryList) { + if (cleanPath.startsWith(library->getDirectory())) { + return library->getModelByPath(cleanPath); + } + } + + throw MaterialNotFound(); +} + +std::shared_ptr ModelManagerLocal::getModelByPath(const QString& path, + const QString& lib) const +{ + auto library = getLibrary(lib); // May throw LibraryNotFound + return library->getModelByPath(path); // May throw ModelNotFound +} + +std::shared_ptr ModelManagerLocal::getLibrary(const QString& name) const +{ + for (auto& library : *_libraryList) { + if (library->getName() == name) { + return library; + } + } + + throw LibraryNotFound(); +} diff --git a/src/Mod/Material/App/ModelManagerLocal.h b/src/Mod/Material/App/ModelManagerLocal.h new file mode 100644 index 0000000000..535a3428c9 --- /dev/null +++ b/src/Mod/Material/App/ModelManagerLocal.h @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (c) 2024 David Carter * + * * + * 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 MATERIAL_MODELMANAGERLOCAL_H +#define MATERIAL_MODELMANAGERLOCAL_H + +#include + +#include + +#include + +#include "Exceptions.h" +#include "FolderTree.h" +#include "Model.h" +#include "ModelLibrary.h" + +namespace Materials +{ + +class MaterialsExport ModelManagerLocal: public Base::BaseClass +{ + TYPESYSTEM_HEADER_WITH_OVERRIDE(); + +public: + ModelManagerLocal(); + ~ModelManagerLocal() override = default; + + static void cleanup(); + void refresh(); + + std::shared_ptr>> getLibraries(); + void createLibrary(const QString& libraryName, + const QString& directory, + const QString& icon, + bool readOnly = true); + void renameLibrary(const QString& libraryName, const QString& newName); + void changeIcon(const QString& libraryName, const QString& icon); + void removeLibrary(const QString& libraryName); + std::shared_ptr>> + libraryModels(const QString& libraryName); + + std::shared_ptr>> getModels() + { + return _modelMap; + } + std::shared_ptr>> + getModelTree(std::shared_ptr library, ModelFilter filter = ModelFilter_None) const + { + return library->getModelTree(filter); + } + std::shared_ptr getModel(const QString& uuid) const; + std::shared_ptr getModelByPath(const QString& path) const; + std::shared_ptr getModelByPath(const QString& path, const QString& lib) const; + std::shared_ptr getLibrary(const QString& name) const; + + static bool isModel(const QString& file); + +private: + static void initLibraries(); + + static std::shared_ptr>> _libraryList; + static std::shared_ptr>> _modelMap; + static QMutex _mutex; +}; + +} // namespace Materials + +#endif // MATERIAL_MODELMANAGERLOCAL_H \ No newline at end of file diff --git a/src/Mod/Material/App/ModelManagerPy.xml b/src/Mod/Material/App/ModelManagerPy.xml index b6e48da967..693cf04238 100644 --- a/src/Mod/Material/App/ModelManagerPy.xml +++ b/src/Mod/Material/App/ModelManagerPy.xml @@ -10,7 +10,7 @@ FatherInclude="Base/BaseClassPy.h" FatherNamespace="Base" Constructor="true" - Delete="true"> + Delete="false"> Material model descriptions. @@ -31,6 +31,12 @@ + + + List of local model libraries. + + + List of model libraries. diff --git a/src/Mod/Material/App/ModelManagerPyImp.cpp b/src/Mod/Material/App/ModelManagerPyImp.cpp index 6fdc872508..3dd8d6467d 100644 --- a/src/Mod/Material/App/ModelManagerPyImp.cpp +++ b/src/Mod/Material/App/ModelManagerPyImp.cpp @@ -43,7 +43,7 @@ std::string ModelManagerPy::representation() const PyObject* ModelManagerPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper { // never create such objects with the constructor - return new ModelManagerPy(new ModelManager()); + return new ModelManagerPy(&(ModelManager::getManager())); } // constructor method @@ -116,7 +116,7 @@ PyObject* ModelManagerPy::getModelByPath(PyObject* args) Py::List ModelManagerPy::getModelLibraries() const { - auto libraries = getModelManagerPtr()->getModelLibraries(); + auto libraries = getModelManagerPtr()->getLibraries(); Py::List list; for (auto it = libraries->begin(); it != libraries->end(); it++) { @@ -125,6 +125,26 @@ Py::List ModelManagerPy::getModelLibraries() const libTuple.setItem(0, Py::String(lib->getName().toStdString())); libTuple.setItem(1, Py::String(lib->getDirectoryPath().toStdString())); libTuple.setItem(2, Py::String(lib->getIconPath().toStdString())); + libTuple.setItem(3, Py::Boolean(lib->isReadOnly())); + + list.append(libTuple); + } + + return list; +} + +Py::List ModelManagerPy::getLocalModelLibraries() const +{ + auto libraries = getModelManagerPtr()->getLocalLibraries(); + Py::List list; + + for (auto it = libraries->begin(); it != libraries->end(); it++) { + auto lib = *it; + Py::Tuple libTuple(3); + libTuple.setItem(0, Py::String(lib->getName().toStdString())); + libTuple.setItem(1, Py::String(lib->getDirectoryPath().toStdString())); + libTuple.setItem(2, Py::String(lib->getIconPath().toStdString())); + libTuple.setItem(3, Py::Boolean(lib->isReadOnly())); list.append(libTuple); } diff --git a/src/Mod/Material/App/ModelPropertyPy.xml b/src/Mod/Material/App/ModelPropertyPy.xml index ebe7a5d136..195e5b4581 100644 --- a/src/Mod/Material/App/ModelPropertyPy.xml +++ b/src/Mod/Material/App/ModelPropertyPy.xml @@ -15,35 +15,64 @@ Material property descriptions. - + Property name. - + + + Property display friendly name. + + + + Property type. - + Property units category. - + URL to a detailed description of the property. - + Property description. + + + List of array columns. + + + + + + UUID of the model in which the property is defined. + + + + + + True if the property is inherited. + + + + + + Add a model property column. + + - + \ No newline at end of file diff --git a/src/Mod/Material/App/ModelPropertyPyImp.cpp b/src/Mod/Material/App/ModelPropertyPyImp.cpp index d701df9da6..90974e770b 100644 --- a/src/Mod/Material/App/ModelPropertyPyImp.cpp +++ b/src/Mod/Material/App/ModelPropertyPyImp.cpp @@ -31,19 +31,8 @@ using namespace Materials; // returns a string which represents the object e.g. when printed in python std::string ModelPropertyPy::representation() const { - ModelPropertyPy::PointerType ptr = getModelPropertyPtr(); std::stringstream str; - str << "Property [Name=("; - str << ptr->getName().toStdString(); - str << "), Type=("; - str << ptr->getPropertyType().toStdString(); - str << "), Units=("; - str << ptr->getUnits().toStdString(); - str << "), URL=("; - str << ptr->getURL().toStdString(); - str << "), Description=("; - str << ptr->getDescription().toStdString(); - str << ")]"; + str << ""; return str.str(); } @@ -65,26 +54,97 @@ Py::String ModelPropertyPy::getName() const return Py::String(getModelPropertyPtr()->getName().toStdString()); } +void ModelPropertyPy::setName(Py::String arg) +{ + getModelPropertyPtr()->setName(QString::fromStdString(arg)); +} + +Py::String ModelPropertyPy::getDisplayName() const +{ + return Py::String(getModelPropertyPtr()->getDisplayName().toStdString()); +} + +void ModelPropertyPy::setDisplayName(Py::String arg) +{ + getModelPropertyPtr()->setDisplayName(QString::fromStdString(arg)); +} + Py::String ModelPropertyPy::getType() const { return Py::String(getModelPropertyPtr()->getPropertyType().toStdString()); } +void ModelPropertyPy::setType(Py::String arg) +{ + getModelPropertyPtr()->setPropertyType(QString::fromStdString(arg)); +} + Py::String ModelPropertyPy::getUnits() const { return Py::String(getModelPropertyPtr()->getUnits().toStdString()); } +void ModelPropertyPy::setUnits(Py::String arg) +{ + getModelPropertyPtr()->setUnits(QString::fromStdString(arg)); +} + Py::String ModelPropertyPy::getURL() const { return Py::String(getModelPropertyPtr()->getURL().toStdString()); } +void ModelPropertyPy::setURL(Py::String arg) +{ + getModelPropertyPtr()->setURL(QString::fromStdString(arg)); +} + Py::String ModelPropertyPy::getDescription() const { return Py::String(getModelPropertyPtr()->getDescription().toStdString()); } +void ModelPropertyPy::setDescription(Py::String arg) +{ + getModelPropertyPtr()->setDescription(QString::fromStdString(arg)); +} + +Py::List ModelPropertyPy::getColumns() const +{ + Py::List list; + + auto columns = getModelPropertyPtr()->getColumns(); + for (auto& column : columns) { + PyObject* modelPropertyPy = new ModelPropertyPy(new ModelProperty(column)); + list.append(Py::Object(modelPropertyPy, true)); + } + + return list; +} + +Py::String ModelPropertyPy::getInheritance() const +{ + return Py::String(getModelPropertyPtr()->getInheritance().toStdString()); +} + +Py::Boolean ModelPropertyPy::getInherited() const +{ + return getModelPropertyPtr()->isInherited(); +} + +PyObject* ModelPropertyPy::addColumn(PyObject* args) +{ + PyObject* object; + if (!PyArg_ParseTuple(args, "O!", &ModelPropertyPy::Type, &object)) { + return nullptr; + } + ModelProperty* property = static_cast(object)->getModelPropertyPtr(); + + getModelPropertyPtr()->addColumn(*property); + Py_INCREF(Py_None); + return Py_None; +} + PyObject* ModelPropertyPy::getCustomAttributes(const char* /*attr*/) const { return nullptr; @@ -93,4 +153,4 @@ PyObject* ModelPropertyPy::getCustomAttributes(const char* /*attr*/) const int ModelPropertyPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) { return 0; -} +} \ No newline at end of file diff --git a/src/Mod/Material/App/ModelPy.xml b/src/Mod/Material/App/ModelPy.xml index 9202307244..3d84a7e172 100644 --- a/src/Mod/Material/App/ModelPy.xml +++ b/src/Mod/Material/App/ModelPy.xml @@ -33,13 +33,19 @@ - + Model name. - + + + Model type. + + + + Model directory. @@ -51,19 +57,19 @@ - + Description of the model. - + URL to a detailed description of the model. - + Digital Object Identifier (see https://doi.org/) @@ -81,5 +87,15 @@ + + + Add an inherited model. + + + + + Add a model property. + + diff --git a/src/Mod/Material/App/ModelPyImp.cpp b/src/Mod/Material/App/ModelPyImp.cpp index 1c8c769b1c..f90abf64bb 100644 --- a/src/Mod/Material/App/ModelPyImp.cpp +++ b/src/Mod/Material/App/ModelPyImp.cpp @@ -34,42 +34,8 @@ using namespace Materials; // returns a string which represents the object e.g. when printed in python std::string ModelPy::representation() const { - ModelPy::PointerType ptr = getModelPtr(); std::stringstream str; - str << "Property [Name=("; - str << ptr->getName().toStdString(); - str << "), UUID=("; - str << ptr->getUUID().toStdString(); - auto library = ptr->getLibrary(); - if (library) { - str << "), Library Name=("; - str << ptr->getLibrary()->getName().toStdString(); - str << "), Library Root=("; - str << ptr->getLibrary()->getDirectoryPath().toStdString(); - str << "), Library Icon=("; - str << ptr->getLibrary()->getIconPath().toStdString(); - } - str << "), Directory=("; - str << ptr->getDirectory().toStdString(); - str << "), URL=("; - str << ptr->getURL().toStdString(); - str << "), DOI=("; - str << ptr->getDOI().toStdString(); - str << "), Description=("; - str << ptr->getDescription().toStdString(); - str << "), Inherits=["; - auto& inherited = getModelPtr()->getInheritance(); - for (auto it = inherited.begin(); it != inherited.end(); it++) { - QString uuid = *it; - if (it != inherited.begin()) { - str << "), UUID=("; - } - else { - str << "UUID=("; - } - str << uuid.toStdString() << ")"; - } - str << "]]"; + str << ""; return str.str(); } @@ -109,9 +75,38 @@ Py::String ModelPy::getName() const return Py::String(getModelPtr()->getName().toStdString()); } +void ModelPy::setName(Py::String arg) +{ + getModelPtr()->setName(QString::fromStdString(arg)); +} + +Py::String ModelPy::getType() const +{ + auto type = (getModelPtr()->getType() == Model::ModelType_Physical) + ? "Physical" + : "Appearance"; + + return Py::String(type); +} + +void ModelPy::setType(Py::String arg) +{ + if (arg.as_std_string() == "Appearance") { + getModelPtr()->setType(Model::ModelType_Appearance); + } + else { + getModelPtr()->setType(Model::ModelType_Physical); + } +} + Py::String ModelPy::getDirectory() const { - return Py::String(getModelPtr()->getDirectoryPath().toStdString()); + return Py::String(getModelPtr()->getDirectory().toStdString()); +} + +void ModelPy::setDirectory(Py::String arg) +{ + getModelPtr()->setDirectory(QString::fromStdString(arg)); } Py::String ModelPy::getUUID() const @@ -124,16 +119,31 @@ Py::String ModelPy::getDescription() const return Py::String(getModelPtr()->getDescription().toStdString()); } +void ModelPy::setDescription(Py::String arg) +{ + getModelPtr()->setDescription(QString::fromStdString(arg)); +} + Py::String ModelPy::getURL() const { return Py::String(getModelPtr()->getURL().toStdString()); } +void ModelPy::setURL(Py::String arg) +{ + getModelPtr()->setURL(QString::fromStdString(arg)); +} + Py::String ModelPy::getDOI() const { return Py::String(getModelPtr()->getDOI().toStdString()); } +void ModelPy::setDOI(Py::String arg) +{ + getModelPtr()->setDOI(QString::fromStdString(arg)); +} + Py::List ModelPy::getInherited() const { auto& inherited = getModelPtr()->getInheritance(); @@ -148,7 +158,6 @@ Py::List ModelPy::getInherited() const Py::Dict ModelPy::getProperties() const { - // std::map *models = getModelPtr()->getModels(); Py::Dict dict; for (auto it = getModelPtr()->begin(); it != getModelPtr()->end(); it++) { @@ -162,6 +171,31 @@ Py::Dict ModelPy::getProperties() const return dict; } +PyObject* ModelPy::addInheritance(PyObject* args) +{ + char* uuid; + if (!PyArg_ParseTuple(args, "s", &uuid)) { + return nullptr; + } + + getModelPtr()->addInheritance(QString::fromStdString(uuid)); + Py_INCREF(Py_None); + return Py_None; +} + +PyObject* ModelPy::addProperty(PyObject* args) +{ + PyObject* object; + if (!PyArg_ParseTuple(args, "O!", &ModelPropertyPy::Type, &object)) { + return nullptr; + } + ModelProperty* property = static_cast(object)->getModelPropertyPtr(); + + getModelPtr()->addProperty(*property); + Py_INCREF(Py_None); + return Py_None; +} + PyObject* ModelPy::getCustomAttributes(const char* /*attr*/) const { return nullptr; diff --git a/src/Mod/Material/App/PropertyMaterial.cpp b/src/Mod/Material/App/PropertyMaterial.cpp index 04d7579235..07f405ef03 100644 --- a/src/Mod/Material/App/PropertyMaterial.cpp +++ b/src/Mod/Material/App/PropertyMaterial.cpp @@ -88,14 +88,12 @@ void PropertyMaterial::Save(Base::Writer& writer) const void PropertyMaterial::Restore(Base::XMLReader& reader) { - MaterialManager manager; - // read my Element reader.readElement("PropertyMaterial"); // get the value of my Attribute auto uuid = reader.getAttribute("uuid"); - setValue(*manager.getMaterial(QString::fromLatin1(uuid))); + setValue(*MaterialManager::getManager().getMaterial(QString::fromLatin1(uuid))); } const char* PropertyMaterial::getEditorName() const diff --git a/src/Mod/Material/App/PyVariants.cpp b/src/Mod/Material/App/PyVariants.cpp new file mode 100644 index 0000000000..c1599e3661 --- /dev/null +++ b/src/Mod/Material/App/PyVariants.cpp @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * 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 "PyVariants.h" +#include "Exceptions.h" + +using namespace Materials; + +PyObject* Materials::_pyObjectFromVariant(const QVariant& value) +{ + if (value.isNull()) { + Py_RETURN_NONE; + } + + if (value.userType() == qMetaTypeId()) { + return new Base::QuantityPy(new Base::Quantity(value.value())); + } + if (value.userType() == QMetaType::Double) { + return PyFloat_FromDouble(value.toDouble()); + } + if (value.userType() == QMetaType::Float) { + return PyFloat_FromDouble(value.toFloat()); + } + if (value.userType() == QMetaType::Int) { + return PyLong_FromLong(value.toInt()); + } + if (value.userType() == QMetaType::Long) { + return PyLong_FromLong(value.toInt()); + } + if (value.userType() == QMetaType::Bool) { + return Py::new_reference_to(Py::Boolean(value.toBool())); + } + if (value.userType() == QMetaType::QString) { + return PyUnicode_FromString(value.toString().toStdString().c_str()); + } + if (value.userType() == qMetaTypeId>()) { + return Py::new_reference_to(getList(value)); + } + + throw UnknownValueType(); +} + +Py::List Materials::getList(const QVariant& value) +{ + auto listValue = value.value>(); + Py::List list; + + for (auto& it : listValue) { + list.append(Py::Object(_pyObjectFromVariant(it))); + } + + return list; +} diff --git a/src/Mod/Material/App/PyVariants.h b/src/Mod/Material/App/PyVariants.h new file mode 100644 index 0000000000..f3ef124213 --- /dev/null +++ b/src/Mod/Material/App/PyVariants.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (c) 2023 David Carter * + * * + * 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 MATERIAL_PYVARIANTS_H +#define MATERIAL_PYVARIANTS_H + +#include + +#include +#include +#include +#include + +#include + +namespace Materials +{ + +extern MaterialsExport PyObject* _pyObjectFromVariant(const QVariant& value); +extern MaterialsExport Py::List getList(const QVariant& value); + +} // namespace Materials + +#endif // MATERIAL_PYVARIANTS_H diff --git a/src/Mod/Material/App/UUIDsPyImp.cpp b/src/Mod/Material/App/UUIDsPyImp.cpp index 8e64b50ec8..4ff55ee7b8 100644 --- a/src/Mod/Material/App/UUIDsPyImp.cpp +++ b/src/Mod/Material/App/UUIDsPyImp.cpp @@ -199,7 +199,6 @@ Py::String UUIDsPy::getTextureRendering() const Py::String UUIDsPy::getAdvancedRendering() const { - Base::Console().Log(getModelUUIDsPtr()->ModelUUID_Rendering_Advanced.toStdString().c_str()); return Py::String(getModelUUIDsPtr()->ModelUUID_Rendering_Advanced.toStdString()); } diff --git a/src/Mod/Material/Gui/Array2D.cpp b/src/Mod/Material/Gui/Array2D.cpp index e2fdfb9860..2d2f6acefc 100644 --- a/src/Mod/Material/Gui/Array2D.cpp +++ b/src/Mod/Material/Gui/Array2D.cpp @@ -63,7 +63,7 @@ Array2D::Array2D(const QString& propertyName, } if (_property) { _value = - std::static_pointer_cast(_property->getMaterialValue()); + std::static_pointer_cast(_property->getMaterialValue()); setWindowTitle(_property->getDisplayName()); } else { diff --git a/src/Mod/Material/Gui/Array2D.h b/src/Mod/Material/Gui/Array2D.h index 3372d85c56..0bf71041b7 100644 --- a/src/Mod/Material/Gui/Array2D.h +++ b/src/Mod/Material/Gui/Array2D.h @@ -64,7 +64,7 @@ private: std::unique_ptr ui; std::shared_ptr _material; std::shared_ptr _property; - std::shared_ptr _value; + std::shared_ptr _value; QAction _deleteAction; diff --git a/src/Mod/Material/Gui/Array3D.cpp b/src/Mod/Material/Gui/Array3D.cpp index 450ca682cc..f91b3f5b83 100644 --- a/src/Mod/Material/Gui/Array3D.cpp +++ b/src/Mod/Material/Gui/Array3D.cpp @@ -59,7 +59,7 @@ Array3D::Array3D(const QString& propertyName, } if (_property) { _value = - std::static_pointer_cast(_property->getMaterialValue()); + std::static_pointer_cast(_property->getMaterialValue()); } else { _value = nullptr; diff --git a/src/Mod/Material/Gui/Array3D.h b/src/Mod/Material/Gui/Array3D.h index 00c9b6d68f..0bc629889f 100644 --- a/src/Mod/Material/Gui/Array3D.h +++ b/src/Mod/Material/Gui/Array3D.h @@ -67,7 +67,7 @@ private: std::unique_ptr ui; std::shared_ptr _material; std::shared_ptr _property; - std::shared_ptr _value; + std::shared_ptr _value; QAction _deleteDepthAction; QAction _delete2DAction; diff --git a/src/Mod/Material/Gui/ArrayModel.cpp b/src/Mod/Material/Gui/ArrayModel.cpp index ce4a00b867..c51fa0062e 100644 --- a/src/Mod/Material/Gui/ArrayModel.cpp +++ b/src/Mod/Material/Gui/ArrayModel.cpp @@ -47,7 +47,7 @@ AbstractArrayModel::AbstractArrayModel(QObject* parent) Array2DModel::Array2DModel(const std::shared_ptr& property, - const std::shared_ptr& value, + const std::shared_ptr& value, QObject* parent) : AbstractArrayModel(parent) , _property(property) @@ -199,7 +199,7 @@ bool Array2DModel::removeColumns(int column, int count, const QModelIndex& paren //=== Array3DDepthModel::Array3DDepthModel(const std::shared_ptr& property, - const std::shared_ptr& value, + const std::shared_ptr& value, QObject* parent) : AbstractArrayModel(parent) , _property(property) @@ -337,7 +337,7 @@ bool Array3DDepthModel::removeColumns(int column, int count, const QModelIndex& //=== Array3DModel::Array3DModel(const std::shared_ptr& property, - const std::shared_ptr& value, + const std::shared_ptr& value, QObject* parent) : AbstractArrayModel(parent) , _property(property) diff --git a/src/Mod/Material/Gui/ArrayModel.h b/src/Mod/Material/Gui/ArrayModel.h index da5bf69fac..f138e4e1bf 100644 --- a/src/Mod/Material/Gui/ArrayModel.h +++ b/src/Mod/Material/Gui/ArrayModel.h @@ -48,7 +48,7 @@ class Array2DModel: public AbstractArrayModel { public: explicit Array2DModel(const std::shared_ptr& property = nullptr, - const std::shared_ptr& value = nullptr, + const std::shared_ptr& value = nullptr, QObject* parent = nullptr); ~Array2DModel() override = default; @@ -71,7 +71,7 @@ public: private: std::shared_ptr _property; - std::shared_ptr _value; + std::shared_ptr _value; }; class Array3DDepthModel: public AbstractArrayModel @@ -79,7 +79,7 @@ class Array3DDepthModel: public AbstractArrayModel public: explicit Array3DDepthModel( const std::shared_ptr& property = nullptr, - const std::shared_ptr& value = nullptr, + const std::shared_ptr& value = nullptr, QObject* parent = nullptr); ~Array3DDepthModel() override = default; @@ -106,14 +106,14 @@ public: private: std::shared_ptr _property; - std::shared_ptr _value; + std::shared_ptr _value; }; class Array3DModel: public AbstractArrayModel { public: explicit Array3DModel(const std::shared_ptr& property = nullptr, - const std::shared_ptr& value = nullptr, + const std::shared_ptr& value = nullptr, QObject* parent = nullptr); ~Array3DModel() override = default; @@ -138,7 +138,7 @@ public: private: std::shared_ptr _property; - std::shared_ptr _value; + std::shared_ptr _value; }; } // namespace MatGui diff --git a/src/Mod/Material/Gui/Command.cpp b/src/Mod/Material/Gui/Command.cpp index fe36ed5102..37d1059cc7 100644 --- a/src/Mod/Material/Gui/Command.cpp +++ b/src/Mod/Material/Gui/Command.cpp @@ -61,8 +61,6 @@ void CmdMaterialEdit::activated(int iMsg) { Q_UNUSED(iMsg); - Base::Console().Log("Material_Edit\n"); - static QPointer dlg = nullptr; if (!dlg) { dlg = new MatGui::MaterialsEditor(Gui::getMainWindow()); diff --git a/src/Mod/Material/Gui/DlgInspectAppearance.cpp b/src/Mod/Material/Gui/DlgInspectAppearance.cpp index 25c36cfa41..2d67cfe676 100644 --- a/src/Mod/Material/Gui/DlgInspectAppearance.cpp +++ b/src/Mod/Material/Gui/DlgInspectAppearance.cpp @@ -151,7 +151,7 @@ void DlgInspectAppearance::update(std::vector& views) ui->editObjectLabel->setText(QString::fromUtf8(labelProp->getValue())); } else { - ui->editObjectLabel->setText(QLatin1String("")); + ui->editObjectLabel->setText(QStringLiteral("")); } ui->editObjectName->setText(QLatin1String(obj->getNameInDocument())); @@ -162,15 +162,15 @@ void DlgInspectAppearance::update(std::vector& views) ui->editSubShape->setText(QString::fromStdString(subObject.getSubNames()[0])); } else { - ui->editSubShape->setText(QLatin1String("")); + ui->editSubShape->setText(QStringLiteral("")); } } else { - ui->editSubShape->setText(QLatin1String("")); + ui->editSubShape->setText(QStringLiteral("")); } auto subShapeType = QString::fromUtf8(obj->getTypeId().getName()); - subShapeType.remove(subShapeType.indexOf(QLatin1String("::")), subShapeType.size()); + subShapeType.remove(subShapeType.indexOf(QStringLiteral("::")), subShapeType.size()); ui->editSubShapeType->setText(subShapeType); ui->editShapeType->setText(QString::fromUtf8(obj->getTypeId().getName())); diff --git a/src/Mod/Material/Gui/DlgInspectMaterial.cpp b/src/Mod/Material/Gui/DlgInspectMaterial.cpp index c599b7f75b..2c6fe1a0bc 100644 --- a/src/Mod/Material/Gui/DlgInspectMaterial.cpp +++ b/src/Mod/Material/Gui/DlgInspectMaterial.cpp @@ -120,7 +120,7 @@ void DlgInspectMaterial::appendClip(QString text) { // Need to add indent QString indent(clipboardIndent * 4, QLatin1Char(' ')); - clipboardText += indent + text + QLatin1String("\n"); + clipboardText += indent + text + QStringLiteral("\n"); } QStandardItem* DlgInspectMaterial::clipItem(QString text) @@ -144,7 +144,7 @@ void DlgInspectMaterial::unindent() void DlgInspectMaterial::update(std::vector& views) { - clipboardText = QLatin1String(""); + clipboardText = QStringLiteral(""); clipboardIndent = 0; App::Document* doc = App::GetApplication().getActiveDocument(); if (doc) { @@ -166,7 +166,7 @@ void DlgInspectMaterial::update(std::vector& views) appendClip(tr("Label: ") + QString::fromUtf8(labelProp->getValue())); } else { - ui->editObjectLabel->setText(QLatin1String("")); + ui->editObjectLabel->setText(QStringLiteral("")); } ui->editObjectName->setText(QLatin1String(obj->getNameInDocument())); appendClip(tr("Internal Name: ") + QString::fromUtf8(obj->getNameInDocument())); @@ -178,15 +178,15 @@ void DlgInspectMaterial::update(std::vector& views) ui->editSubShape->setText(QString::fromStdString(subObject.getSubNames()[0])); } else { - ui->editSubShape->setText(QLatin1String("")); + ui->editSubShape->setText(QStringLiteral("")); } } else { - ui->editSubShape->setText(QLatin1String("")); + ui->editSubShape->setText(QStringLiteral("")); } auto subShapeType = QString::fromUtf8(obj->getTypeId().getName()); - subShapeType.remove(subShapeType.indexOf(QLatin1String("::")), subShapeType.size()); + subShapeType.remove(subShapeType.indexOf(QStringLiteral("::")), subShapeType.size()); appendClip(tr("Type: ") + subShapeType); ui->editSubShapeType->setText(subShapeType); appendClip(tr("TypeID: ") + QString::fromUtf8(obj->getTypeId().getName())); @@ -248,7 +248,7 @@ void DlgInspectMaterial::addModels(QTreeView* tree, } else { for (const QString& uuid : *models) { - auto model = modelManager.getModel(uuid); + auto model = Materials::ModelManager::getManager().getModel(uuid); auto name = clipItem(tr("Name: ") + model->getName()); addExpanded(tree, parent, name); @@ -287,7 +287,7 @@ void DlgInspectMaterial::addModelDetails(QTreeView* tree, } else { for (const QString& inherited : inheritedUuids) { - auto inheritedModel = modelManager.getModel(inherited); + auto inheritedModel = Materials::ModelManager::getManager().getModel(inherited); auto name = clipItem(tr("Name: ") + inheritedModel->getName()); addExpanded(tree, inherits, name); @@ -340,10 +340,10 @@ void DlgInspectMaterial::addMaterialDetails(QTreeView* tree, { auto uuid = clipItem(tr("UUID: ") + material.getUUID()); addExpanded(tree, parent, uuid); - auto library = clipItem(tr("Library: ") + material.getLibrary()->getName()); + auto library = + clipItem(tr("Library: ") + material.getLibrary()->getName()); addExpanded(tree, parent, library); - auto libraryPath = - clipItem(tr("Library Directory: ") + material.getLibrary()->getDirectoryPath()); + auto libraryPath = clipItem(tr("Library Directory: ") + material.getLibrary()->getDirectoryPath()); addExpanded(tree, parent, libraryPath); auto directory = clipItem(tr("Sub Directory: ") + material.getDirectory()); addExpanded(tree, parent, directory); @@ -353,7 +353,7 @@ void DlgInspectMaterial::addMaterialDetails(QTreeView* tree, indent(); auto parentUUID = material.getParentUUID(); if (!parentUUID.isEmpty()) { - auto parentMaterial = materialManager.getMaterial(material.getParentUUID()); + auto parentMaterial = Materials::MaterialManager::getManager().getMaterial(material.getParentUUID()); addMaterial(tree, inherits, *parentMaterial); } else { diff --git a/src/Mod/Material/Gui/DlgInspectMaterial.h b/src/Mod/Material/Gui/DlgInspectMaterial.h index 710212a2a4..2ad4a4e026 100644 --- a/src/Mod/Material/Gui/DlgInspectMaterial.h +++ b/src/Mod/Material/Gui/DlgInspectMaterial.h @@ -60,8 +60,6 @@ public: private: std::unique_ptr ui; - Materials::MaterialManager materialManager; - Materials::ModelManager modelManager; QString clipboardText; int clipboardIndent; diff --git a/src/Mod/Material/Gui/MaterialSave.cpp b/src/Mod/Material/Gui/MaterialSave.cpp index fd0ebb729a..a0c2beca4a 100644 --- a/src/Mod/Material/Gui/MaterialSave.cpp +++ b/src/Mod/Material/Gui/MaterialSave.cpp @@ -137,22 +137,23 @@ void MaterialSave::onOk(bool checked) QFileInfo filepath(_selectedPath + QStringLiteral("/") + name + QStringLiteral(".FCMat")); - if (library->fileExists(filepath.filePath())) { + /*if (library->fileExists(filepath.filePath()))*/ { // confirm overwrite auto res = confirmOverwrite(_filename); if (res == QMessageBox::Cancel) { return; } - _manager.saveMaterial(library, _material, filepath.filePath(), true, false, _saveInherited); + Materials::MaterialManager::getManager() + .saveMaterial(library, _material, filepath.filePath(), true, false, _saveInherited); accept(); return; } bool saveAsCopy = false; - if (_manager.exists(_material->getUUID())) { + if (Materials::MaterialManager::getManager().exists(_material->getUUID())) { // Does it already exist in this library? - if (_manager.exists(library, _material->getUUID())) { + if (Materials::MaterialManager::getManager().exists(library, _material->getUUID())) { // Confirm saving a new material auto res = confirmNewMaterial(); if (res == QMessageBox::Cancel) { @@ -174,7 +175,7 @@ void MaterialSave::onOk(bool checked) } } - _manager + Materials::MaterialManager::getManager() .saveMaterial(library, _material, filepath.filePath(), false, saveAsCopy, _saveInherited); accept(); @@ -287,12 +288,16 @@ void MaterialSave::reject() void MaterialSave::setLibraries() { - auto libraries = _manager.getMaterialLibraries(); + auto libraries = Materials::MaterialManager::getManager().getLibraries(); for (auto& library : *libraries) { - if (!library->isReadOnly()) { - QVariant libraryVariant; - libraryVariant.setValue(library); - ui->comboLibrary->addItem(library->getName(), libraryVariant); + if (library->isLocal()) { + auto materialLibrary = + reinterpret_cast&>(library); + if (!materialLibrary->isReadOnly()) { + QVariant libraryVariant; + libraryVariant.setValue(materialLibrary); + ui->comboLibrary->addItem(materialLibrary->getName(), libraryVariant); + } } } } @@ -327,7 +332,7 @@ void MaterialSave::addMaterials( auto tree = ui->treeMaterials; for (auto& mat : *modelTree) { std::shared_ptr nodePtr = mat.second; - if (nodePtr->getType() == Materials::MaterialTreeNode::DataNode) { + if (nodePtr->getType() == Materials::MaterialTreeNode::NodeType::DataNode) { std::shared_ptr material = nodePtr->getData(); QString uuid = material->getUUID(); @@ -368,7 +373,7 @@ void MaterialSave::showSelectedTree() lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); addExpanded(tree, model, lib); - auto modelTree = _manager.getMaterialTree(library); + auto modelTree = Materials::MaterialManager::getManager().getMaterialTree(library); addMaterials(*lib, modelTree, folderIcon, icon); } else { @@ -444,14 +449,14 @@ void MaterialSave::createFolder(const QString& path) { auto library = currentLibrary(); - _manager.createFolder(library, path); + Materials::MaterialManager::getManager().createFolder(library, path); } void MaterialSave::renameFolder(const QString& oldPath, const QString& newPath) { auto library = currentLibrary(); - _manager.renameFolder(library, oldPath, newPath); + Materials::MaterialManager::getManager().renameFolder(library, oldPath, newPath); } void MaterialSave::deleteRecursive(const QString& path) @@ -459,7 +464,7 @@ void MaterialSave::deleteRecursive(const QString& path) // This will delete files, folders, and any children auto library = currentLibrary(); - _manager.deleteRecursive(library, path); + Materials::MaterialManager::getManager().deleteRecursive(library, path); } void MaterialSave::onNewFolder(bool checked) @@ -552,9 +557,9 @@ int MaterialSave::confirmDelete(QWidget* parent) { auto library = currentLibrary(); - if (library->isRoot(_selectedFull)) { - return QMessageBox::Cancel; - } + // if (library->isRoot(_selectedFull)) { + // return QMessageBox::Cancel; + // } QMessageBox box(parent ? parent : this); box.setIcon(QMessageBox::Question); @@ -600,10 +605,10 @@ void MaterialSave::deleteSelected() { auto library = currentLibrary(); - if (!library->isRoot(_selectedFull)) { - _manager.deleteRecursive(library, _selectedFull); - removeSelectedFromTree(); - } + // if (!library->isRoot(_selectedFull)) { + // Materials::MaterialManager::getManager().deleteRecursive(library, _selectedFull); + // removeSelectedFromTree(); + // } } void MaterialSave::removeChildren(QStandardItem* item) diff --git a/src/Mod/Material/Gui/MaterialSave.h b/src/Mod/Material/Gui/MaterialSave.h index 9d4028a3b4..7e794f4784 100644 --- a/src/Mod/Material/Gui/MaterialSave.h +++ b/src/Mod/Material/Gui/MaterialSave.h @@ -35,6 +35,8 @@ namespace MatGui { +class MaterialLibrary; + class Ui_MaterialSave; class MaterialSave: public QDialog @@ -76,7 +78,6 @@ public: private: std::unique_ptr ui; - Materials::MaterialManager _manager; std::shared_ptr _material; bool _saveInherited; QString _selectedPath; diff --git a/src/Mod/Material/Gui/MaterialTreeWidget.cpp b/src/Mod/Material/Gui/MaterialTreeWidget.cpp index 872547ac2d..99df9e84a6 100644 --- a/src/Mod/Material/Gui/MaterialTreeWidget.cpp +++ b/src/Mod/Material/Gui/MaterialTreeWidget.cpp @@ -487,7 +487,7 @@ void MaterialTreeWidget::addRecent(const QString& uuid) } // Ensure it is a material. New, unsaved materials will not be try { - auto material = _materialManager.getMaterial(uuid); + auto material = Materials::MaterialManager::getManager().getMaterial(uuid); Q_UNUSED(material) } catch (const Materials::MaterialNotFound&) { @@ -555,12 +555,15 @@ void MaterialTreeWidget::fillMaterialTree() addRecents(lib); } - auto libraries = _materialManager.getMaterialLibraries(); + auto libraries = Materials::MaterialManager::getManager().getLibraries(); for (const auto& library : *libraries) { - auto modelTree = _materialManager.getMaterialTree(library, _filter, _filterOptions); + auto materialTree = + Materials::MaterialManager::getManager().getMaterialTree(library, + _filter, + _filterOptions); bool showLibraries = _filterOptions.includeEmptyLibraries(); - if (!_filterOptions.includeEmptyLibraries() && modelTree->size() > 0) { + if (!_filterOptions.includeEmptyLibraries() && materialTree->size() > 0) { showLibraries = true; } @@ -572,7 +575,7 @@ void MaterialTreeWidget::fillMaterialTree() QIcon icon(library->getIconPath()); QIcon folderIcon(QStringLiteral(":/icons/folder.svg")); - addMaterials(*lib, modelTree, folderIcon, icon, param); + addMaterials(*lib, materialTree, folderIcon, icon, param); } } } @@ -616,8 +619,7 @@ void MaterialTreeWidget::addRecents(QStandardItem* parent) for (auto& uuid : _recents) { try { auto material = getMaterialManager().getMaterial(uuid); - - QIcon icon = QIcon(material->getLibrary()->getIconPath()); + QIcon icon(material->getLibrary()->getIconPath()); auto card = new QStandardItem(icon, material->getName()); card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); card->setData(QVariant(uuid), Qt::UserRole); @@ -634,8 +636,7 @@ void MaterialTreeWidget::addFavorites(QStandardItem* parent) for (auto& uuid : _favorites) { try { auto material = getMaterialManager().getMaterial(uuid); - - QIcon icon = QIcon(material->getLibrary()->getIconPath()); + QIcon icon(material->getLibrary()->getIconPath()); auto card = new QStandardItem(icon, material->getName()); card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); card->setData(QVariant(uuid), Qt::UserRole); @@ -657,9 +658,8 @@ void MaterialTreeWidget::addMaterials( auto childParam = param->GetGroup(parent.text().toStdString().c_str()); for (auto& mat : *modelTree) { auto nodePtr = mat.second; - if (nodePtr->getType() == Materials::MaterialTreeNode::DataNode) { - auto material = nodePtr->getData(); - QString uuid = material->getUUID(); + if (nodePtr->getType() == Materials::MaterialTreeNode::NodeType::DataNode) { + QString uuid = nodePtr->getUUID(); auto card = new QStandardItem(icon, mat.first); card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); diff --git a/src/Mod/Material/Gui/MaterialTreeWidget.h b/src/Mod/Material/Gui/MaterialTreeWidget.h index efdbf0e41e..4b58759c3c 100644 --- a/src/Mod/Material/Gui/MaterialTreeWidget.h +++ b/src/Mod/Material/Gui/MaterialTreeWidget.h @@ -48,7 +48,6 @@ namespace MatGui { -class CommandManager; class WidgetFactoryInst; class MaterialTreeWidgetPy; @@ -207,17 +206,13 @@ private: int _recentMax; MaterialTreeWidgetPy* pyTreeWidget {nullptr}; - Materials::MaterialManager _materialManager; - // friends friend class Gui::WidgetFactoryInst; protected: - // bool m_Restored = false; - Materials::MaterialManager& getMaterialManager() { - return _materialManager; + return Materials::MaterialManager::getManager(); } void getFavorites(); diff --git a/src/Mod/Material/Gui/MaterialsEditor.cpp b/src/Mod/Material/Gui/MaterialsEditor.cpp index d4d714c565..16530b487e 100644 --- a/src/Mod/Material/Gui/MaterialsEditor.cpp +++ b/src/Mod/Material/Gui/MaterialsEditor.cpp @@ -46,6 +46,7 @@ #include #include +#include #include #include @@ -89,7 +90,7 @@ void MaterialsEditor::setup() Gui::WaitCursor wc; ui->setupUi(this); - _warningIcon = QIcon(QLatin1String(":/icons/Warning.svg")); + _warningIcon = QIcon(QStringLiteral(":/icons/Warning.svg")); getFavorites(); getRecents(); @@ -108,7 +109,7 @@ void MaterialsEditor::setup() resize(width, height); - ui->buttonURL->setIcon(QIcon(QLatin1String(":/icons/internet-web-browser.svg"))); + ui->buttonURL->setIcon(QIcon(QStringLiteral(":/icons/internet-web-browser.svg"))); connect(ui->standardButtons->button(QDialogButtonBox::Ok), &QPushButton::clicked, @@ -214,7 +215,7 @@ void MaterialsEditor::addFavorite(const QString& uuid) { // Ensure it is a material. New, unsaved materials will not be try { - auto material = _materialManager.getMaterial(uuid); + auto material = Materials::MaterialManager::getManager().getMaterial(uuid); Q_UNUSED(material) } catch (const Materials::MaterialNotFound&) { @@ -299,7 +300,7 @@ void MaterialsEditor::addRecent(const QString& uuid) { // Ensure it is a material. New, unsaved materials will not be try { - auto material = _materialManager.getMaterial(uuid); + auto material = Materials::MaterialManager::getManager().getMaterial(uuid); Q_UNUSED(material) } catch (const Materials::MaterialNotFound&) { @@ -429,7 +430,7 @@ void MaterialsEditor::onAppearanceAdd(bool checked) if (dialog.exec() == QDialog::Accepted) { QString selected = dialog.selectedModel(); _material->addAppearance(selected); - auto model = getModelManager().getModel(selected); + auto model = Materials::ModelManager::getManager().getModel(selected); if (selected == Materials::ModelUUIDs::ModelUUID_Rendering_Basic || model->inherits(Materials::ModelUUIDs::ModelUUID_Rendering_Basic)) { // Add default appearance properties @@ -497,7 +498,7 @@ void MaterialsEditor::setMaterialDefaults() _material->setLicense(QLatin1String(name)); // Empty materials will have no parent - _materialManager.dereference(_material); + Materials::MaterialManager::getManager().dereference(_material); updateMaterial(); _material->resetEditState(); @@ -665,18 +666,22 @@ void MaterialsEditor::saveMaterialTree(const Base::Reference& para void MaterialsEditor::addMaterials( QStandardItem& parent, const std::shared_ptr>> - modelTree, + materialTree, const QIcon& folderIcon, const QIcon& icon, const Base::Reference& param) { auto childParam = param->GetGroup(parent.text().toStdString().c_str()); auto tree = ui->treeMaterials; - for (auto& mat : *modelTree) { + for (auto& mat : *materialTree) { std::shared_ptr nodePtr = mat.second; - if (nodePtr->getType() == Materials::MaterialTreeNode::DataNode) { + if (nodePtr->getType() == Materials::MaterialTreeNode::NodeType::DataNode) { + QString uuid = nodePtr->getUUID(); auto material = nodePtr->getData(); - QString uuid = material->getUUID(); + if (!material) { + material = Materials::MaterialManager::getManager().getMaterial(uuid); + nodePtr->setData(material); + } QIcon matIcon = icon; if (material->isOldFormat()) { @@ -697,7 +702,9 @@ void MaterialsEditor::addMaterials( addExpanded(tree, &parent, node, childParam); node->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); auto treeMap = nodePtr->getFolder(); - addMaterials(*node, treeMap, folderIcon, icon, childParam); + // if (treeMap) { + addMaterials(*node, treeMap, folderIcon, icon, childParam); + // } } } } @@ -800,14 +807,15 @@ void MaterialsEditor::addRecents(QStandardItem* parent) for (auto& uuid : _recents) { try { auto material = getMaterialManager().getMaterial(uuid); + // if (material->getLibrary()->isLocal()) { + QIcon icon = QIcon(material->getLibrary()->getIconPath()); + auto card = new QStandardItem(icon, libraryPath(material)); + card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled + | Qt::ItemIsDropEnabled); + card->setData(QVariant(uuid), Qt::UserRole); - QIcon icon = QIcon(material->getLibrary()->getIconPath()); - auto card = new QStandardItem(icon, libraryPath(material)); - card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled - | Qt::ItemIsDropEnabled); - card->setData(QVariant(uuid), Qt::UserRole); - - addExpanded(tree, parent, card); + addExpanded(tree, parent, card); + // } } catch (const Materials::MaterialNotFound&) { } @@ -820,11 +828,10 @@ void MaterialsEditor::addFavorites(QStandardItem* parent) for (auto& uuid : _favorites) { try { auto material = getMaterialManager().getMaterial(uuid); - QIcon icon = QIcon(material->getLibrary()->getIconPath()); auto card = new QStandardItem(icon, libraryPath(material)); card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled - | Qt::ItemIsDropEnabled); + | Qt::ItemIsDropEnabled); card->setData(QVariant(uuid), Qt::UserRole); addExpanded(tree, parent, card); @@ -856,12 +863,12 @@ void MaterialsEditor::fillMaterialTree() addRecents(lib); } - auto libraries = getMaterialManager().getMaterialLibraries(); + auto libraries = getMaterialManager().getLibraries(); for (const auto& library : *libraries) { - auto modelTree = getMaterialManager().getMaterialTree(library); + auto materialTree = getMaterialManager().getMaterialTree(library); bool showLibraries = _filterOptions.includeEmptyLibraries(); - if (!_filterOptions.includeEmptyLibraries() && modelTree->size() > 0) { + if (!_filterOptions.includeEmptyLibraries() && materialTree->size() > 0) { showLibraries = true; } @@ -873,7 +880,7 @@ void MaterialsEditor::fillMaterialTree() QIcon icon(library->getIconPath()); QIcon folderIcon(QStringLiteral(":/icons/folder.svg")); - addMaterials(*lib, modelTree, folderIcon, icon, param); + addMaterials(*lib, materialTree, folderIcon, icon, param); } } } @@ -901,11 +908,11 @@ bool MaterialsEditor::updateTexturePreview() const { bool hasImage = false; QImage image; - //double scaling = 99.0; + // double scaling = 99.0; if (_material->hasModel(Materials::ModelUUIDs::ModelUUID_Rendering_Texture)) { // First try loading an embedded image try { - auto property = _material->getAppearanceProperty(QLatin1String("TextureImage")); + auto property = _material->getAppearanceProperty(QStringLiteral("TextureImage")); if (!property->isNull()) { // Base::Console().Log("Has 'TextureImage'\n"); auto propertyValue = property->getString(); @@ -922,7 +929,7 @@ bool MaterialsEditor::updateTexturePreview() const // If no embedded image, load from a path if (!hasImage) { try { - auto property = _material->getAppearanceProperty(QLatin1String("TexturePath")); + auto property = _material->getAppearanceProperty(QStringLiteral("TexturePath")); if (!property->isNull()) { // Base::Console().Log("Has 'TexturePath'\n"); auto filePath = property->getString(); @@ -940,10 +947,10 @@ bool MaterialsEditor::updateTexturePreview() const // Apply any scaling try { - auto property = _material->getAppearanceProperty(QLatin1String("TextureScaling")); + auto property = _material->getAppearanceProperty(QStringLiteral("TextureScaling")); if (!property->isNull()) { - //scaling = property->getFloat(); - // Base::Console().Log("Has 'TextureScaling' = %g\n", scaling); + // scaling = property->getFloat(); + // Base::Console().Log("Has 'TextureScaling' = %g\n", scaling); } } catch (const Materials::PropertyNotFound&) { @@ -959,43 +966,43 @@ bool MaterialsEditor::updateTexturePreview() const bool MaterialsEditor::updateMaterialPreview() const { - if (_material->hasAppearanceProperty(QLatin1String("AmbientColor"))) { - QString color = _material->getAppearanceValueString(QLatin1String("AmbientColor")); + if (_material->hasAppearanceProperty(QStringLiteral("AmbientColor"))) { + QString color = _material->getAppearanceValueString(QStringLiteral("AmbientColor")); _rendered->setAmbientColor(getColorHash(color, 255)); } else { _rendered->resetAmbientColor(); } - if (_material->hasAppearanceProperty(QLatin1String("DiffuseColor"))) { - QString color = _material->getAppearanceValueString(QLatin1String("DiffuseColor")); + if (_material->hasAppearanceProperty(QStringLiteral("DiffuseColor"))) { + QString color = _material->getAppearanceValueString(QStringLiteral("DiffuseColor")); _rendered->setDiffuseColor(getColorHash(color, 255)); } else { _rendered->resetDiffuseColor(); } - if (_material->hasAppearanceProperty(QLatin1String("SpecularColor"))) { - QString color = _material->getAppearanceValueString(QLatin1String("SpecularColor")); + if (_material->hasAppearanceProperty(QStringLiteral("SpecularColor"))) { + QString color = _material->getAppearanceValueString(QStringLiteral("SpecularColor")); _rendered->setSpecularColor(getColorHash(color, 255)); } else { _rendered->resetSpecularColor(); } - if (_material->hasAppearanceProperty(QLatin1String("EmissiveColor"))) { - QString color = _material->getAppearanceValueString(QLatin1String("EmissiveColor")); + if (_material->hasAppearanceProperty(QStringLiteral("EmissiveColor"))) { + QString color = _material->getAppearanceValueString(QStringLiteral("EmissiveColor")); _rendered->setEmissiveColor(getColorHash(color, 255)); } else { _rendered->resetEmissiveColor(); } - if (_material->hasAppearanceProperty(QLatin1String("Shininess"))) { - double value = _material->getAppearanceValue(QLatin1String("Shininess")).toDouble(); + if (_material->hasAppearanceProperty(QStringLiteral("Shininess"))) { + double value = _material->getAppearanceValue(QStringLiteral("Shininess")).toDouble(); _rendered->setShininess(value); } else { _rendered->resetShininess(); } - if (_material->hasAppearanceProperty(QLatin1String("Transparency"))) { - double value = _material->getAppearanceValue(QLatin1String("Transparency")).toDouble(); + if (_material->hasAppearanceProperty(QStringLiteral("Transparency"))) { + double value = _material->getAppearanceValue(QStringLiteral("Transparency")).toDouble(); _rendered->setTransparency(value); } else { @@ -1065,7 +1072,7 @@ void MaterialsEditor::updateMaterialAppearance() for (auto it = models->begin(); it != models->end(); it++) { QString uuid = *it; try { - auto model = getModelManager().getModel(uuid); + auto model = Materials::ModelManager::getManager().getModel(uuid); QString name = model->getName(); auto modelRoot = new QStandardItem(name); @@ -1129,7 +1136,7 @@ void MaterialsEditor::updateMaterialProperties() for (auto it = models->begin(); it != models->end(); it++) { QString uuid = *it; try { - auto model = getModelManager().getModel(uuid); + auto model = Materials::ModelManager::getManager().getModel(uuid); QString name = model->getName(); auto modelRoot = new QStandardItem(name); @@ -1177,14 +1184,14 @@ QString MaterialsEditor::libraryPath(const std::shared_ptr& QString path; auto library = material->getLibrary(); if (library) { - path = QStringLiteral("/%1/%2") - .arg(material->getLibrary()->getName()) - .arg(material->getDirectory()); - } - else { - path = QStringLiteral("%1").arg(material->getDirectory()); + path = QStringLiteral("/%1/%2/%3") + .arg(library->getName()) + .arg(material->getDirectory()) + .arg(material->getName()); + return path; } + path = QStringLiteral("%1/%2").arg(material->getDirectory()).arg(material->getName()); return path; } @@ -1192,7 +1199,7 @@ void MaterialsEditor::updateMaterialGeneral() { QString parentString; try { - auto parent = _materialManager.getParent(_material); + auto parent = Materials::MaterialManager::getManager().getParent(_material); parentString = libraryPath(parent); } catch (const Materials::MaterialNotFound&) { diff --git a/src/Mod/Material/Gui/MaterialsEditor.h b/src/Mod/Material/Gui/MaterialsEditor.h index 9d27f86b9b..0ad9e9e5b7 100644 --- a/src/Mod/Material/Gui/MaterialsEditor.h +++ b/src/Mod/Material/Gui/MaterialsEditor.h @@ -82,11 +82,7 @@ public: Materials::MaterialManager& getMaterialManager() { - return _materialManager; - } - Materials::ModelManager& getModelManager() - { - return _modelManager; + return Materials::MaterialManager::getManager(); } static QString libraryPath(const std::shared_ptr& material); @@ -114,8 +110,6 @@ protected: private: std::unique_ptr ui; - Materials::MaterialManager _materialManager; - Materials::ModelManager _modelManager; std::shared_ptr _material; AppearancePreview* _rendered; bool _materialSelected; diff --git a/src/Mod/Material/Gui/ModelSelect.cpp b/src/Mod/Material/Gui/ModelSelect.cpp index 464c112f02..5151b22614 100644 --- a/src/Mod/Material/Gui/ModelSelect.cpp +++ b/src/Mod/Material/Gui/ModelSelect.cpp @@ -239,7 +239,7 @@ void ModelSelect::addModels( auto tree = ui->treeModels; for (auto& mod : *modelTree) { std::shared_ptr nodePtr = mod.second; - if (nodePtr->getType() == Materials::ModelTreeNode::DataNode) { + if (nodePtr->getType() == Materials::ModelTreeNode::NodeType::DataNode) { auto model = nodePtr->getData(); QString uuid = model->getUUID(); @@ -265,9 +265,9 @@ void ModelSelect::addRecents(QStandardItem* parent) auto tree = ui->treeModels; for (auto& uuid : _recents) { try { - auto model = getModelManager().getModel(uuid); + auto model = Materials::ModelManager::getManager().getModel(uuid); - if (getModelManager().passFilter(_filter, model->getType())) { + if (Materials::ModelManager::getManager().passFilter(_filter, model->getType())) { QIcon icon = QIcon(model->getLibrary()->getIconPath()); auto card = new QStandardItem(icon, model->getName()); card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled @@ -287,9 +287,9 @@ void ModelSelect::addFavorites(QStandardItem* parent) auto tree = ui->treeModels; for (auto& uuid : _favorites) { try { - auto model = getModelManager().getModel(uuid); + auto model = Materials::ModelManager::getManager().getModel(uuid); - if (getModelManager().passFilter(_filter, model->getType())) { + if (Materials::ModelManager::getManager().passFilter(_filter, model->getType())) { QIcon icon = QIcon(model->getLibrary()->getIconPath()); auto card = new QStandardItem(icon, model->getName()); card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled @@ -339,13 +339,13 @@ void ModelSelect::fillTree() addExpanded(tree, model, lib); addRecents(lib); - auto libraries = getModelManager().getModelLibraries(); + auto libraries = Materials::ModelManager::getManager().getLibraries(); for (auto& library : *libraries) { lib = new QStandardItem(library->getName()); lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled); addExpanded(tree, model, lib); - auto modelTree = getModelManager().getModelTree(library, _filter); + auto modelTree = Materials::ModelManager::getManager().getModelTree(library, _filter); addModels(*lib, modelTree, QIcon(library->getIconPath())); } } @@ -426,7 +426,7 @@ void ModelSelect::updateModelProperties(std::shared_ptr model) void ModelSelect::updateMaterialModel(const QString& uuid) { - auto model = getModelManager().getModel(uuid); + auto model = Materials::ModelManager::getManager().getModel(uuid); // Update the general information ui->editName->setText(model->getName()); diff --git a/src/Mod/Material/Gui/ModelSelect.h b/src/Mod/Material/Gui/ModelSelect.h index c316cf5414..c2fd8dba56 100644 --- a/src/Mod/Material/Gui/ModelSelect.h +++ b/src/Mod/Material/Gui/ModelSelect.h @@ -91,14 +91,9 @@ private: void setColumnWidths(QTableView* table); void updateModelProperties(std::shared_ptr model); void createModelProperties(); - Materials::ModelManager& getModelManager() - { - return _modelManager; - } Materials::ModelFilter _filter; std::unique_ptr ui; - Materials::ModelManager _modelManager; QString _selected; std::list _favorites; std::list _recents; diff --git a/src/Mod/Material/materialtests/TestModels.py b/src/Mod/Material/materialtests/TestModels.py index 1091c9f80e..f2c252cf73 100644 --- a/src/Mod/Material/materialtests/TestModels.py +++ b/src/Mod/Material/materialtests/TestModels.py @@ -118,3 +118,220 @@ class ModelTestCases(unittest.TestCase): self.assertIn("URL", dir(prop)) self.assertIn("Units", dir(prop)) self.assertEqual(prop.Name, "Density") + + def testTestModelCompleteness(self): + """ Test that the Test model has been loaded correctly """ + model = self.ModelManager.getModel(self.uuids.TestModel) + self.assertIsNotNone(model) + self.assertEqual(model.Name, "Test Model") + self.assertEqual(model.UUID, "34d0583d-f999-49ba-99e6-aa40bd5c3a6b") + self.assertIn("TestString", model.Properties) + self.assertEqual(len(model.Properties), 17) + prop = model.Properties["TestString"] + self.assertIn("Description", dir(prop)) + self.assertIn("Name", dir(prop)) + self.assertIn("Type", dir(prop)) + self.assertIn("URL", dir(prop)) + self.assertIn("Units", dir(prop)) + self.assertEqual(prop.Name, "TestString") + self.assertEqual(prop.Type, "String") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A String") + prop = model.Properties["TestURL"] + self.assertEqual(prop.Name, "TestURL") + self.assertEqual(prop.Type, "URL") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A URL") + prop = model.Properties["TestList"] + self.assertEqual(prop.Name, "TestList") + self.assertEqual(prop.Type, "List") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A List") + prop = model.Properties["TestFileList"] + self.assertEqual(prop.Name, "TestFileList") + self.assertEqual(prop.Type, "FileList") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A List of file paths") + prop = model.Properties["TestImageList"] + self.assertEqual(prop.Name, "TestImageList") + self.assertEqual(prop.Type, "ImageList") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A List of embedded images") + prop = model.Properties["TestInteger"] + self.assertEqual(prop.Name, "TestInteger") + self.assertEqual(prop.Type, "Integer") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A Integer") + prop = model.Properties["TestFloat"] + self.assertEqual(prop.Name, "TestFloat") + self.assertEqual(prop.Type, "Float") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A Float") + prop = model.Properties["TestBoolean"] + self.assertEqual(prop.Name, "TestBoolean") + self.assertEqual(prop.Type, "Boolean") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A Boolean") + prop = model.Properties["TestColor"] + self.assertEqual(prop.Name, "TestColor") + self.assertEqual(prop.Type, "Color") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A Color") + prop = model.Properties["TestFile"] + self.assertEqual(prop.Name, "TestFile") + self.assertEqual(prop.Type, "File") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A File") + prop = model.Properties["TestSVG"] + self.assertEqual(prop.Name, "TestSVG") + self.assertEqual(prop.Type, "SVG") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "An SVG") + prop = model.Properties["TestImage"] + self.assertEqual(prop.Name, "TestImage") + self.assertEqual(prop.Type, "Image") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "An Image") + prop = model.Properties["TestQuantity"] + self.assertEqual(prop.Name, "TestQuantity") + self.assertEqual(prop.Type, "Quantity") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "kg/m^3") + self.assertEqual(prop.Description, "A Quantity") + prop = model.Properties["TestMultiLineString"] + self.assertEqual(prop.Name, "TestMultiLineString") + self.assertEqual(prop.Type, "MultiLineString") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "A string that spans multiple lines") + + prop = model.Properties["TestArray2D"] + self.assertEqual(prop.Name, "TestArray2D") + self.assertEqual(prop.Type, "2DArray") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "2 Dimensional array showing density with temperature\n") + self.assertEqual(len(prop.Columns), 2) + col = prop.Columns[0] + self.assertIn("Description", dir(col)) + self.assertIn("Name", dir(col)) + self.assertIn("Type", dir(col)) + self.assertIn("URL", dir(col)) + self.assertIn("Units", dir(col)) + self.assertEqual(col.Name, "Temperature") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "") + self.assertEqual(col.Units, "C") + self.assertEqual(col.Description, "Temperature") + col = prop.Columns[1] + self.assertEqual(col.Name, "Density") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "https://en.wikipedia.org/wiki/Density") + self.assertEqual(col.Units, "kg/m^3") + self.assertEqual(col.Description, "Density in [FreeCAD Density unit]") + + prop = model.Properties["TestArray2D3Column"] + self.assertEqual(prop.Name, "TestArray2D3Column") + self.assertEqual(prop.Type, "2DArray") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "2 Dimensional array showing density and initial yield stress with temperature\n") + self.assertEqual(len(prop.Columns), 3) + col = prop.Columns[0] + self.assertEqual(col.Name, "Temperature") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "") + self.assertEqual(col.Units, "C") + self.assertEqual(col.Description, "Temperature") + col = prop.Columns[1] + self.assertEqual(col.Name, "Density") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "https://en.wikipedia.org/wiki/Density") + self.assertEqual(col.Units, "kg/m^3") + self.assertEqual(col.Description, "Density in [FreeCAD Density unit]") + col = prop.Columns[2] + self.assertEqual(col.Name, "InitialYieldStress") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "") + self.assertEqual(col.Units, "kPa") + self.assertEqual(col.Description, "Saturation stress for Voce isotropic hardening [FreeCAD Pressure unit]\n") + + prop = model.Properties["TestArray3D"] + self.assertEqual(prop.Name, "TestArray3D") + self.assertEqual(prop.Type, "3DArray") + self.assertEqual(prop.URL, "") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "3 Dimensional array showing stress and strain as a function of temperature\n") + self.assertEqual(len(prop.Columns), 3) + col = prop.Columns[0] + self.assertEqual(col.Name, "Temperature") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "") + self.assertEqual(col.Units, "C") + self.assertEqual(col.Description, "Temperature") + col = prop.Columns[1] + self.assertEqual(col.Name, "Stress") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "") + self.assertEqual(col.Units, "MPa") + self.assertEqual(col.Description, "Stress") + col = prop.Columns[2] + self.assertEqual(col.Name, "Strain") + self.assertEqual(col.Type, "Quantity") + self.assertEqual(col.URL, "") + self.assertEqual(col.Units, "MPa") + self.assertEqual(col.Description, "Strain") + + def testModelInheritance(self): + """ Test that the inherited models have been loaded correctly """ + model = self.ModelManager.getModel(self.uuids.LinearElastic) + self.assertIsNotNone(model) + self.assertEqual(model.Name, "Linear Elastic") + self.assertEqual(model.UUID, "7b561d1d-fb9b-44f6-9da9-56a4f74d7536") + self.assertIn("Density", model.Properties) + prop = model.Properties["Density"] + self.assertEqual(prop.Name, "Density") + self.assertEqual(prop.Type, "Quantity") + self.assertEqual(prop.URL, "https://en.wikipedia.org/wiki/Density") + self.assertEqual(prop.Units, "kg/m^3") + self.assertEqual(prop.Description, "Density in [FreeCAD Density unit]") + prop = model.Properties["BulkModulus"] + self.assertEqual(prop.Name, "BulkModulus") + self.assertEqual(prop.DisplayName, "Bulk Modulus") + self.assertEqual(prop.Type, "Quantity") + self.assertEqual(prop.URL, "https://en.wikipedia.org/wiki/Bulk_modulus") + self.assertEqual(prop.Units, "kPa") + self.assertEqual(prop.Description, "Bulk modulus in [FreeCAD Pressure unit]") + prop = model.Properties["PoissonRatio"] + self.assertEqual(prop.Name, "PoissonRatio") + self.assertEqual(prop.DisplayName, "Poisson Ratio") + self.assertEqual(prop.Type, "Float") + self.assertEqual(prop.URL, "https://en.wikipedia.org/wiki/Poisson%27s_ratio") + self.assertEqual(prop.Units, "") + self.assertEqual(prop.Description, "Poisson's ratio [unitless]") + prop = model.Properties["ShearModulus"] + self.assertEqual(prop.Name, "ShearModulus") + self.assertEqual(prop.DisplayName, "Shear Modulus") + self.assertEqual(prop.Type, "Quantity") + self.assertEqual(prop.URL, "https://en.wikipedia.org/wiki/Shear_modulus") + self.assertEqual(prop.Units, "kPa") + self.assertEqual(prop.Description, "Shear modulus in [FreeCAD Pressure unit]") + prop = model.Properties["YoungsModulus"] + self.assertEqual(prop.Name, "YoungsModulus") + self.assertEqual(prop.DisplayName, "Young's Modulus") + self.assertEqual(prop.Type, "Quantity") + self.assertEqual(prop.URL, "https://en.wikipedia.org/wiki/Young%27s_modulus") + self.assertEqual(prop.Units, "kPa") + self.assertEqual(prop.Description, "Young's modulus (or E-Module) in [FreeCAD Pressure unit]") \ No newline at end of file diff --git a/tests/src/Mod/Material/App/Model.cpp b/tests/src/Mod/Material/App/Model.cpp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/src/Mod/Material/App/TestMaterialCards.cpp b/tests/src/Mod/Material/App/TestMaterialCards.cpp index 435ba19822..a4c9c4be50 100644 --- a/tests/src/Mod/Material/App/TestMaterialCards.cpp +++ b/tests/src/Mod/Material/App/TestMaterialCards.cpp @@ -57,12 +57,12 @@ protected: QDir libDir(libPath); libDir.removeRecursively(); // Clear old run data libDir.mkdir(libPath); - _library = std::make_shared(QStringLiteral("Testing"), + _library = std::make_shared(QStringLiteral("Testing"), libPath, QStringLiteral(":/icons/preferences-general.svg"), false); - _modelManager = new Materials::ModelManager(); - _materialManager = new Materials::MaterialManager(); + _modelManager = &(Materials::ModelManager::getManager()); + _materialManager = &(Materials::MaterialManager::getManager()); _testMaterialUUID = QStringLiteral("c6c64159-19c1-40b5-859c-10561f20f979"); } @@ -70,7 +70,7 @@ protected: // void TearDown() override {} Materials::ModelManager* _modelManager; Materials::MaterialManager* _materialManager; - std::shared_ptr _library; + std::shared_ptr _library; QString _testMaterialUUID; }; @@ -187,17 +187,17 @@ TEST_F(TestMaterialCards, TestColumns) EXPECT_TRUE(testMaterial->hasPhysicalProperty(QStringLiteral("TestArray2D"))); auto array2d = testMaterial->getPhysicalProperty(QStringLiteral("TestArray2D"))->getMaterialValue(); EXPECT_TRUE(array2d); - EXPECT_EQ(dynamic_cast(*array2d).columns(), 2); + EXPECT_EQ(dynamic_cast(*array2d).columns(), 2); EXPECT_TRUE(testMaterial->hasPhysicalProperty(QStringLiteral("TestArray2D3Column"))); auto array2d3Column = testMaterial->getPhysicalProperty(QStringLiteral("TestArray2D3Column"))->getMaterialValue(); EXPECT_TRUE(array2d3Column); - EXPECT_EQ(dynamic_cast(*array2d3Column).columns(), 3); + EXPECT_EQ(dynamic_cast(*array2d3Column).columns(), 3); EXPECT_TRUE(testMaterial->hasPhysicalProperty(QStringLiteral("TestArray3D"))); auto array3d = testMaterial->getPhysicalProperty(QStringLiteral("TestArray3D"))->getMaterialValue(); EXPECT_TRUE(array3d); - EXPECT_EQ(dynamic_cast(*array3d).columns(), 2); + EXPECT_EQ(dynamic_cast(*array3d).columns(), 2); } // clang-format on diff --git a/tests/src/Mod/Material/App/TestMaterialFilter.cpp b/tests/src/Mod/Material/App/TestMaterialFilter.cpp index f79c12bc96..c84c91d6ef 100644 --- a/tests/src/Mod/Material/App/TestMaterialFilter.cpp +++ b/tests/src/Mod/Material/App/TestMaterialFilter.cpp @@ -52,8 +52,8 @@ protected: } void SetUp() override { - _modelManager = new Materials::ModelManager(); - _materialManager = new Materials::MaterialManager(); + _modelManager = &(Materials::ModelManager::getManager()); + _materialManager = &(Materials::MaterialManager::getManager()); // Use our test files as a custom directory ParameterGrp::handle hGrp = @@ -74,7 +74,7 @@ protected: _materialManager->refresh(); - _library = _materialManager->getLibrary(QLatin1String("Custom")); + _library = _materialManager->getLibrary(QStringLiteral("Custom")); } void TearDown() override { @@ -153,7 +153,7 @@ TEST_F(TestMaterialFilter, TestFilters) ASSERT_EQ(tree->size(), 5); // Create a basic rendering filter - filter->setName(QLatin1String("Basic Appearance")); + filter->setName(QStringLiteral("Basic Appearance")); filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Rendering_Basic); options.setIncludeLegacy(false); @@ -166,7 +166,7 @@ TEST_F(TestMaterialFilter, TestFilters) // Create an advanced rendering filter filter->clear(); - filter->setName(QLatin1String("Advanced Appearance")); + filter->setName(QStringLiteral("Advanced Appearance")); filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Rendering_Advanced); options.setIncludeLegacy(false); @@ -179,7 +179,7 @@ TEST_F(TestMaterialFilter, TestFilters) // Create a Density filter filter->clear(); - filter->setName(QLatin1String("Density")); + filter->setName(QStringLiteral("Density")); filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Mechanical_Density); options.setIncludeLegacy(false); @@ -192,7 +192,7 @@ TEST_F(TestMaterialFilter, TestFilters) // Create a Hardness filter filter->clear(); - filter->setName(QLatin1String("Hardness")); + filter->setName(QStringLiteral("Hardness")); filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Mechanical_Hardness); options.setIncludeLegacy(false); @@ -205,7 +205,7 @@ TEST_F(TestMaterialFilter, TestFilters) // Create a Density and Basic Rendering filter filter->clear(); - filter->setName(QLatin1String("Density and Basic Rendering")); + filter->setName(QStringLiteral("Density and Basic Rendering")); filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Rendering_Basic); filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Mechanical_Density); options.setIncludeLegacy(false); @@ -219,7 +219,7 @@ TEST_F(TestMaterialFilter, TestFilters) // Create a Linear Elastic filter filter->clear(); - filter->setName(QLatin1String("Linear Elastic")); + filter->setName(QStringLiteral("Linear Elastic")); filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Mechanical_LinearElastic); options.setIncludeLegacy(false); @@ -231,7 +231,7 @@ TEST_F(TestMaterialFilter, TestFilters) ASSERT_EQ(tree->size(), 0); filter->clear(); - filter->setName(QLatin1String("Linear Elastic")); + filter->setName(QStringLiteral("Linear Elastic")); filter->addRequired(Materials::ModelUUIDs::ModelUUID_Mechanical_LinearElastic); options.setIncludeLegacy(false); diff --git a/tests/src/Mod/Material/App/TestMaterialProperties.cpp b/tests/src/Mod/Material/App/TestMaterialProperties.cpp index c1f4a2a26e..6fd60c6709 100644 --- a/tests/src/Mod/Material/App/TestMaterialProperties.cpp +++ b/tests/src/Mod/Material/App/TestMaterialProperties.cpp @@ -130,9 +130,9 @@ TEST_F(TestMaterialProperties, TestEmpty) TEST_F(TestMaterialProperties, TestSingle) { - Materials::MaterialProperty prop(modelProp1, QLatin1String("sampleUUID")); + Materials::MaterialProperty prop(modelProp1, QStringLiteral("sampleUUID")); EXPECT_EQ(prop.getType(), Materials::MaterialValue::Quantity); - EXPECT_EQ(prop.getModelUUID(), QLatin1String("sampleUUID")); + EXPECT_EQ(prop.getModelUUID(), QStringLiteral("sampleUUID")); EXPECT_TRUE(prop.isNull()); auto variant = prop.getValue(); EXPECT_TRUE(variant.canConvert()); @@ -146,9 +146,9 @@ TEST_F(TestMaterialProperties, TestSingle) void check2DArray(Materials::MaterialProperty& prop) { EXPECT_EQ(prop.getType(), Materials::MaterialValue::Array2D); - EXPECT_EQ(prop.getModelUUID(), QLatin1String("sampleUUID")); + EXPECT_EQ(prop.getModelUUID(), QStringLiteral("sampleUUID")); EXPECT_TRUE(prop.isNull()); - auto array = std::static_pointer_cast(prop.getMaterialValue()); + auto array = std::static_pointer_cast(prop.getMaterialValue()); EXPECT_EQ(array->rows(), 0); auto variant = prop.getValue(); // Throw an error? EXPECT_FALSE(variant.canConvert()); @@ -162,20 +162,20 @@ void check2DArray(Materials::MaterialProperty& prop) TEST_F(TestMaterialProperties, Test2DArray) { - Materials::MaterialProperty prop(modelProp, QLatin1String("sampleUUID")); + Materials::MaterialProperty prop(modelProp, QStringLiteral("sampleUUID")); check2DArray(prop); } TEST_F(TestMaterialProperties, Test2DArrayCopy) { - Materials::MaterialProperty propBase(modelProp, QLatin1String("sampleUUID")); + Materials::MaterialProperty propBase(modelProp, QStringLiteral("sampleUUID")); Materials::MaterialProperty prop(propBase); check2DArray(prop); } TEST_F(TestMaterialProperties, Test2DArrayAssignment) { - Materials::MaterialProperty propBase(modelProp, QLatin1String("sampleUUID")); + Materials::MaterialProperty propBase(modelProp, QStringLiteral("sampleUUID")); Materials::MaterialProperty prop; prop = propBase; @@ -185,9 +185,9 @@ TEST_F(TestMaterialProperties, Test2DArrayAssignment) void check3DArray(Materials::MaterialProperty& prop) { EXPECT_EQ(prop.getType(), Materials::MaterialValue::Array3D); - EXPECT_EQ(prop.getModelUUID(), QLatin1String("sampleUUID")); + EXPECT_EQ(prop.getModelUUID(), QStringLiteral("sampleUUID")); EXPECT_TRUE(prop.isNull()); - auto array = std::static_pointer_cast(prop.getMaterialValue()); + auto array = std::static_pointer_cast(prop.getMaterialValue()); EXPECT_EQ(array->depth(), 0); auto variant = prop.getValue(); // Throw an error? EXPECT_FALSE(variant.canConvert()); @@ -201,20 +201,20 @@ void check3DArray(Materials::MaterialProperty& prop) TEST_F(TestMaterialProperties, Test3DArray) { - Materials::MaterialProperty prop(model3DProp, QLatin1String("sampleUUID")); + Materials::MaterialProperty prop(model3DProp, QStringLiteral("sampleUUID")); check3DArray(prop); } TEST_F(TestMaterialProperties, Test3DArrayCopy) { - Materials::MaterialProperty propBase(model3DProp, QLatin1String("sampleUUID")); + Materials::MaterialProperty propBase(model3DProp, QStringLiteral("sampleUUID")); Materials::MaterialProperty prop(propBase); check3DArray(prop); } TEST_F(TestMaterialProperties, Test3DArrayAssignment) { - Materials::MaterialProperty propBase(model3DProp, QLatin1String("sampleUUID")); + Materials::MaterialProperty propBase(model3DProp, QStringLiteral("sampleUUID")); Materials::MaterialProperty prop; prop = propBase; diff --git a/tests/src/Mod/Material/App/TestMaterialValue.cpp b/tests/src/Mod/Material/App/TestMaterialValue.cpp index b31fd82d99..1a5d1d12e4 100644 --- a/tests/src/Mod/Material/App/TestMaterialValue.cpp +++ b/tests/src/Mod/Material/App/TestMaterialValue.cpp @@ -172,7 +172,7 @@ TEST_F(TestMaterialValue, TestArray2DType) { EXPECT_THROW(auto mat1 = Materials::MaterialValue(Materials::MaterialValue::Array2D), Materials::InvalidMaterialType); - auto mat2 = Materials::Material2DArray(); + auto mat2 = Materials::Array2D(); EXPECT_EQ(mat2.getType(), Materials::MaterialValue::Array2D); EXPECT_TRUE(mat2.isNull()); EXPECT_EQ(mat2.rows(), 0); @@ -182,7 +182,7 @@ TEST_F(TestMaterialValue, TestArray3DType) { EXPECT_THROW(auto mat1 = Materials::MaterialValue(Materials::MaterialValue::Array3D), Materials::InvalidMaterialType); - auto mat2 = Materials::Material3DArray(); + auto mat2 = Materials::Array3D(); mat2.setColumns(2); EXPECT_EQ(mat2.getType(), Materials::MaterialValue::Array3D); EXPECT_TRUE(mat2.isNull()); diff --git a/tests/src/Mod/Material/App/TestMaterials.cpp b/tests/src/Mod/Material/App/TestMaterials.cpp index b1940c7d2e..e47b8dc59a 100644 --- a/tests/src/Mod/Material/App/TestMaterials.cpp +++ b/tests/src/Mod/Material/App/TestMaterials.cpp @@ -58,8 +58,8 @@ class TestMaterial : public ::testing::Test { void SetUp() override { Base::Interpreter().runString("import Part"); - _modelManager = new Materials::ModelManager(); - _materialManager = new Materials::MaterialManager(); + _modelManager = &(Materials::ModelManager::getManager()); + _materialManager = &(Materials::MaterialManager::getManager()); } // void TearDown() override {} @@ -72,11 +72,11 @@ TEST_F(TestMaterial, TestInstallation) ASSERT_NE(_modelManager, nullptr); // We should have loaded at least the system library - auto libraries = _materialManager->getMaterialLibraries(); + auto libraries = _materialManager->getLibraries(); ASSERT_GT(libraries->size(), 0); // We should have at least one material - auto materials = _materialManager->getMaterials(); + auto materials = _materialManager->getLocalMaterials(); ASSERT_GT(materials->size(), 0); } @@ -365,17 +365,17 @@ TEST_F(TestMaterial, TestColumns) EXPECT_TRUE(testMaterial.hasPhysicalProperty(QStringLiteral("TestArray2D"))); auto array2d = testMaterial.getPhysicalProperty(QStringLiteral("TestArray2D"))->getMaterialValue(); EXPECT_TRUE(array2d); - EXPECT_EQ(dynamic_cast(*array2d).columns(), 2); + EXPECT_EQ(dynamic_cast(*array2d).columns(), 2); EXPECT_TRUE(testMaterial.hasPhysicalProperty(QStringLiteral("TestArray2D3Column"))); auto array2d3Column = testMaterial.getPhysicalProperty(QStringLiteral("TestArray2D3Column"))->getMaterialValue(); EXPECT_TRUE(array2d3Column); - EXPECT_EQ(dynamic_cast(*array2d3Column).columns(), 3); + EXPECT_EQ(dynamic_cast(*array2d3Column).columns(), 3); EXPECT_TRUE(testMaterial.hasPhysicalProperty(QStringLiteral("TestArray3D"))); auto array3d = testMaterial.getPhysicalProperty(QStringLiteral("TestArray3D"))->getMaterialValue(); EXPECT_TRUE(array3d); - EXPECT_EQ(dynamic_cast(*array3d).columns(), 2); + EXPECT_EQ(dynamic_cast(*array3d).columns(), 2); } // clang-format on diff --git a/tests/src/Mod/Material/App/TestModel.cpp b/tests/src/Mod/Material/App/TestModel.cpp index d4db24220a..dde62d476d 100644 --- a/tests/src/Mod/Material/App/TestModel.cpp +++ b/tests/src/Mod/Material/App/TestModel.cpp @@ -46,7 +46,7 @@ class TestModel : public ::testing::Test { } void SetUp() override { - _modelManager = new Materials::ModelManager(); + _modelManager = &(Materials::ModelManager::getManager()); } // void TearDown() override {} @@ -76,7 +76,7 @@ TEST_F(TestModel, TestInstallation) ASSERT_NE(_modelManager, nullptr); // We should have loaded at least the system library - auto libraries = _modelManager->getModelLibraries(); + auto libraries = _modelManager->getLibraries(); ASSERT_GT(libraries->size(), 0); // We should have at least one model From 9e1537c8845aa3457087b0a05b60177023a3475f Mon Sep 17 00:00:00 2001 From: David Carter <38090157+davesrocketshop@users.noreply.github.com> Date: Wed, 12 Mar 2025 11:39:11 -0400 Subject: [PATCH 2/5] Update src/Mod/Material/App/Model.cpp Co-authored-by: Chris Hennes --- src/Mod/Material/App/Model.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Material/App/Model.cpp b/src/Mod/Material/App/Model.cpp index 6de9a2fa89..9c7d0a7bcf 100644 --- a/src/Mod/Material/App/Model.cpp +++ b/src/Mod/Material/App/Model.cpp @@ -139,7 +139,7 @@ void ModelProperty::validate(const ModelProperty& other) const Base::Console().Log("Remote property column count %d\n", other._columns.size()); throw InvalidProperty("Model property column counts don't match"); } - for (int i = 0; i < _columns.size(); i++) { + for (size_t i = 0; i < _columns.size(); i++) { _columns[i].validate(other._columns[i]); } } From 71101dfc6a8d7d2d18098907bcc1c1c3e5666099 Mon Sep 17 00:00:00 2001 From: David Carter <38090157+davesrocketshop@users.noreply.github.com> Date: Wed, 12 Mar 2025 11:40:11 -0400 Subject: [PATCH 3/5] Update src/Mod/Material/App/Library.cpp Co-authored-by: Chris Hennes --- src/Mod/Material/App/Library.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Material/App/Library.cpp b/src/Mod/Material/App/Library.cpp index cb7b31244d..4a1c690f61 100644 --- a/src/Mod/Material/App/Library.cpp +++ b/src/Mod/Material/App/Library.cpp @@ -42,9 +42,9 @@ Library::Library(const QString& libraryName, const QString& icon, bool readOnly) Library::Library(const QString& libraryName, const QString& dir, const QString& icon, bool readOnly) : _name(libraryName) + , _directory(QDir::cleanPath(dir)) , _iconPath(icon) , _readOnly(readOnly) - , _directory(QDir::cleanPath(dir)) {} bool Library::operator==(const Library& library) const From ec1da540c6a192a9e4e0e8eea0e5c36442c4be89 Mon Sep 17 00:00:00 2001 From: David Carter <38090157+davesrocketshop@users.noreply.github.com> Date: Wed, 12 Mar 2025 11:40:36 -0400 Subject: [PATCH 4/5] Update src/Mod/Material/App/Materials.cpp Co-authored-by: Chris Hennes --- src/Mod/Material/App/Materials.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mod/Material/App/Materials.cpp b/src/Mod/Material/App/Materials.cpp index 03b3a9a34d..90ad18b073 100644 --- a/src/Mod/Material/App/Materials.cpp +++ b/src/Mod/Material/App/Materials.cpp @@ -454,7 +454,7 @@ void MaterialProperty::validate(const MaterialProperty& other) const { if (_columns.size() != other._columns.size()) { throw InvalidProperty("Model property column counts don't match"); } - for (int i = 0; i < _columns.size(); i++) { + for (size_t i = 0; i < _columns.size(); i++) { _columns[i].validate(other._columns[i]); } } From dd59cce8e9981c807e21c19b3fef5d6f0e0928d9 Mon Sep 17 00:00:00 2001 From: David Carter Date: Wed, 12 Mar 2025 12:11:51 -0400 Subject: [PATCH 5/5] Linter fixes --- src/Mod/Material/App/FolderTree.h | 4 ++-- src/Mod/Material/App/Library.cpp | 2 -- tests/src/Mod/Material/App/TestMaterials.cpp | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Mod/Material/App/FolderTree.h b/src/Mod/Material/App/FolderTree.h index 8e12302d3e..1482016b9a 100644 --- a/src/Mod/Material/App/FolderTree.h +++ b/src/Mod/Material/App/FolderTree.h @@ -56,7 +56,7 @@ public: _type = type; } - const std::shared_ptr>>> getFolder() const + std::shared_ptr>>> getFolder() const { assert(_type == NodeType::FolderNode); return _folder; @@ -87,7 +87,7 @@ public: setType(NodeType::DataNode); _data = data; } - void setUUID(const QString uuid) + void setUUID(const QString& uuid) { setType(NodeType::DataNode); _uuid = uuid; diff --git a/src/Mod/Material/App/Library.cpp b/src/Mod/Material/App/Library.cpp index 4a1c690f61..57bbd50480 100644 --- a/src/Mod/Material/App/Library.cpp +++ b/src/Mod/Material/App/Library.cpp @@ -97,8 +97,6 @@ bool Library::isRoot(const QString& path) const { QString localPath = getLocalPath(path); QString cleanPath = getLocalPath(QStringLiteral("")); - std::string pLocal = localPath.toStdString(); - std::string pclean = cleanPath.toStdString(); return (cleanPath == localPath); } diff --git a/tests/src/Mod/Material/App/TestMaterials.cpp b/tests/src/Mod/Material/App/TestMaterials.cpp index e47b8dc59a..7c3b31a6cf 100644 --- a/tests/src/Mod/Material/App/TestMaterials.cpp +++ b/tests/src/Mod/Material/App/TestMaterials.cpp @@ -31,8 +31,8 @@ #include #include -#include #include +#include #include #include @@ -97,7 +97,7 @@ TEST_F(TestMaterial, TestMaterialsWithModel) // All LinearElastic models should be in IsotropicLinearElastic since it is inherited EXPECT_LE(materialsLinearElastic->size(), materials->size()); - for (auto itp : *materialsLinearElastic) { + for (auto &itp : *materialsLinearElastic) { auto mat = itp.first; EXPECT_NO_THROW(materials->at(mat)); }