Materials: External Modules Part 1

Refactored code to support local and external material sources

This is the first PR in a series to support external modules. External
modules allow materials to be stored in external data sources such as
databases or web services. No new functionality is introduced in this
PR, rather it is a refactoring of code that will allow for changes to
be introduced in future PRs. Minor performance improvements have also
been made in the model and material managers.

The Python API has been enhanced for many data types to allow for
modification within Python.
This commit is contained in:
David Carter
2025-03-07 10:13:56 -05:00
committed by Chris Hennes
parent 3c4977a2d4
commit 00c57a9d08
80 changed files with 4372 additions and 1396 deletions

View File

@@ -29,15 +29,24 @@
#include <App/CleanupProcess.h>
#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

View File

@@ -3,8 +3,8 @@
<PythonExport
Father="BaseClassPy"
Name="Array2DPy"
Twin="Material2DArray"
TwinPointer="Material2DArray"
Twin="Array2D"
TwinPointer="Array2D"
Include="Mod/Material/App/MaterialValue.h"
Namespace="Materials"
FatherInclude="Base/BaseClassPy.h"
@@ -21,13 +21,19 @@
</Documentation>
<Parameter Name="Array" Type="List"/>
</Attribute>
<Attribute Name="Rows" ReadOnly="true">
<Attribute Name="Dimensions" ReadOnly="true">
<Documentation>
<UserDocu>The number of dimensions in the array, in this case 2.</UserDocu>
</Documentation>
<Parameter Name="Dimensions" Type="Int"/>
</Attribute>
<Attribute Name="Rows" ReadOnly="false">
<Documentation>
<UserDocu>The number of rows in the array.</UserDocu>
</Documentation>
<Parameter Name="Rows" Type="Long"/>
</Attribute>
<Attribute Name="Columns" ReadOnly="true">
<Attribute Name="Columns" ReadOnly="false">
<Documentation>
<UserDocu>The number of columns in the array.</UserDocu>
</Documentation>
@@ -43,5 +49,10 @@
<UserDocu>Get the value at the given row and column</UserDocu>
</Documentation>
</Methode>
<Methode Name="setValue" ReadOnly="true">
<Documentation>
<UserDocu>Set the value at the given row and column</UserDocu>
</Documentation>
</Methode>
</PythonExport>
</GenerateModel>

View File

@@ -44,7 +44,7 @@ using namespace Materials;
std::string Array2DPy::representation() const
{
std::stringstream str;
str << "<Array2D object at " << getMaterial2DArrayPtr() << ">";
str << "<Array2D object at " << getArray2DPtr() << ">";
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<Base::Quantity>()));
@@ -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<Base::Quantity>()));
}
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;

View File

@@ -3,8 +3,8 @@
<PythonExport
Father="BaseClassPy"
Name="Array3DPy"
Twin="Material3DArray"
TwinPointer="Material3DArray"
Twin="Array3D"
TwinPointer="Array3D"
Include="Mod/Material/App/MaterialValue.h"
Namespace="Materials"
FatherInclude="Base/BaseClassPy.h"
@@ -21,17 +21,23 @@
</Documentation>
<Parameter Name="Array" Type="List"/>
</Attribute>
<Attribute Name="Columns" ReadOnly="true">
<Attribute Name="Dimensions" ReadOnly="true">
<Documentation>
<UserDocu>The number of dimensions in the array, in this case 3.</UserDocu>
</Documentation>
<Parameter Name="Dimensions" Type="Int"/>
</Attribute>
<Attribute Name="Columns" ReadOnly="false">
<Documentation>
<UserDocu>The number of columns in the array.</UserDocu>
</Documentation>
<Parameter Name="Columns" Type="Long"/>
<Parameter Name="Columns" Type="Int"/>
</Attribute>
<Attribute Name="Depth" ReadOnly="true">
<Attribute Name="Depth" ReadOnly="false">
<Documentation>
<UserDocu>The depth of the array (3rd dimension).</UserDocu>
</Documentation>
<Parameter Name="Columns" Type="Long"/>
<Parameter Name="Depth" Type="Int"/>
</Attribute>
<Methode Name="getRows" ReadOnly="true">
<Documentation>
@@ -48,5 +54,20 @@
<UserDocu>Get the column value at the given depth</UserDocu>
</Documentation>
</Methode>
<Methode Name="setDepthValue" ReadOnly="true">
<Documentation>
<UserDocu>Set the column value at the given depth</UserDocu>
</Documentation>
</Methode>
<Methode Name="setValue" ReadOnly="true">
<Documentation>
<UserDocu>Set the value at the given depth, row, and column</UserDocu>
</Documentation>
</Methode>
<Methode Name="setRows" ReadOnly="true">
<Documentation>
<UserDocu>Set the number of rows at the given depth</UserDocu>
</Documentation>
</Methode>
</PythonExport>
</GenerateModel>

View File

@@ -44,7 +44,7 @@ using namespace Materials;
std::string Array3DPy::representation() const
{
std::stringstream str;
str << "<Array3D object at " << getMaterial3DArrayPtr() << ">";
str << "<Array3D object at " << getArray3DPtr() << ">";
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;

View File

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

View File

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

View File

@@ -34,18 +34,21 @@ template<class T>
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<std::map<QString, std::shared_ptr<FolderTreeNode<T>>>> getFolder() const
{
assert(_type == NodeType::FolderNode);
return _folder;
}
std::shared_ptr<std::map<QString, std::shared_ptr<FolderTreeNode<T>>>> getFolder()
{
assert(_type == NodeType::FolderNode);
return _folder;
}
std::shared_ptr<T> getData() const
{
assert(_type == NodeType::DataNode);
return _data;
}
QString getUUID() const
{
assert(_type == NodeType::DataNode);
return _uuid;
}
void setFolder(std::shared_ptr<std::map<QString, std::shared_ptr<FolderTreeNode<T>>>> folder)
{
setType(FolderNode);
setType(NodeType::FolderNode);
_folder = folder;
}
void setData(std::shared_ptr<T> data)
{
setType(DataNode);
setType(NodeType::DataNode);
_data = data;
}
void setUUID(const QString uuid)
{
setType(NodeType::DataNode);
_uuid = uuid;
}
private:
NodeType _type;
std::shared_ptr<std::map<QString, std::shared_ptr<FolderTreeNode<T>>>> _folder;
QString _uuid;
std::shared_ptr<T> _data;
};

View File

@@ -0,0 +1,143 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#include <string>
#endif
#include <App/Application.h>
#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;
}

View File

@@ -0,0 +1,114 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_LIBRARY_H
#define MATERIAL_LIBRARY_H
#include <QDir>
#include <QString>
#include <Base/BaseClass.h>
#include <Mod/Material/MaterialGlobal.h>
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

View File

