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