@@ -1022,7 +1022,7 @@ void MaterialConfigLoader::addLegacy(const QMap<QString, QString>& 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<QString, QString>& fcmat,
}
std::shared_ptr<Material>
MaterialConfigLoader::getMaterialFromPath(const std::shared_ptr<MaterialLibrary>& library,
MaterialConfigLoader::getMaterialFromPath(const std::shared_ptr<MaterialLibraryLocal>& 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<MaterialLibrary>
QString sourceReference = value(fcmat, "ReferenceSource", "");
QString sourceURL = value(fcmat, "SourceURL", "");
std::shared_ptr<Material> finalModel = std::make_shared<Material>(library, path, uuid, name);
auto baseLibrary =
reinterpret_cast<const std::shared_ptr<Materials::MaterialLibrary>&>(library);
std::shared_ptr<Material> finalModel =
std::make_shared<Material>(baseLibrary, path, uuid, name);
finalModel->setOldFormat(true);
finalModel->setAuthor(author);

View File

@@ -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<Material>
getMaterialFromPath(const std::shared_ptr<MaterialLibrary>& library, const QString& path);
getMaterialFromPath(const std::shared_ptr<MaterialLibraryLocal>& library, const QString& path);
private:
static QString value(const QMap<QString, QString>& fcmat,

View File

@@ -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>& 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&) {

View File

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

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="BaseClassPy"
Name="MaterialFilterOptionsPy"
Twin="MaterialFilterOptions"
TwinPointer="MaterialFilterOptions"
Include="Mod/Material/App/MaterialFilter.h"
Namespace="Materials"
FatherInclude="Base/BaseClassPy.h"
FatherNamespace="Base"
Constructor="true"
Delete="true">
<Documentation>
<Author Licence="LGPL" Name="DavidCarter" EMail="dcarter@davidcarter.ca" />
<UserDocu>Material filtering options.</UserDocu>
</Documentation>
<Attribute Name="IncludeFavorites" ReadOnly="false">
<Documentation>
<UserDocu>Include materials marked as favorite.</UserDocu>
</Documentation>
<Parameter Name="IncludeFavorites" Type="Boolean"/>
</Attribute>
<Attribute Name="IncludeRecent" ReadOnly="false">
<Documentation>
<UserDocu>Include recently used materials.</UserDocu>
</Documentation>
<Parameter Name="IncludeRecent" Type="Boolean"/>
</Attribute>
<Attribute Name="IncludeEmptyFolders" ReadOnly="false">
<Documentation>
<UserDocu>Include empty folders.</UserDocu>
</Documentation>
<Parameter Name="IncludeEmptyFolders" Type="Boolean"/>
</Attribute>
<Attribute Name="IncludeEmptyLibraries" ReadOnly="false">
<Documentation>
<UserDocu>Include empty libraries.</UserDocu>
</Documentation>
<Parameter Name="IncludeEmptyLibraries" Type="Boolean"/>
</Attribute>
<Attribute Name="IncludeLegacy" ReadOnly="false">
<Documentation>
<UserDocu>Include materials using the older legacy format.</UserDocu>
</Documentation>
<Parameter Name="IncludeLegacy" Type="Boolean"/>
</Attribute>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,122 @@
/***************************************************************************
* Copyright (c) 2023-2024 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include "PreCompiled.h"
#include <QMetaType>
#include <Base/Quantity.h>
#include <Base/QuantityPy.h>
#include <CXX/Objects.hxx>
#include <Gui/MetaTypes.h>
#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 << "<MaterialFilterOptions object at " << getMaterialFilterOptionsPtr() << ">";
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;
}

View File

@@ -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<std::map<QString, std::shared_ptr<Material>>>())
: 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<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
MaterialLibrary::getMaterialTree(const std::shared_ptr<Materials::MaterialFilter>& filter,
const Materials::MaterialFilterOptions& options) const
{
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>> materialTree =
std::make_shared<std::map<QString, std::shared_ptr<MaterialTreeNode>>>();
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<std::map<QString, std::shared_ptr<MaterialTreeNode>>> 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<QString, std::shared_ptr<MaterialTreeNode>>>();
std::shared_ptr<MaterialTreeNode> child =
std::make_shared<MaterialTreeNode>();
child->setFolder(mapPtr);
(*node)[itp] = child;
node = mapPtr;
}
else {
node = (*node)[itp]->getFolder();
}
}
}
std::shared_ptr<MaterialTreeNode> child = std::make_shared<MaterialTreeNode>();
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<const Materials::MaterialLibraryLocal*>(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<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
// mapPtr = std::make_shared<
// std::map<QString, std::shared_ptr<MaterialTreeNode>>>();
// std::shared_ptr<MaterialTreeNode> child =
// std::make_shared<MaterialTreeNode>();
// 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<std::map<QString, std::shared_ptr<Material>>>())
{
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<Material> MaterialLibrary::saveMaterial(const std::shared_ptr<Material>& material,
const QString& path,
bool overwrite,
bool saveAsCopy,
bool saveInherited)
std::shared_ptr<Material>
MaterialLibraryLocal::saveMaterial(const std::shared_ptr<Material>& material,
const QString& path,
bool overwrite,
bool saveAsCopy,
bool saveInherited)
{
QString filePath = getLocalPath(path);
QFile file(filePath);
@@ -220,7 +324,7 @@ std::shared_ptr<Material> MaterialLibrary::saveMaterial(const std::shared_ptr<Ma
return addMaterial(material, path);
}
bool MaterialLibrary::fileExists(const QString& path) const
bool MaterialLibraryLocal::fileExists(const QString& path) const
{
QString filePath = getLocalPath(path);
QFileInfo info(filePath);
@@ -228,137 +332,41 @@ bool MaterialLibrary::fileExists(const QString& path) const
return info.exists();
}
std::shared_ptr<Material> MaterialLibrary::addMaterial(const std::shared_ptr<Material>& material,
const QString& path)
std::shared_ptr<Material>
MaterialLibraryLocal::addMaterial(const std::shared_ptr<Material>& material, const QString& path)
{
QString filePath = getRelativePath(path);
QFileInfo info(filePath);
std::shared_ptr<Material> newMaterial = std::make_shared<Material>(*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<Material> MaterialLibrary::getMaterialByPath(const QString& path) const
std::shared_ptr<Material> 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>& material,
const std::shared_ptr<Materials::MaterialFilter>& 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<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
MaterialLibrary::getMaterialTree(const std::shared_ptr<Materials::MaterialFilter>& filter,
const Materials::MaterialFilterOptions& options) const
{
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>> materialTree =
std::make_shared<std::map<QString, std::shared_ptr<MaterialTreeNode>>>();
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<std::map<QString, std::shared_ptr<MaterialTreeNode>>> node =
materialTree;
for (auto& itp : list) {
if (itp.endsWith(QStringLiteral(".FCMat"))) {
std::shared_ptr<MaterialTreeNode> child = std::make_shared<MaterialTreeNode>();
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<QString, std::shared_ptr<MaterialTreeNode>>>();
std::shared_ptr<MaterialTreeNode> child =
std::make_shared<MaterialTreeNode>();
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<std::map<QString, std::shared_ptr<MaterialTreeNode>>> mapPtr =
std::make_shared<std::map<QString, std::shared_ptr<MaterialTreeNode>>>();
std::shared_ptr<MaterialTreeNode> child = std::make_shared<MaterialTreeNode>();
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)
{}

View File

@@ -31,6 +31,7 @@
#include <Base/BaseClass.h>
#include <Mod/Material/MaterialGlobal.h>
#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<MaterialLibrary>
class MaterialsExport MaterialLibrary
: public Library,
public std::enable_shared_from_this<MaterialLibrary>
{
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<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
getMaterialTree(const std::shared_ptr<Materials::MaterialFilter>& filter,
const Materials::MaterialFilterOptions& options) const;
// Use this to get a shared_ptr for *this
std::shared_ptr<MaterialLibrary> getptr()
{
return LibraryBase::operator==(library);
return shared_from_this();
}
bool operator!=(const MaterialLibrary& library) const
{
return !operator==(library);
}
std::shared_ptr<Material> 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<Material> addMaterial(const std::shared_ptr<Material>& material,
const QString& path);
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
getMaterialTree(const std::shared_ptr<Materials::MaterialFilter>& filter,
const Materials::MaterialFilterOptions& options) const;
std::shared_ptr<Material> 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<MaterialLibrary> 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>& material,
const std::shared_ptr<Materials::MaterialFilter>& filter,
const Materials::MaterialFilterOptions& options) const;
bool _readOnly;
std::unique_ptr<std::map<QString, std::shared_ptr<Material>>> _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<Materials::MaterialLibrary>)
Q_DECLARE_METATYPE(std::shared_ptr<Materials::MaterialLibraryLocal>)
#endif // MATERIAL_MATERIALLIBRARY_H

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="BaseClassPy"
Name="MaterialLibraryPy"
Twin="MaterialLibrary"
TwinPointer="MaterialLibrary"
Include="Mod/Material/App/MaterialLibrary.h"
Namespace="Materials"
FatherInclude="Base/BaseClassPy.h"
FatherNamespace="Base"
Constructor="true"
Delete="true">
<Documentation>
<Author Licence="LGPL" Name="DavidCarter" EMail="dcarter@davidcarter.ca" />
<UserDocu>Material library.</UserDocu>
</Documentation>
<Attribute Name="Name" ReadOnly="false">
<Documentation>
<UserDocu>Name of the library</UserDocu>
</Documentation>
<Parameter Name="Name" Type="String"/>
</Attribute>
<Attribute Name="Icon" ReadOnly="false">
<Documentation>
<UserDocu>String value of the icon.</UserDocu>
</Documentation>
<Parameter Name="Icon" Type="String"/>
</Attribute>
<Attribute Name="Directory" ReadOnly="false">
<Documentation>
<UserDocu>Local directory where the library is located. For non-local libraries this will be empty</UserDocu>
</Documentation>
<Parameter Name="Directory" Type="String"/>
</Attribute>
<Attribute Name="ReadOnly" ReadOnly="false">
<Documentation>
<UserDocu>True if the library is local.</UserDocu>
</Documentation>
<Parameter Name="ReadOnly" Type="Boolean"/>
</Attribute>
<Attribute Name="Local" ReadOnly="false">
<Documentation>
<UserDocu>True if the library is local.</UserDocu>
</Documentation>
<Parameter Name="Local" Type="Boolean"/>
</Attribute>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,125 @@
/***************************************************************************
* Copyright (c) 2023-2024 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include "PreCompiled.h"
#include <QMetaType>
#include <Base/Quantity.h>
#include <Base/QuantityPy.h>
#include <CXX/Objects.hxx>
#include <Gui/MetaTypes.h>
#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 << "<MaterialLibrary object at " << getMaterialLibraryPtr() << ">";
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;
}

View File

@@ -45,7 +45,7 @@
using namespace Materials;
MaterialEntry::MaterialEntry(const std::shared_ptr<MaterialLibrary>& library,
MaterialEntry::MaterialEntry(const std::shared_ptr<MaterialLibraryLocal>& library,
const QString& modelName,
const QString& dir,
const QString& modelUuid)
@@ -55,7 +55,7 @@ MaterialEntry::MaterialEntry(const std::shared_ptr<MaterialLibrary>& library,
, _uuid(modelUuid)
{}
MaterialYamlEntry::MaterialYamlEntry(const std::shared_ptr<MaterialLibrary>& library,
MaterialYamlEntry::MaterialYamlEntry(const std::shared_ptr<MaterialLibraryLocal>& library,
const QString& modelName,
const QString& dir,
const QString& modelUuid,
@@ -98,9 +98,9 @@ std::shared_ptr<QList<QVariant>> MaterialYamlEntry::readImageList(const YAML::No
return readList(node, true);
}
std::shared_ptr<Material2DArray> MaterialYamlEntry::read2DArray(const YAML::Node& node, int columns)
std::shared_ptr<Array2D> MaterialYamlEntry::read2DArray(const YAML::Node& node, int columns)
{
auto array2d = std::make_shared<Material2DArray>();
auto array2d = std::make_shared<Array2D>();
array2d->setColumns(columns);
if (node.size() == 1 || node.size() == 2) {
@@ -126,9 +126,9 @@ std::shared_ptr<Material2DArray> MaterialYamlEntry::read2DArray(const YAML::Node
return array2d;
}
std::shared_ptr<Material3DArray> MaterialYamlEntry::read3DArray(const YAML::Node& node, int columns)
std::shared_ptr<Array3D> MaterialYamlEntry::read3DArray(const YAML::Node& node, int columns)
{
auto array3d = std::make_shared<Material3DArray>();
auto array3d = std::make_shared<Array3D>();
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<MaterialLibrary>& model)
void MaterialLoader::addLibrary(const std::shared_ptr<MaterialLibraryLocal>& model)
{
_libraryList->push_back(model);
}
std::shared_ptr<MaterialEntry>
MaterialLoader::getMaterialFromYAML(const std::shared_ptr<MaterialLibrary>& library,
MaterialLoader::getMaterialFromYAML(const std::shared_ptr<MaterialLibraryLocal>& library,
YAML::Node& yamlroot,
const QString& path)
{
@@ -387,18 +387,20 @@ MaterialLoader::getMaterialFromYAML(const std::shared_ptr<MaterialLibrary>& libr
}
std::shared_ptr<MaterialEntry>
MaterialLoader::getMaterialFromPath(const std::shared_ptr<MaterialLibrary>& library,
MaterialLoader::getMaterialFromPath(const std::shared_ptr<MaterialLibraryLocal>& library,
const QString& path) const
{
std::shared_ptr<MaterialEntry> model = nullptr;
auto materialLibrary =
reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(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<MaterialLibrary>& 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>& material)
dereference(_materialMap, material);
}
void MaterialLoader::loadLibrary(const std::shared_ptr<MaterialLibrary>& library)
void MaterialLoader::loadLibrary(const std::shared_ptr<MaterialLibraryLocal>& library)
{
if (_materialEntryMap == nullptr) {
_materialEntryMap = std::make_unique<std::map<QString, std::shared_ptr<MaterialEntry>>>();
@@ -541,12 +543,16 @@ void MaterialLoader::loadLibrary(const std::shared_ptr<MaterialLibrary>& library
}
}
void MaterialLoader::loadLibraries()
void MaterialLoader::loadLibraries(
const std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>>& 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<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(it);
loadLibrary(materialLibrary);
}
}
}
@@ -555,92 +561,8 @@ void MaterialLoader::loadLibraries()
}
}
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> 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<MaterialLibrary>(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<MaterialLibrary>(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<MaterialLibrary>(
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<MaterialLibrary>(QStringLiteral("Custom"),
resourceDir,
QStringLiteral(":/icons/user.svg"),
false);
_libraryList->push_back(libData);
}
}
}
return _libraryList;
}
std::shared_ptr<std::list<QString>>
MaterialLoader::getMaterialFolders(const MaterialLibrary& library)
MaterialLoader::getMaterialFolders(const MaterialLibraryLocal& library)
{
std::shared_ptr<std::list<QString>> pathList = std::make_shared<std::list<QString>>();
QDirIterator it(library.getDirectory(), QDirIterator::Subdirectories);

View File

@@ -33,12 +33,14 @@
namespace Materials
{
class MaterialLibrary;
class MaterialLibraryLocal;
class MaterialEntry
{
public:
MaterialEntry() = default;
MaterialEntry(const std::shared_ptr<MaterialLibrary>& library,
MaterialEntry(const std::shared_ptr<MaterialLibraryLocal>& library,
const QString& modelName,
const QString& dir,
const QString& modelUuid);
@@ -47,7 +49,7 @@ public:
virtual void
addToTree(std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> materialMap) = 0;
std::shared_ptr<MaterialLibrary> getLibrary() const
std::shared_ptr<MaterialLibraryLocal> getLibrary() const
{
return _library;
}
@@ -65,7 +67,7 @@ public:
}
protected:
std::shared_ptr<MaterialLibrary> _library;
std::shared_ptr<MaterialLibraryLocal> _library;
QString _name;
QString _directory;
QString _uuid;
@@ -74,7 +76,7 @@ protected:
class MaterialYamlEntry: public MaterialEntry
{
public:
MaterialYamlEntry(const std::shared_ptr<MaterialLibrary>& library,
MaterialYamlEntry(const std::shared_ptr<MaterialLibraryLocal>& library,
const QString& modelName,
const QString& dir,
const QString& modelUuid,
@@ -101,8 +103,8 @@ private:
static std::shared_ptr<QList<QVariant>> readList(const YAML::Node& node,
bool isImageList = false);
static std::shared_ptr<QList<QVariant>> readImageList(const YAML::Node& node);
static std::shared_ptr<Material2DArray> read2DArray(const YAML::Node& node, int columns);
static std::shared_ptr<Material3DArray> read3DArray(const YAML::Node& node, int columns);
static std::shared_ptr<Array2D> read2DArray(const YAML::Node& node, int columns);
static std::shared_ptr<Array3D> read3DArray(const YAML::Node& node, int columns);
YAML::Node _model;
};
@@ -114,14 +116,14 @@ public:
const std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>>& libraryList);
~MaterialLoader() = default;
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> getMaterialLibraries();
static std::shared_ptr<std::list<QString>> getMaterialFolders(const MaterialLibrary& library);
static std::shared_ptr<std::list<QString>>
getMaterialFolders(const MaterialLibraryLocal& library);
static void showYaml(const YAML::Node& yaml);
static void
dereference(const std::shared_ptr<std::map<QString, std::shared_ptr<Material>>>& materialMap,
const std::shared_ptr<Material>& material);
static std::shared_ptr<MaterialEntry>
getMaterialFromYAML(const std::shared_ptr<MaterialLibrary>& library,
getMaterialFromYAML(const std::shared_ptr<MaterialLibraryLocal>& library,
YAML::Node& yamlroot,
const QString& path);
@@ -131,10 +133,11 @@ private:
void addToTree(std::shared_ptr<MaterialEntry> model);
void dereference(const std::shared_ptr<Material>& material);
std::shared_ptr<MaterialEntry>
getMaterialFromPath(const std::shared_ptr<MaterialLibrary>& library, const QString& path) const;
void addLibrary(const std::shared_ptr<MaterialLibrary>& model);
void loadLibrary(const std::shared_ptr<MaterialLibrary>& library);
void loadLibraries();
getMaterialFromPath(const std::shared_ptr<MaterialLibraryLocal>& library, const QString& path) const;
void addLibrary(const std::shared_ptr<MaterialLibraryLocal>& model);
void loadLibrary(const std::shared_ptr<MaterialLibraryLocal>& library);
void loadLibraries(
const std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>>& libraryList);
static std::unique_ptr<std::map<QString, std::shared_ptr<MaterialEntry>>> _materialEntryMap;
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> _materialMap;

View File

@@ -24,8 +24,8 @@
#include <random>
#endif
#include <QMutex>
#include <QDirIterator>
#include <QMutex>
#include <QMutexLocker>
#include <App/Application.h>
@@ -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<std::list<std::shared_ptr<MaterialLibrary>>> MaterialManager::_libraryList =
nullptr;
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> MaterialManager::_materialMap =
nullptr;
QMutex MaterialManager::_mutex;
TYPESYSTEM_SOURCE(Materials::MaterialManager, Base::BaseClass)
QMutex MaterialManager::_mutex;
MaterialManager* MaterialManager::_manager = nullptr;
std::unique_ptr<MaterialManagerLocal> 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<ModelManager>();
Q_UNUSED(manager)
_materialMap = std::make_shared<std::map<QString, std::shared_ptr<Material>>>();
if (_libraryList == nullptr) {
_libraryList = std::make_shared<std::list<std::shared_ptr<MaterialLibrary>>>();
}
// 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<MaterialManagerLocal>();
}
}
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<MaterialLibrary>& library,
const std::shared_ptr<Material>& 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<App::Material> MaterialManager::defaultAppearance()
{
@@ -149,7 +106,7 @@ std::shared_ptr<App::Material> 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<Material> 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<std::list<std::shared_ptr<MaterialLibrary>>> MaterialManager::getLibraries()
{
auto libraries = std::make_shared<std::list<std::shared_ptr<MaterialLibrary>>>();
auto localLibraries = _localManager->getLibraries();
for (auto& local : *localLibraries) {
libraries->push_back(local);
}
return libraries;
}
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> MaterialManager::getLocalLibraries()
{
return _localManager->getLibraries();
}
std::shared_ptr<MaterialLibrary> 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<std::vector<std::tuple<QString, QString, QString>>>
MaterialManager::libraryMaterials(const QString& libraryName)
{
return _localManager->libraryMaterials(libraryName);
}
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
MaterialManager::libraryMaterials(const QString& libraryName,
const std::shared_ptr<MaterialFilter>& filter,
const MaterialFilterOptions& options)
{
return _localManager->libraryMaterials(libraryName, filter, options);
}
bool MaterialManager::isLocalLibrary(const QString& libraryName)
{
return true;
}
//=====
//
// Folder management
//
//=====
std::shared_ptr<std::list<QString>>
MaterialManager::getMaterialFolders(const std::shared_ptr<MaterialLibrary>& library) const
{
if (library->isLocal()) {
auto materialLibrary =
reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(library);
return _localManager->getMaterialFolders(materialLibrary);
}
return std::make_shared<std::list<QString>>();
}
void MaterialManager::createFolder(const std::shared_ptr<MaterialLibrary>& library,
const QString& path)
{
if (library->isLocal()) {
auto materialLibrary =
reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(library);
_localManager->createFolder(materialLibrary, path);
}
}
void MaterialManager::renameFolder(const std::shared_ptr<MaterialLibrary>& library,
const QString& oldPath,
const QString& newPath)
{
if (library->isLocal()) {
auto materialLibrary =
reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(library);
_localManager->renameFolder(materialLibrary, oldPath, newPath);
}
}
void MaterialManager::deleteRecursive(const std::shared_ptr<MaterialLibrary>& library,
const QString& path)
{
if (library->isLocal()) {
auto materialLibrary =
reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(library);
_localManager->deleteRecursive(materialLibrary, path);
}
}
//=====
//
// Tree management
//
//=====
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
MaterialManager::getMaterialTree(const std::shared_ptr<MaterialLibrary>& library,
const std::shared_ptr<Materials::MaterialFilter>& filter) const
{
MaterialFilterOptions options;
return library->getMaterialTree(filter, options);
}
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
MaterialManager::getMaterialTree(const std::shared_ptr<MaterialLibrary>& library,
const std::shared_ptr<Materials::MaterialFilter>& filter,
const MaterialFilterOptions& options) const
{
return library->getMaterialTree(filter, options);
}
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
MaterialManager::getMaterialTree(const std::shared_ptr<MaterialLibrary>& library) const
{
std::shared_ptr<Materials::MaterialFilter> filter;
MaterialFilterOptions options;
return library->getMaterialTree(filter, options);
}
//=====
//
// Material management
//
//=====
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>>
MaterialManager::getLocalMaterials() const
{
return _localManager->getLocalMaterials();
}
std::shared_ptr<Material> 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<Material> MaterialManager::getMaterial(const App::Material& material)
@@ -237,65 +358,13 @@ std::shared_ptr<Material> MaterialManager::getMaterial(const App::Material& mate
std::shared_ptr<Material> 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<Material> 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<Material>
@@ -308,105 +377,69 @@ MaterialManager::getParent(const std::shared_ptr<Material>& material) const
return getMaterial(material->getParentUUID());
}
bool MaterialManager::exists(const QString& uuid) const
{
return _localManager->exists(uuid);
}
bool MaterialManager::exists(const std::shared_ptr<MaterialLibrary>& 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<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(library);
return _localManager->exists(materialLibrary, uuid);
}
return false;
}
std::shared_ptr<MaterialLibrary> 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<std::list<std::shared_ptr<MaterialLibrary>>>
MaterialManager::getMaterialLibraries() const
void MaterialManager::saveMaterial(const std::shared_ptr<MaterialLibrary>& library,
const std::shared_ptr<Material>& material,
const QString& path,
bool overwrite,
bool saveAsCopy,
bool saveInherited) const
{
if (_libraryList == nullptr) {
if (_materialMap == nullptr) {
_materialMap = std::make_shared<std::map<QString, std::shared_ptr<Material>>>();
}
_libraryList = std::make_shared<std::list<std::shared_ptr<MaterialLibrary>>>();
// Load the libraries
MaterialLoader loader(_materialMap, _libraryList);
}
return _libraryList;
auto materialLibrary =
reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(library);
_localManager
->saveMaterial(materialLibrary, material, path, overwrite, saveAsCopy, saveInherited);
}
std::shared_ptr<std::list<QString>>
MaterialManager::getMaterialFolders(const std::shared_ptr<MaterialLibrary>& 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<std::map<QString, std::shared_ptr<Material>>>
MaterialManager::materialsWithModel(const QString& uuid) const
{
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> dict =
std::make_shared<std::map<QString, std::shared_ptr<Material>>>();
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<std::map<QString, std::shared_ptr<Material>>>
MaterialManager::materialsWithModelComplete(const QString& uuid) const
{
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> dict =
std::make_shared<std::map<QString, std::shared_ptr<Material>>>();
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> material) const
{
MaterialLoader::dereference(_materialMap, material);
_localManager->dereference(material);
}

View File

@@ -26,13 +26,14 @@
#include <filesystem>
#include <Base/Parameter.h>
#include <Mod/Material/MaterialGlobal.h>
#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<App::Material> defaultAppearance();
static std::shared_ptr<Material> defaultMaterial();
static QString defaultMaterialUUID();
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> getMaterials() const
{
return _materialMap;
}
// Library management
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> getLibraries();
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> getLocalLibraries();
std::shared_ptr<MaterialLibrary> 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<std::vector<std::tuple<QString, QString, QString>>>
libraryMaterials(const QString& libraryName);
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
libraryMaterials(const QString& libraryName,
const std::shared_ptr<MaterialFilter>& filter,
const MaterialFilterOptions& options);
bool isLocalLibrary(const QString& libraryName);
// Folder management
std::shared_ptr<std::list<QString>>
getMaterialFolders(const std::shared_ptr<MaterialLibrary>& library) const;
void createFolder(const std::shared_ptr<MaterialLibrary>& library, const QString& path);
void renameFolder(const std::shared_ptr<MaterialLibrary>& library,
const QString& oldPath,
const QString& newPath);
void deleteRecursive(const std::shared_ptr<MaterialLibrary>& library, const QString& path);
// Tree management
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
getMaterialTree(const std::shared_ptr<MaterialLibrary>& library,
const std::shared_ptr<Materials::MaterialFilter>& filter) const;
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
getMaterialTree(const std::shared_ptr<MaterialLibrary>& library,
const std::shared_ptr<Materials::MaterialFilter>& filter,
const MaterialFilterOptions& options) const;
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
getMaterialTree(const std::shared_ptr<MaterialLibrary>& library) const;
// Material management
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> getLocalMaterials() const;
std::shared_ptr<Material> getMaterial(const QString& uuid) const;
static std::shared_ptr<Material> getMaterial(const App::Material& material);
std::shared_ptr<Material> getMaterialByPath(const QString& path) const;
std::shared_ptr<Material> getMaterialByPath(const QString& path, const QString& library) const;
std::shared_ptr<Material> getParent(const std::shared_ptr<Material>& material) const;
std::shared_ptr<MaterialLibrary> getLibrary(const QString& name) const;
bool exists(const QString& uuid) const;
bool exists(const std::shared_ptr<MaterialLibrary>& library, const QString& uuid) const;
// Library management
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> getMaterialLibraries() const;
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
getMaterialTree(const std::shared_ptr<MaterialLibrary>& library,
const std::shared_ptr<Materials::MaterialFilter>& filter) const
{
MaterialFilterOptions options;
return library->getMaterialTree(filter, options);
}
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
getMaterialTree(const std::shared_ptr<MaterialLibrary>& library,
const std::shared_ptr<Materials::MaterialFilter>& filter,
const MaterialFilterOptions& options) const
{
return library->getMaterialTree(filter, options);
}
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
getMaterialTree(const std::shared_ptr<MaterialLibrary>& library) const
{
std::shared_ptr<Materials::MaterialFilter> filter;
MaterialFilterOptions options;
return library->getMaterialTree(filter, options);
}
std::shared_ptr<std::list<QString>>
getMaterialFolders(const std::shared_ptr<MaterialLibrary>& library) const;
void createFolder(const std::shared_ptr<MaterialLibrary>& library, const QString& path) const
{
library->createFolder(path);
}
void renameFolder(const std::shared_ptr<MaterialLibrary>& library,
const QString& oldPath,
const QString& newPath) const
{
library->renameFolder(oldPath, newPath);
}
void deleteRecursive(const std::shared_ptr<MaterialLibrary>& 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<MaterialLibrary>& library,
const std::shared_ptr<Material>& material,
@@ -136,13 +137,15 @@ public:
void dereference() const;
private:
static std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> _libraryList;
static std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> _materialMap;
static QMutex _mutex;
MaterialManager();
static void initManagers();
static void initLibraries();
static MaterialManager* _manager;
static std::unique_ptr<MaterialManagerLocal> _localManager;
static QMutex _mutex;
};
} // namespace Materials
#endif // MATERIAL_MATERIALMANAGER_H
#endif // MATERIAL_MATERIALMANAGER_H

View File

@@ -0,0 +1,593 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#include <random>
#endif
#include <QDirIterator>
#include <QMutex>
#include <QMutexLocker>
#include <App/Application.h>
#include <App/Material.h>
#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<std::list<std::shared_ptr<MaterialLibrary>>>
MaterialManagerLocal::_libraryList = nullptr;
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> 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<std::map<QString, std::shared_ptr<Material>>>();
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<std::list<std::shared_ptr<MaterialLibrary>>> MaterialManagerLocal::getLibraries()
{
if (_libraryList == nullptr) {
initLibraries();
}
return _libraryList;
}
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>>
MaterialManagerLocal::getMaterialLibraries()
{
if (_libraryList == nullptr) {
initLibraries();
}
return _libraryList;
}
std::shared_ptr<MaterialLibrary> 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<MaterialLibraryLocal>(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<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(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<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(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<std::vector<std::tuple<QString, QString, QString>>>
MaterialManagerLocal::libraryMaterials(const QString& libraryName)
{
auto materials = std::make_shared<std::vector<std::tuple<QString, QString, QString>>>();
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<QString, QString, QString>(it.first,
it.second->getDirectory(),
it.second->getName()));
}
}
return materials;
}
bool MaterialManagerLocal::passFilter(const std::shared_ptr<Material>& material,
const std::shared_ptr<Materials::MaterialFilter>& 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<std::vector<std::tuple<QString, QString, QString>>>
MaterialManagerLocal::libraryMaterials(const QString& libraryName,
const std::shared_ptr<MaterialFilter>& filter,
const MaterialFilterOptions& options)
{
auto materials = std::make_shared<std::vector<std::tuple<QString, QString, QString>>>();
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<QString, QString, QString>(it.first,
it.second->getDirectory(),
it.second->getName()));
}
}
}
return materials;
}
//=====
//
// Folder management
//
//=====
std::shared_ptr<std::list<QString>>
MaterialManagerLocal::getMaterialFolders(const std::shared_ptr<MaterialLibraryLocal>& library) const
{
// auto materialLibrary =
// reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(library);
return MaterialLoader::getMaterialFolders(*library);
}
void MaterialManagerLocal::createFolder(const std::shared_ptr<MaterialLibraryLocal>& library,
const QString& path)
{
library->createFolder(path);
}
void MaterialManagerLocal::renameFolder(const std::shared_ptr<MaterialLibraryLocal>& library,
const QString& oldPath,
const QString& newPath)
{
library->renameFolder(oldPath, newPath);
}
void MaterialManagerLocal::deleteRecursive(const std::shared_ptr<MaterialLibraryLocal>& library,
const QString& path)
{
library->deleteRecursive(path);
dereference();
}
//=====
//
// Material management
//
//=====
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>>
MaterialManagerLocal::getLocalMaterials() const
{
return _materialMap;
}
std::shared_ptr<Material> MaterialManagerLocal::getMaterial(const QString& uuid) const
{
try {
return _materialMap->at(uuid);
}
catch (std::out_of_range&) {
throw MaterialNotFound();
}
}
std::shared_ptr<Material> MaterialManagerLocal::getMaterialByPath(const QString& path) const
{
QString cleanPath = QDir::cleanPath(path);
for (auto& library : *_libraryList) {
if (library->isLocal()) {
auto materialLibrary =
reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(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<Material> MaterialManagerLocal::getMaterialByPath(const QString& path,
const QString& lib) const
{
auto library = getLibrary(lib); // May throw LibraryNotFound
if (library->isLocal()) {
auto materialLibrary =
reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(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<MaterialLibrary>& library,
const QString& uuid) const
{
try {
auto material = getMaterial(uuid);
if (material && material->getLibrary()->isLocal()) {
auto materialLibrary =
reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(
*(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<MaterialLibraryLocal>& library,
const std::shared_ptr<Material>& 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<std::map<QString, std::shared_ptr<Material>>>
MaterialManagerLocal::materialsWithModel(const QString& uuid) const
{
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> dict =
std::make_shared<std::map<QString, std::shared_ptr<Material>>>();
for (auto& it : *_materialMap) {
QString key = it.first;
auto material = it.second;
if (material->hasModel(uuid)) {
(*dict)[key] = material;
}
}
return dict;
}
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>>
MaterialManagerLocal::materialsWithModelComplete(const QString& uuid) const
{
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> dict =
std::make_shared<std::map<QString, std::shared_ptr<Material>>>();
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> material) const
{
MaterialLoader::dereference(_materialMap, material);
}
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>>
MaterialManagerLocal::getConfiguredLibraries()
{
auto libraryList = std::make_shared<std::list<std::shared_ptr<MaterialLibrary>>>();
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<MaterialLibraryLocal>(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<MaterialLibraryLocal>(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<MaterialLibraryLocal>(
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<MaterialLibraryLocal>(
QStringLiteral("Custom"),
resourceDir,
QStringLiteral(":/icons/user.svg"),
false);
libraryList->push_back(libData);
}
}
}
return libraryList;
}

View File

@@ -0,0 +1,131 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_MATERIALMANAGERLOCAL_H
#define MATERIAL_MATERIALMANAGERLOCAL_H
#include <memory>
#include <filesystem>
#include <Mod/Material/MaterialGlobal.h>
#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<std::list<std::shared_ptr<MaterialLibrary>>> getLibraries();
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> getMaterialLibraries();
std::shared_ptr<MaterialLibrary> 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<std::vector<std::tuple<QString, QString, QString>>>
libraryMaterials(const QString& libraryName);
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
libraryMaterials(const QString& libraryName,
const std::shared_ptr<MaterialFilter>& filter,
const MaterialFilterOptions& options);
// Folder management
std::shared_ptr<std::list<QString>>
getMaterialFolders(const std::shared_ptr<MaterialLibraryLocal>& library) const;
void createFolder(const std::shared_ptr<MaterialLibraryLocal>& library, const QString& path);
void renameFolder(const std::shared_ptr<MaterialLibraryLocal>& library,
const QString& oldPath,
const QString& newPath);
void deleteRecursive(const std::shared_ptr<MaterialLibraryLocal>& library, const QString& path);
// Material management
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> getLocalMaterials() const;
std::shared_ptr<Material> getMaterial(const QString& uuid) const;
std::shared_ptr<Material> getMaterialByPath(const QString& path) const;
std::shared_ptr<Material> getMaterialByPath(const QString& path, const QString& library) const;
bool exists(const QString& uuid) const;
bool exists(const std::shared_ptr<MaterialLibrary>& library, const QString& uuid) const;
void remove(const QString& uuid);
void saveMaterial(const std::shared_ptr<MaterialLibraryLocal>& library,
const std::shared_ptr<Material>& 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<std::map<QString, std::shared_ptr<Material>>>
materialsWithModel(const QString& uuid) const;
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>>
materialsWithModelComplete(const QString& uuid) const;
void dereference(std::shared_ptr<Material> material) const;
void dereference() const;
protected:
static std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> getConfiguredLibraries();
bool passFilter(const std::shared_ptr<Material>& material,
const std::shared_ptr<Materials::MaterialFilter>& filter,
const Materials::MaterialFilterOptions& options) const;
private:
static std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> _libraryList;
static std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> _materialMap;
static QMutex _mutex;
static void initLibraries();
};
} // namespace Materials
#endif // MATERIAL_MATERIALMANAGERLOCAL_H

View File

@@ -10,7 +10,7 @@
FatherInclude="Base/BaseClassPy.h"
FatherNamespace="Base"
Constructor="true"
Delete="true">
Delete="false">
<Documentation>
<Author Licence="LGPL" Name="DavidCarter" EMail="dcarter@davidcarter.ca" />
<UserDocu>Material descriptions.</UserDocu>

View File

@@ -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<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(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<std::map<QString, std::shared_ptr<MaterialTreeNode>>>& 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<MaterialFilter>(*(static_cast<MaterialFilterPy*>(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);
}
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="ModelPropertyPy"
Name="MaterialPropertyPy"
Twin="MaterialProperty"
TwinPointer="MaterialProperty"
Include="Mod/Material/App/Materials.h"
Namespace="Materials"
FatherInclude="Mod/Material/App/Model.h"
FatherNamespace="Materials"
Constructor="true"
Delete="false">
<Documentation>
<Author Licence="LGPL" Name="DavidCarter" EMail="dcarter@davidcarter.ca" />
<UserDocu>Material property descriptions.</UserDocu>
</Documentation>
<Attribute Name="Value" ReadOnly="true">
<Documentation>
<UserDocu>The value of the material property.</UserDocu>
</Documentation>
<Parameter Name="Value" Type="Object"/>
</Attribute>
<Attribute Name="Empty" ReadOnly="true">
<Documentation>
<UserDocu>The property value is undefined.</UserDocu>
</Documentation>
<Parameter Name="Empty" Type="Boolean"/>
</Attribute>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,74 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include "PreCompiled.h"
#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 << "<MaterialProperty object at " << getMaterialPropertyPtr() << ">";
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;
}

View File

@@ -39,7 +39,7 @@
</Documentation>
<Parameter Name="Name" Type="String"/>
</Attribute>
<Attribute Name="Directory" ReadOnly="true">
<Attribute Name="Directory" ReadOnly="false">
<Documentation>
<UserDocu>Model directory relative to the library root.</UserDocu>
</Documentation>
@@ -69,7 +69,7 @@
</Documentation>
<Parameter Name="Reference" Type="String"/>
</Attribute>
<Attribute Name="Parent" ReadOnly="true">
<Attribute Name="Parent" ReadOnly="false">
<Documentation>
<UserDocu>Parent material UUID.</UserDocu>
</Documentation>
@@ -210,6 +210,17 @@
<UserDocu>Set the value associated with the property</UserDocu>
</Documentation>
</Methode>
<Methode Name="setValue" ReadOnly="true">
<Documentation>
<UserDocu>Set the value associated with the property</UserDocu>
</Documentation>
</Methode>
<Attribute Name="PropertyObjects" ReadOnly="true">
<Documentation>
<UserDocu>Dictionary of MaterialProperty objects.</UserDocu>
</Documentation>
<Parameter Name="PropertyObjects" Type="Dict"/>
</Attribute>
<Methode Name="keys" NoArgs="true">
<Documentation>
<UserDocu>Property keys</UserDocu>

View File

@@ -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<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(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<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(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<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(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<QList<QVariant>>();
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<Base::Quantity>()) {
return new Base::QuantityPy(new Base::Quantity(value.value<Base::Quantity>()));
}
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<QList<QVariant>>()) {
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<Materials::Material2DArray>(property->getMaterialValue());
return new Array2DPy(new Material2DArray(*value));
std::static_pointer_cast<Materials::Array2D>(property->getMaterialValue());
return new Array2DPy(new Array2D(*value));
}
if (property->getType() == MaterialValue::Array3D) {
auto value =
std::static_pointer_cast<Materials::Material3DArray>(property->getMaterialValue());
return new Array3DPy(new Material3DArray(*value));
std::static_pointer_cast<Materials::Array3D>(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<Materials::Array2D>(property->getMaterialValue());
return new Array2DPy(new Array2D(*value));
}
if (property->getType() == MaterialValue::Array3D) {
auto value =
std::static_pointer_cast<Materials::Array3D>(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<QVariant> 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<Array2DPy*>(arrayObj);
auto shared = std::make_shared<Array2D>(*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<Array3DPy*>(arrayObj);
auto shared = std::make_shared<Array3D>(*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<Materials::Array2D>(
// 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<Materials::Array3D>(
// 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<MaterialPy*>(self)->getProperties();
return Py::new_reference_to(dict.getItem(Py::Object(key)));
}
}

View File

@@ -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<Base::Quantity>();
auto q2 = other._value.value<Base::Quantity>();
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<const Materials::Array2D*>(this);
auto a2 = static_cast<const Materials::Array2D*>(&other);
a1->validate(*a2);
}
else if (_valueType == Array3D) {
auto a1 = static_cast<const Materials::Array3D*>(this);
auto a2 = static_cast<const Materials::Array3D*>(&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<QVariant>& 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<QList<QVariant>> 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<QList<QVariant>> Array2D::getRow(int row) const
{
validateRow(row);
@@ -392,7 +464,7 @@ std::shared_ptr<QList<QVariant>> Material2DArray::getRow(int row) const
}
}
std::shared_ptr<QList<QVariant>> Material2DArray::getRow(int row)
std::shared_ptr<QList<QVariant>> Array2D::getRow(int row)
{
validateRow(row);
@@ -404,17 +476,17 @@ std::shared_ptr<QList<QVariant>> Material2DArray::getRow(int row)
}
}
void Material2DArray::addRow(const std::shared_ptr<QList<QVariant>>& row)
void Array2D::addRow(const std::shared_ptr<QList<QVariant>>& row)
{
_rows.push_back(row);
}
void Material2DArray::insertRow(int index, const std::shared_ptr<QList<QVariant>>& row)
void Array2D::insertRow(int index, const std::shared_ptr<QList<QVariant>>& row)
{
_rows.insert(_rows.begin() + index, row);
}
void Material2DArray::deleteRow(int row)
void Array2D::deleteRow(int row)
{
if (row >= static_cast<int>(_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<QList<QVariant>>();
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<QList<QVariant>>& row)
void Array2D::dumpRow(const std::shared_ptr<QList<QVariant>>& row)
{
Base::Console().Log("row: ");
for (auto& column : *row) {
@@ -458,14 +541,14 @@ void Material2DArray::dumpRow(const std::shared_ptr<QList<QVariant>>& 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<QList<Base::Quantity>>();
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<QList<std::shared_ptr<QList<Base::Quantity>>>>&
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<QList<std::shared_ptr<QList<Base::Quantity>>>>&
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<QList<Base::Quantity>> Material3DArray::getRow(int depth, int row) const
std::shared_ptr<QList<Base::Quantity>> Array3D::getRow(int depth, int row) const
{
validateRow(depth, row);
@@ -585,13 +723,13 @@ std::shared_ptr<QList<Base::Quantity>> Material3DArray::getRow(int depth, int ro
}
}
std::shared_ptr<QList<Base::Quantity>> Material3DArray::getRow(int row) const
std::shared_ptr<QList<Base::Quantity>> Array3D::getRow(int row) const
{
// Check if we can convert otherwise throw error
return getRow(_currentDepth, row);
}
std::shared_ptr<QList<Base::Quantity>> Material3DArray::getRow(int depth, int row)
std::shared_ptr<QList<Base::Quantity>> Array3D::getRow(int depth, int row)
{
validateRow(depth, row);
@@ -603,12 +741,12 @@ std::shared_ptr<QList<Base::Quantity>> Material3DArray::getRow(int depth, int ro
}
}
std::shared_ptr<QList<Base::Quantity>> Material3DArray::getRow(int row)
std::shared_ptr<QList<Base::Quantity>> Array3D::getRow(int row)
{
return getRow(_currentDepth, row);
}
void Material3DArray::addRow(int depth, const std::shared_ptr<QList<Base::Quantity>>& row)
void Array3D::addRow(int depth, const std::shared_ptr<QList<Base::Quantity>>& row)
{
try {
getTable(depth)->push_back(row);
@@ -618,12 +756,12 @@ void Material3DArray::addRow(int depth, const std::shared_ptr<QList<Base::Quanti
}
}
void Material3DArray::addRow(const std::shared_ptr<QList<Base::Quantity>>& row)
void Array3D::addRow(const std::shared_ptr<QList<Base::Quantity>>& 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<QList<std::shared_ptr<QList<Base::Quantity>>>>();
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<QList<Base::Quantity>>& rowData)
{
@@ -667,12 +814,12 @@ void Material3DArray::insertRow(int depth,
}
}
void Material3DArray::insertRow(int row, const std::shared_ptr<QList<Base::Quantity>>& rowData)
void Array3D::insertRow(int row, const std::shared_ptr<QList<Base::Quantity>>& 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<int>(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<QList<Base::Quantity>>();
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();

View File

@@ -91,6 +91,7 @@ public:
return _value.value<QList<QVariant>>();
}
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<QString, ValueType> _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<std::shared_ptr<QList<QVariant>>>& 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<QList<QVariant>> getRow(int row) const;
std::shared_ptr<QList<QVariant>> getRow(int row);
@@ -171,6 +176,7 @@ public:
void addRow(const std::shared_ptr<QList<QVariant>>& row);
void insertRow(int index, const std::shared_ptr<QList<QVariant>>& 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<std::shared_ptr<QList<QVariant>>> _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<Base::Quantity, std::shared_ptr<QList<std::shared_ptr<QList<Base::Quantity>>>>>>&
@@ -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<QList<std::shared_ptr<QList<Base::Quantity>>>>&
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<std::pair<Base::Quantity, std::shared_ptr<QList<std::shared_ptr<QList<Base::Quantity>>>>>>
_rowMap;
int _currentDepth;
@@ -269,7 +284,7 @@ protected:
} // namespace Materials
Q_DECLARE_METATYPE(Materials::MaterialValue)
Q_DECLARE_METATYPE(std::shared_ptr<Materials::Material2DArray>)
Q_DECLARE_METATYPE(std::shared_ptr<Materials::Material3DArray>)
Q_DECLARE_METATYPE(std::shared_ptr<Materials::Array2D>)
Q_DECLARE_METATYPE(std::shared_ptr<Materials::Array3D>)
#endif // MATERIAL_MATERIALVALUE_H

View File

@@ -65,11 +65,11 @@ void MaterialProperty::copyValuePtr(const std::shared_ptr<MaterialValue>& value)
{
if (value->getType() == MaterialValue::Array2D) {
_valuePtr =
std::make_shared<Material2DArray>(*(std::static_pointer_cast<Material2DArray>(value)));
std::make_shared<Array2D>(*(std::static_pointer_cast<Array2D>(value)));
}
else if (value->getType() == MaterialValue::Array3D) {
_valuePtr =
std::make_shared<Material3DArray>(*(std::static_pointer_cast<Material3DArray>(value)));
std::make_shared<Array3D>(*(std::static_pointer_cast<Array3D>(value)));
}
else {
_valuePtr = std::make_shared<MaterialValue>(*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<Base::Quantity>();
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<Material2DArray>();
auto arrayPtr = std::make_shared<Array2D>();
arrayPtr->setColumns(columns());
_valuePtr = arrayPtr;
}
else if (mappedType == MaterialValue::Array3D) {
auto arrayPtr = std::make_shared<Material3DArray>();
auto arrayPtr = std::make_shared<Array3D>();
// 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<MaterialLibrary>& 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<MaterialValue>& 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::map<QString, std::shared_ptr<MaterialPropert
if (value.isNull()) {
return {};
}
return QString(QLatin1String("%L1"))
return QString(QStringLiteral("%L1"))
.arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION);
}
return property->getValue().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<Material> 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<Material> 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<Material>& 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);
}
}

View File

@@ -80,6 +80,10 @@ public:
{
return _valuePtr->isNull();
}
bool isEmpty() const
{
return _valuePtr->isEmpty();
}
std::shared_ptr<MaterialValue> getMaterialValue();
std::shared_ptr<MaterialValue> 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<MaterialProperty>& 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<MaterialValue>& 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<Material>& other) const;
protected:
void addModel(const QString& uuid);
static void removeUUID(QSet<QString>& uuidList, const QString& uuid);
@@ -455,6 +468,7 @@ protected:
private:
std::shared_ptr<MaterialLibrary> _library;
QString _directory;
QString _filename;
QString _uuid;
QString _name;
QString _author;

View File

@@ -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<ModelLibrary> 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<Model>& other) const
{
try {
_library->validate(*(other->_library));
}
catch (const InvalidLibrary& e)
{
throw InvalidModel(e.what());
}
// std::map<QString, ModelProperty> _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);
}
}

View File

@@ -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<Model>& other) const;
private:
std::shared_ptr<ModelLibrary> _library;
ModelType _type;
QString _name;
QString _directory;
QString _filename;
QString _uuid;
QString _description;
QString _url;

View File

@@ -24,6 +24,8 @@
#include <string>
#endif
#include <QFileInfo>
#include <App/Application.h>
#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<std::map<QString, std::shared_ptr<Model>>>();
}
@@ -131,9 +67,11 @@ std::shared_ptr<Model> ModelLibrary::getModelByPath(const QString& path) const
std::shared_ptr<Model> ModelLibrary::addModel(const Model& model, const QString& path)
{
QString filePath = getRelativePath(path);
QFileInfo info(filePath);
std::shared_ptr<Model> newModel = std::make_shared<Model>(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<ModelTreeNode> child = std::make_shared<ModelTreeNode>();
child->setUUID(model->getUUID());
child->setData(model);
(*node)[itp] = child;
}

View File

@@ -32,68 +32,28 @@
#include <Mod/Material/MaterialGlobal.h>
#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<ModelLibrary>
{
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<Materials::ModelLibrary>)
#endif // MATERIAL_MODELLIBRARY_H

View File

@@ -26,118 +26,141 @@
#include <QDirIterator>
#include <QMutexLocker>
#include <App/Application.h>
#include <Base/Console.h>
#include "Model.h"
#include "ModelLoader.h"
#include "ModelManager.h"
#include "ModelManagerLocal.h"
using namespace Materials;
std::shared_ptr<std::list<std::shared_ptr<ModelLibrary>>> ModelManager::_libraryList = nullptr;
std::shared_ptr<std::map<QString, std::shared_ptr<Model>>> ModelManager::_modelMap = nullptr;
QMutex ModelManager::_mutex;
TYPESYSTEM_SOURCE(Materials::ModelManager, Base::BaseClass)
QMutex ModelManager::_mutex;
ModelManager* ModelManager::_manager = nullptr;
std::unique_ptr<ModelManagerLocal> 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<std::map<QString, std::shared_ptr<Model>>>();
if (_libraryList == nullptr) {
_libraryList = std::make_shared<std::list<std::shared_ptr<ModelLibrary>>>();
}
// 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<ModelManagerLocal>();
}
}
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<std::list<std::shared_ptr<ModelLibrary>>> ModelManager::getLibraries()
{
return _localManager->getLibraries();
}
std::shared_ptr<std::list<std::shared_ptr<ModelLibrary>>> 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<std::vector<std::tuple<QString, QString, QString>>>
ModelManager::libraryModels(const QString& libraryName)
{
return _localManager->libraryModels(libraryName);
}
bool ModelManager::isLocalLibrary(const QString& libraryName)
{
return true;
}
std::shared_ptr<std::map<QString, std::shared_ptr<Model>>> ModelManager::getModels()
{
return _localManager->getModels();
}
std::shared_ptr<std::map<QString, std::shared_ptr<Model>>> ModelManager::getLocalModels()
{
return _localManager->getModels();
}
std::shared_ptr<Model> 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<Model> 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<Model> 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<ModelLibrary> 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)

View File

@@ -24,6 +24,7 @@
#include <memory>
#include <Base/Parameter.h>
#include <Mod/Material/MaterialGlobal.h>
#include <QMutex>
@@ -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<std::list<std::shared_ptr<ModelLibrary>>> getModelLibraries()
{
return _libraryList;
}
std::shared_ptr<std::map<QString, std::shared_ptr<Model>>> getModels()
{
return _modelMap;
}
std::shared_ptr<std::list<std::shared_ptr<ModelLibrary>>> getLibraries();
std::shared_ptr<std::list<std::shared_ptr<ModelLibrary>>> 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<std::vector<std::tuple<QString, QString, QString>>>
libraryModels(const QString& libraryName);
bool isLocalLibrary(const QString& libraryName);
std::shared_ptr<std::map<QString, std::shared_ptr<Model>>> getModels();
std::shared_ptr<std::map<QString, std::shared_ptr<Model>>> getLocalModels();
std::shared_ptr<std::map<QString, std::shared_ptr<ModelTreeNode>>>
getModelTree(std::shared_ptr<ModelLibrary> 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<std::list<std::shared_ptr<ModelLibrary>>> _libraryList;
static std::shared_ptr<std::map<QString, std::shared_ptr<Model>>> _modelMap;
static ModelManager* _manager;
static std::unique_ptr<ModelManagerLocal> _localManager;
static QMutex _mutex;
};
} // namespace Materials
#endif // MATERIAL_MODELMANAGER_H
#endif // MATERIAL_MODELMANAGER_H

View File

@@ -0,0 +1,219 @@
/***************************************************************************
* Copyright (c) 2024 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#endif
#include <QDirIterator>
#include <QMutexLocker>
#include <Base/Console.h>
#include "Model.h"
#include "ModelLoader.h"
#include "ModelManagerLocal.h"
using namespace Materials;
std::shared_ptr<std::list<std::shared_ptr<ModelLibrary>>> ModelManagerLocal::_libraryList = nullptr;
std::shared_ptr<std::map<QString, std::shared_ptr<Model>>> 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<std::map<QString, std::shared_ptr<Model>>>();
if (_libraryList == nullptr) {
_libraryList = std::make_shared<std::list<std::shared_ptr<ModelLibrary>>>();
}
// 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<std::list<std::shared_ptr<ModelLibrary>>> 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<ModelLibrary>(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<std::vector<std::tuple<QString, QString, QString>>>
ModelManagerLocal::libraryModels(const QString& libraryName)
{
auto models = std::make_shared<std::vector<std::tuple<QString, QString, QString>>>();
for (auto& it : *_modelMap) {
// This is needed to resolve cyclic dependencies
if (it.second->getLibrary()->getName() == libraryName) {
models->push_back(
std::tuple<QString, QString, QString>(it.first, it.second->getDirectory(), it.second->getName()));
}
}
return models;
}
std::shared_ptr<Model> 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<Model> 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<Model> 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<ModelLibrary> ModelManagerLocal::getLibrary(const QString& name) const
{
for (auto& library : *_libraryList) {
if (library->getName() == name) {
return library;
}
}
throw LibraryNotFound();
}

View File

@@ -0,0 +1,87 @@
/***************************************************************************
* Copyright (c) 2024 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_MODELMANAGERLOCAL_H
#define MATERIAL_MODELMANAGERLOCAL_H
#include <memory>
#include <Mod/Material/MaterialGlobal.h>
#include <QMutex>
#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<std::list<std::shared_ptr<ModelLibrary>>> 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<std::vector<std::tuple<QString, QString, QString>>>
libraryModels(const QString& libraryName);
std::shared_ptr<std::map<QString, std::shared_ptr<Model>>> getModels()
{
return _modelMap;
}
std::shared_ptr<std::map<QString, std::shared_ptr<ModelTreeNode>>>
getModelTree(std::shared_ptr<ModelLibrary> library, ModelFilter filter = ModelFilter_None) const
{
return library->getModelTree(filter);
}
std::shared_ptr<Model> getModel(const QString& uuid) const;
std::shared_ptr<Model> getModelByPath(const QString& path) const;
std::shared_ptr<Model> getModelByPath(const QString& path, const QString& lib) const;
std::shared_ptr<ModelLibrary> getLibrary(const QString& name) const;
static bool isModel(const QString& file);
private:
static void initLibraries();
static std::shared_ptr<std::list<std::shared_ptr<ModelLibrary>>> _libraryList;
static std::shared_ptr<std::map<QString, std::shared_ptr<Model>>> _modelMap;
static QMutex _mutex;
};
} // namespace Materials
#endif // MATERIAL_MODELMANAGERLOCAL_H

View File

@@ -10,7 +10,7 @@
FatherInclude="Base/BaseClassPy.h"
FatherNamespace="Base"
Constructor="true"
Delete="true">
Delete="false">
<Documentation>
<Author Licence="LGPL" Name="DavidCarter" EMail="dcarter@davidcarter.ca" />
<UserDocu>Material model descriptions.</UserDocu>
@@ -31,6 +31,12 @@
</Documentation>
<Parameter Name="ModelLibraries" Type="List"/>
</Attribute>
<Attribute Name="LocalModelLibraries" ReadOnly="true">
<Documentation>
<UserDocu>List of local model libraries.</UserDocu>
</Documentation>
<Parameter Name="LocalModelLibraries" Type="List"/>
</Attribute>
<Attribute Name="Models" ReadOnly="true">
<Documentation>
<UserDocu>List of model libraries.</UserDocu>

View File

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

View File

@@ -15,35 +15,64 @@
<Author Licence="LGPL" Name="DavidCarter" EMail="dcarter@davidcarter.ca" />
<UserDocu>Material property descriptions.</UserDocu>
</Documentation>
<Attribute Name="Name" ReadOnly="true">
<Attribute Name="Name" ReadOnly="false">
<Documentation>
<UserDocu>Property name.</UserDocu>
</Documentation>
<Parameter Name="Name" Type="String"/>
</Attribute>
<Attribute Name="Type" ReadOnly="true">
<Attribute Name="DisplayName" ReadOnly="false">
<Documentation>
<UserDocu>Property display friendly name.</UserDocu>
</Documentation>
<Parameter Name="DisplayName" Type="String"/>
</Attribute>
<Attribute Name="Type" ReadOnly="false">
<Documentation>
<UserDocu>Property type.</UserDocu>
</Documentation>
<Parameter Name="Type" Type="String"/>
</Attribute>
<Attribute Name="Units" ReadOnly="true">
<Attribute Name="Units" ReadOnly="false">
<Documentation>
<UserDocu>Property units category.</UserDocu>
</Documentation>
<Parameter Name="Units" Type="String"/>
</Attribute>
<Attribute Name="URL" ReadOnly="true">
<Attribute Name="URL" ReadOnly="false">
<Documentation>
<UserDocu>URL to a detailed description of the property.</UserDocu>
</Documentation>
<Parameter Name="URL" Type="String"/>
</Attribute>
<Attribute Name="Description" ReadOnly="true">
<Attribute Name="Description" ReadOnly="false">
<Documentation>
<UserDocu>Property description.</UserDocu>
</Documentation>
<Parameter Name="Description" Type="String"/>
</Attribute>
<Attribute Name="Columns" ReadOnly="true">
<Documentation>
<UserDocu>List of array columns.</UserDocu>
</Documentation>
<Parameter Name="Columns" Type="List"/>
</Attribute>
<Attribute Name="Inheritance" ReadOnly="true">
<Documentation>
<UserDocu>UUID of the model in which the property is defined.</UserDocu>
</Documentation>
<Parameter Name="Inheritance" Type="String"/>
</Attribute>
<Attribute Name="Inherited" ReadOnly="true">
<Documentation>
<UserDocu>True if the property is inherited.</UserDocu>
</Documentation>
<Parameter Name="Inherited" Type="Boolean"/>
</Attribute>
<Methode Name="addColumn" ReadOnly="false">
<Documentation>
<UserDocu>Add a model property column.</UserDocu>
</Documentation>
</Methode>
</PythonExport>
</GenerateModel>
</GenerateModel>

View File

@@ -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 << "<ModelProperty object at " << getModelPropertyPtr() << ">";
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<ModelPropertyPy*>(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;
}
}

View File

@@ -33,13 +33,19 @@
</Documentation>
<Parameter Name="LibraryIcon" Type="String"/>
</Attribute>
<Attribute Name="Name" ReadOnly="true">
<Attribute Name="Name" ReadOnly="false">
<Documentation>
<UserDocu>Model name.</UserDocu>
</Documentation>
<Parameter Name="Name" Type="String"/>
</Attribute>
<Attribute Name="Directory" ReadOnly="true">
<Attribute Name="Type" ReadOnly="false">
<Documentation>
<UserDocu>Model type.</UserDocu>
</Documentation>
<Parameter Name="Type" Type="String"/>
</Attribute>
<Attribute Name="Directory" ReadOnly="false">
<Documentation>
<UserDocu>Model directory.</UserDocu>
</Documentation>
@@ -51,19 +57,19 @@
</Documentation>
<Parameter Name="UUID" Type="String"/>
</Attribute>
<Attribute Name="Description" ReadOnly="true">
<Attribute Name="Description" ReadOnly="false">
<Documentation>
<UserDocu>Description of the model.</UserDocu>
</Documentation>
<Parameter Name="Description" Type="String"/>
</Attribute>
<Attribute Name="URL" ReadOnly="true">
<Attribute Name="URL" ReadOnly="false">
<Documentation>
<UserDocu>URL to a detailed description of the model.</UserDocu>
</Documentation>
<Parameter Name="URL" Type="String"/>
</Attribute>
<Attribute Name="DOI" ReadOnly="true">
<Attribute Name="DOI" ReadOnly="false">
<Documentation>
<UserDocu>Digital Object Identifier (see https://doi.org/)</UserDocu>
</Documentation>
@@ -81,5 +87,15 @@
</Documentation>
<Parameter Name="Properties" Type="Dict"/>
</Attribute>
<Methode Name="addInheritance" ReadOnly="false">
<Documentation>
<UserDocu>Add an inherited model.</UserDocu>
</Documentation>
</Methode>
<Methode Name="addProperty" ReadOnly="false">
<Documentation>
<UserDocu>Add a model property.</UserDocu>
</Documentation>
</Methode>
</PythonExport>
</GenerateModel>

View File

@@ -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 << "<Model object at " << getModelPtr() << ">";
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<std::string, Model*> *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<ModelPropertyPy*>(object)->getModelPropertyPtr();
getModelPtr()->addProperty(*property);
Py_INCREF(Py_None);
return Py_None;
}
PyObject* ModelPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;

View File

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

View File

@@ -0,0 +1,75 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include "PreCompiled.h"
#include <QVariant>
#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<Base::Quantity>()) {
return new Base::QuantityPy(new Base::Quantity(value.value<Base::Quantity>()));
}
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<QList<QVariant>>()) {
return Py::new_reference_to(getList(value));
}
throw UnknownValueType();
}
Py::List Materials::getList(const QVariant& value)
{
auto listValue = value.value<QList<QVariant>>();
Py::List list;
for (auto& it : listValue) {
list.append(Py::Object(_pyObjectFromVariant(it)));
}
return list;
}

View File

@@ -0,0 +1,42 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_PYVARIANTS_H
#define MATERIAL_PYVARIANTS_H
#include <QMetaType>
#include <Base/Quantity.h>
#include <Base/QuantityPy.h>
#include <CXX/Objects.hxx>
#include <Gui/MetaTypes.h>
#include <Mod/Material/MaterialGlobal.h>
namespace Materials
{
extern MaterialsExport PyObject* _pyObjectFromVariant(const QVariant& value);
extern MaterialsExport Py::List getList(const QVariant& value);
} // namespace Materials
#endif // MATERIAL_PYVARIANTS_H

View File

@@ -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());
}

View File

@@ -63,7 +63,7 @@ Array2D::Array2D(const QString& propertyName,
}
if (_property) {
_value =
std::static_pointer_cast<Materials::Material2DArray>(_property->getMaterialValue());
std::static_pointer_cast<Materials::Array2D>(_property->getMaterialValue());
setWindowTitle(_property->getDisplayName());
}
else {

View File

@@ -64,7 +64,7 @@ private:
std::unique_ptr<Ui_Array2D> ui;
std::shared_ptr<Materials::Material> _material;
std::shared_ptr<Materials::MaterialProperty> _property;
std::shared_ptr<Materials::Material2DArray> _value;
std::shared_ptr<Materials::Array2D> _value;
QAction _deleteAction;

View File

@@ -59,7 +59,7 @@ Array3D::Array3D(const QString& propertyName,
}
if (_property) {
_value =
std::static_pointer_cast<Materials::Material3DArray>(_property->getMaterialValue());
std::static_pointer_cast<Materials::Array3D>(_property->getMaterialValue());
}
else {
_value = nullptr;

View File

@@ -67,7 +67,7 @@ private:
std::unique_ptr<Ui_Array3D> ui;
std::shared_ptr<Materials::Material> _material;
std::shared_ptr<Materials::MaterialProperty> _property;
std::shared_ptr<Materials::Material3DArray> _value;
std::shared_ptr<Materials::Array3D> _value;
QAction _deleteDepthAction;
QAction _delete2DAction;

View File

@@ -47,7 +47,7 @@ AbstractArrayModel::AbstractArrayModel(QObject* parent)
Array2DModel::Array2DModel(const std::shared_ptr<Materials::MaterialProperty>& property,
const std::shared_ptr<Materials::Material2DArray>& value,
const std::shared_ptr<Materials::Array2D>& 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<Materials::MaterialProperty>& property,
const std::shared_ptr<Materials::Material3DArray>& value,
const std::shared_ptr<Materials::Array3D>& 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<Materials::MaterialProperty>& property,
const std::shared_ptr<Materials::Material3DArray>& value,
const std::shared_ptr<Materials::Array3D>& value,
QObject* parent)
: AbstractArrayModel(parent)
, _property(property)

View File

@@ -48,7 +48,7 @@ class Array2DModel: public AbstractArrayModel
{
public:
explicit Array2DModel(const std::shared_ptr<Materials::MaterialProperty>& property = nullptr,
const std::shared_ptr<Materials::Material2DArray>& value = nullptr,
const std::shared_ptr<Materials::Array2D>& value = nullptr,
QObject* parent = nullptr);
~Array2DModel() override = default;
@@ -71,7 +71,7 @@ public:
private:
std::shared_ptr<Materials::MaterialProperty> _property;
std::shared_ptr<Materials::Material2DArray> _value;
std::shared_ptr<Materials::Array2D> _value;
};
class Array3DDepthModel: public AbstractArrayModel
@@ -79,7 +79,7 @@ class Array3DDepthModel: public AbstractArrayModel
public:
explicit Array3DDepthModel(
const std::shared_ptr<Materials::MaterialProperty>& property = nullptr,
const std::shared_ptr<Materials::Material3DArray>& value = nullptr,
const std::shared_ptr<Materials::Array3D>& value = nullptr,
QObject* parent = nullptr);
~Array3DDepthModel() override = default;
@@ -106,14 +106,14 @@ public:
private:
std::shared_ptr<Materials::MaterialProperty> _property;
std::shared_ptr<Materials::Material3DArray> _value;
std::shared_ptr<Materials::Array3D> _value;
};
class Array3DModel: public AbstractArrayModel
{
public:
explicit Array3DModel(const std::shared_ptr<Materials::MaterialProperty>& property = nullptr,
const std::shared_ptr<Materials::Material3DArray>& value = nullptr,
const std::shared_ptr<Materials::Array3D>& value = nullptr,
QObject* parent = nullptr);
~Array3DModel() override = default;
@@ -138,7 +138,7 @@ public:
private:
std::shared_ptr<Materials::MaterialProperty> _property;
std::shared_ptr<Materials::Material3DArray> _value;
std::shared_ptr<Materials::Array3D> _value;
};
} // namespace MatGui

View File

@@ -61,8 +61,6 @@ void CmdMaterialEdit::activated(int iMsg)
{
Q_UNUSED(iMsg);
Base::Console().Log("Material_Edit\n");
static QPointer<QDialog> dlg = nullptr;
if (!dlg) {
dlg = new MatGui::MaterialsEditor(Gui::getMainWindow());

View File

@@ -151,7 +151,7 @@ void DlgInspectAppearance::update(std::vector<Gui::ViewProvider*>& 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<Gui::ViewProvider*>& 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()));

View File

@@ -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<Gui::ViewProvider*>& views)
{
clipboardText = QLatin1String("");
clipboardText = QStringLiteral("");
clipboardIndent = 0;
App::Document* doc = App::GetApplication().getActiveDocument();
if (doc) {
@@ -166,7 +166,7 @@ void DlgInspectMaterial::update(std::vector<Gui::ViewProvider*>& 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<Gui::ViewProvider*>& 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 {

View File

@@ -60,8 +60,6 @@ public:
private:
std::unique_ptr<Ui_DlgInspectMaterial> ui;
Materials::MaterialManager materialManager;
Materials::ModelManager modelManager;
QString clipboardText;
int clipboardIndent;

View File

@@ -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<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(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<Materials::MaterialTreeNode> nodePtr = mat.second;
if (nodePtr->getType() == Materials::MaterialTreeNode::DataNode) {
if (nodePtr->getType() == Materials::MaterialTreeNode::NodeType::DataNode) {
std::shared_ptr<Materials::Material> 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)

View File

@@ -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_MaterialSave> ui;
Materials::MaterialManager _manager;
std::shared_ptr<Materials::Material> _material;
bool _saveInherited;
QString _selectedPath;

View File

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

View File

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

View File

@@ -46,6 +46,7 @@
#include <Gui/WaitCursor.h>
#include <Mod/Material/App/Exceptions.h>
#include <Mod/Material/App/MaterialLibrary.h>
#include <Mod/Material/App/ModelManager.h>
#include <Mod/Material/App/ModelUuids.h>
@@ -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<ParameterGrp>& para
void MaterialsEditor::addMaterials(
QStandardItem& parent,
const std::shared_ptr<std::map<QString, std::shared_ptr<Materials::MaterialTreeNode>>>
modelTree,
materialTree,
const QIcon& folderIcon,
const QIcon& icon,
const Base::Reference<ParameterGrp>& 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<Materials::MaterialTreeNode> 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<Materials::Material>&
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&) {

View File

@@ -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<Materials::Material>& material);
@@ -114,8 +110,6 @@ protected:
private:
std::unique_ptr<Ui_MaterialsEditor> ui;
Materials::MaterialManager _materialManager;
Materials::ModelManager _modelManager;
std::shared_ptr<Materials::Material> _material;
AppearancePreview* _rendered;
bool _materialSelected;

View File

@@ -239,7 +239,7 @@ void ModelSelect::addModels(
auto tree = ui->treeModels;
for (auto& mod : *modelTree) {
std::shared_ptr<Materials::ModelTreeNode> 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<Materials::Model> 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());

View File

@@ -91,14 +91,9 @@ private:
void setColumnWidths(QTableView* table);
void updateModelProperties(std::shared_ptr<Materials::Model> model);
void createModelProperties();
Materials::ModelManager& getModelManager()
{
return _modelManager;
}
Materials::ModelFilter _filter;
std::unique_ptr<Ui_ModelSelect> ui;
Materials::ModelManager _modelManager;
QString _selected;
std::list<QString> _favorites;
std::list<QString> _recents;

View File

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

View File

@@ -57,12 +57,12 @@ protected:
QDir libDir(libPath);
libDir.removeRecursively(); // Clear old run data
libDir.mkdir(libPath);
_library = std::make_shared<Materials::MaterialLibrary>(QStringLiteral("Testing"),
_library = std::make_shared<Materials::MaterialLibraryLocal>(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<Materials::MaterialLibrary> _library;
std::shared_ptr<Materials::MaterialLibraryLocal> _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<Materials::Material2DArray &>(*array2d).columns(), 2);
EXPECT_EQ(dynamic_cast<Materials::Array2D &>(*array2d).columns(), 2);
EXPECT_TRUE(testMaterial->hasPhysicalProperty(QStringLiteral("TestArray2D3Column")));
auto array2d3Column = testMaterial->getPhysicalProperty(QStringLiteral("TestArray2D3Column"))->getMaterialValue();
EXPECT_TRUE(array2d3Column);
EXPECT_EQ(dynamic_cast<Materials::Material2DArray &>(*array2d3Column).columns(), 3);
EXPECT_EQ(dynamic_cast<Materials::Array2D &>(*array2d3Column).columns(), 3);
EXPECT_TRUE(testMaterial->hasPhysicalProperty(QStringLiteral("TestArray3D")));
auto array3d = testMaterial->getPhysicalProperty(QStringLiteral("TestArray3D"))->getMaterialValue();
EXPECT_TRUE(array3d);
EXPECT_EQ(dynamic_cast<Materials::Material3DArray &>(*array3d).columns(), 2);
EXPECT_EQ(dynamic_cast<Materials::Array3D &>(*array3d).columns(), 2);
}
// clang-format on

View File

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

View File

@@ -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<Base::Quantity>());
@@ -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<Materials::Material2DArray>(prop.getMaterialValue());
auto array = std::static_pointer_cast<Materials::Array2D>(prop.getMaterialValue());
EXPECT_EQ(array->rows(), 0);
auto variant = prop.getValue(); // Throw an error?
EXPECT_FALSE(variant.canConvert<QString>());
@@ -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<Materials::Material3DArray>(prop.getMaterialValue());
auto array = std::static_pointer_cast<Materials::Array3D>(prop.getMaterialValue());
EXPECT_EQ(array->depth(), 0);
auto variant = prop.getValue(); // Throw an error?
EXPECT_FALSE(variant.canConvert<QString>());
@@ -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;

View File

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

View File

@@ -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<Materials::Material2DArray &>(*array2d).columns(), 2);
EXPECT_EQ(dynamic_cast<Materials::Array2D &>(*array2d).columns(), 2);
EXPECT_TRUE(testMaterial.hasPhysicalProperty(QStringLiteral("TestArray2D3Column")));
auto array2d3Column = testMaterial.getPhysicalProperty(QStringLiteral("TestArray2D3Column"))->getMaterialValue();
EXPECT_TRUE(array2d3Column);
EXPECT_EQ(dynamic_cast<Materials::Material2DArray &>(*array2d3Column).columns(), 3);
EXPECT_EQ(dynamic_cast<Materials::Array2D &>(*array2d3Column).columns(), 3);
EXPECT_TRUE(testMaterial.hasPhysicalProperty(QStringLiteral("TestArray3D")));
auto array3d = testMaterial.getPhysicalProperty(QStringLiteral("TestArray3D"))->getMaterialValue();
EXPECT_TRUE(array3d);
EXPECT_EQ(dynamic_cast<Materials::Material3DArray &>(*array3d).columns(), 2);
EXPECT_EQ(dynamic_cast<Materials::Array3D &>(*array3d).columns(), 2);
}
// clang-format on

View File

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