// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2023 David Carter * * * * This file is part of FreeCAD. * * * * FreeCAD is free software: you can redistribute it and/or modify it * * under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation, either version 2.1 of the * * License, or (at your option) any later version. * * * * FreeCAD is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with FreeCAD. If not, see * * . * * * **************************************************************************/ #include #include #include #include #include #include "Materials.h" #include "Array2DPy.h" #include "Array3DPy.h" #include "Exceptions.h" #include "MaterialLibrary.h" #include "MaterialPy.h" #include "MaterialValue.h" #include "PyVariants.h" #include "ModelPropertyPy.h" #include "MaterialPropertyPy.h" #include "MaterialPy.cpp" using namespace Materials; // returns a string which represents the object e.g. when printed in python std::string MaterialPy::representation() const { std::ostringstream str; str << ""; return str.str(); } PyObject* MaterialPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper { // never create such objects with the constructor return new MaterialPy(new Material()); } // constructor method int MaterialPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) { return 0; } Py::String MaterialPy::getLibraryName() const { auto library = getMaterialPtr()->getLibrary(); if (library->isLocal()) { auto materialLibrary = reinterpret_cast&>(library); return {materialLibrary ? materialLibrary->getName().toStdString() : ""}; } return ""; } Py::String MaterialPy::getLibraryRoot() const { auto library = getMaterialPtr()->getLibrary(); if (library->isLocal()) { auto materialLibrary = reinterpret_cast&>(library); return {materialLibrary ? materialLibrary->getDirectoryPath().toStdString() : ""}; } return ""; } Py::Object MaterialPy::getLibraryIcon() const { auto library = getMaterialPtr()->getLibrary(); if (library->isLocal()) { auto materialLibrary = reinterpret_cast&>(library); auto icon = materialLibrary->getIcon(); if (icon.isNull()) { return Py::Bytes(); } return Py::Bytes(icon.data(), icon.size()); } return Py::Bytes(); } Py::String MaterialPy::getName() const { return {getMaterialPtr()->getName().toStdString()}; } void MaterialPy::setName(Py::String arg) { getMaterialPtr()->setName(QString::fromStdString(arg)); } 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()}; } Py::String MaterialPy::getDescription() const { return {getMaterialPtr()->getDescription().toStdString()}; } void MaterialPy::setDescription(Py::String arg) { getMaterialPtr()->setDescription(QString::fromStdString(arg)); } Py::String MaterialPy::getURL() const { return {getMaterialPtr()->getURL().toStdString()}; } void MaterialPy::setURL(Py::String arg) { getMaterialPtr()->setURL(QString::fromStdString(arg)); } Py::String MaterialPy::getReference() const { return {getMaterialPtr()->getReference().toStdString()}; } void MaterialPy::setReference(Py::String arg) { getMaterialPtr()->setReference(QString::fromStdString(arg)); } 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()}; } Py::String MaterialPy::getAuthor() const { return {getMaterialPtr()->getAuthor().toStdString()}; } void MaterialPy::setAuthor(Py::String arg) { getMaterialPtr()->setAuthor(QString::fromStdString(arg)); } Py::String MaterialPy::getLicense() const { return {getMaterialPtr()->getLicense().toStdString()}; } void MaterialPy::setLicense(Py::String arg) { getMaterialPtr()->setLicense(QString::fromStdString(arg)); } Py::List MaterialPy::getPhysicalModels() const { auto models = getMaterialPtr()->getPhysicalModels(); Py::List list; for (auto it : *models) { list.append(Py::String(it.toStdString())); } return list; } Py::List MaterialPy::getAppearanceModels() const { auto models = getMaterialPtr()->getAppearanceModels(); Py::List list; for (auto it : *models) { list.append(Py::String(it.toStdString())); } return list; } Py::List MaterialPy::getTags() const { auto& tags = getMaterialPtr()->getTags(); Py::List list; for (auto it : tags) { list.append(Py::String(it.toStdString())); } return list; } PyObject* MaterialPy::getCustomAttributes(const char* /*attr*/) const { return nullptr; } int MaterialPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) { return 0; } PyObject* MaterialPy::addPhysicalModel(PyObject* args) { char* uuid; if (!PyArg_ParseTuple(args, "s", &uuid)) { return nullptr; } getMaterialPtr()->addPhysical(QString::fromStdString(uuid)); Py_INCREF(Py_None); return Py_None; } PyObject* MaterialPy::removePhysicalModel(PyObject* args) { char* uuid; if (!PyArg_ParseTuple(args, "s", &uuid)) { return nullptr; } getMaterialPtr()->removePhysical(QString::fromStdString(uuid)); Py_INCREF(Py_None); return Py_None; } PyObject* MaterialPy::hasPhysicalModel(PyObject* args) { char* uuid; if (!PyArg_ParseTuple(args, "s", &uuid)) { return nullptr; } bool hasProperty = getMaterialPtr()->hasPhysicalModel(QString::fromStdString(uuid)); return PyBool_FromLong(hasProperty ? 1 : 0); } PyObject* MaterialPy::addAppearanceModel(PyObject* args) { char* uuid; if (!PyArg_ParseTuple(args, "s", &uuid)) { return nullptr; } getMaterialPtr()->addAppearance(QString::fromStdString(uuid)); Py_INCREF(Py_None); return Py_None; } PyObject* MaterialPy::removeAppearanceModel(PyObject* args) { char* uuid; if (!PyArg_ParseTuple(args, "s", &uuid)) { return nullptr; } getMaterialPtr()->removeAppearance(QString::fromStdString(uuid)); Py_INCREF(Py_None); return Py_None; } PyObject* MaterialPy::hasAppearanceModel(PyObject* args) { char* uuid; if (!PyArg_ParseTuple(args, "s", &uuid)) { return nullptr; } bool hasProperty = getMaterialPtr()->hasAppearanceModel(QString::fromStdString(uuid)); return PyBool_FromLong(hasProperty ? 1 : 0); } PyObject* MaterialPy::isPhysicalModelComplete(PyObject* args) { char* name; if (!PyArg_ParseTuple(args, "s", &name)) { return nullptr; } bool isComplete = getMaterialPtr()->isPhysicalModelComplete(QString::fromStdString(name)); return PyBool_FromLong(isComplete ? 1 : 0); } PyObject* MaterialPy::isAppearanceModelComplete(PyObject* args) { char* name; if (!PyArg_ParseTuple(args, "s", &name)) { return nullptr; } bool isComplete = getMaterialPtr()->isAppearanceModelComplete(QString::fromStdString(name)); return PyBool_FromLong(isComplete ? 1 : 0); } PyObject* MaterialPy::hasPhysicalProperty(PyObject* args) { char* name; if (!PyArg_ParseTuple(args, "s", &name)) { return nullptr; } bool hasProperty = getMaterialPtr()->hasPhysicalProperty(QString::fromStdString(name)); return PyBool_FromLong(hasProperty ? 1 : 0); } PyObject* MaterialPy::hasAppearanceProperty(PyObject* args) { char* name; if (!PyArg_ParseTuple(args, "s", &name)) { return nullptr; } bool hasProperty = getMaterialPtr()->hasAppearanceProperty(QString::fromStdString(name)); return PyBool_FromLong(hasProperty ? 1 : 0); } PyObject* MaterialPy::hasLegacyProperties(PyObject* args) { if (!PyArg_ParseTuple(args, "")) { return nullptr; } bool hasProperty = getMaterialPtr()->hasLegacyProperties(); return PyBool_FromLong(hasProperty ? 1 : 0); } Py::Dict MaterialPy::getProperties() const { Py::Dict dict; // Maintain backwards compatibility dict.setItem(Py::String("CardName"), Py::String(getMaterialPtr()->getName().toStdString())); dict.setItem(Py::String("AuthorAndLicense"), Py::String(getMaterialPtr()->getAuthorAndLicense().toStdString())); dict.setItem(Py::String("Author"), Py::String(getMaterialPtr()->getAuthor().toStdString())); dict.setItem(Py::String("License"), Py::String(getMaterialPtr()->getLicense().toStdString())); dict.setItem(Py::String("Name"), Py::String(getMaterialPtr()->getName().toStdString())); dict.setItem(Py::String("Description"), Py::String(getMaterialPtr()->getDescription().toStdString())); dict.setItem(Py::String("ReferenceSource"), Py::String(getMaterialPtr()->getReference().toStdString())); dict.setItem(Py::String("SourceURL"), Py::String(getMaterialPtr()->getURL().toStdString())); auto properties = getMaterialPtr()->getPhysicalProperties(); for (auto& it : properties) { QString key = it.first; auto materialProperty = it.second; if (!materialProperty->isNull()) { auto value = materialProperty->getDictionaryString(); dict.setItem(Py::String(key.toStdString()), Py::String(value.toStdString())); } } properties = getMaterialPtr()->getAppearanceProperties(); for (auto& it : properties) { QString key = it.first; auto materialProperty = it.second; if (!materialProperty->isNull()) { auto value = materialProperty->getDictionaryString(); dict.setItem(Py::String(key.toStdString()), Py::String(value.toStdString())); } } auto legacy = getMaterialPtr()->getLegacyProperties(); for (auto& it : legacy) { auto key = it.first; auto value = it.second; if (!value.isEmpty()) { dict.setItem(Py::String(key.toStdString()), Py::String(value.toStdString())); } } return dict; } Py::Dict MaterialPy::getPhysicalProperties() const { Py::Dict dict; auto properties = getMaterialPtr()->getPhysicalProperties(); for (auto& it : properties) { QString key = it.first; auto materialProperty = it.second; if (!materialProperty->isNull()) { auto value = materialProperty->getDictionaryString(); dict.setItem(Py::String(key.toStdString()), Py::String(value.toStdString())); } } return dict; } Py::Dict MaterialPy::getAppearanceProperties() const { Py::Dict dict; auto properties = getMaterialPtr()->getAppearanceProperties(); for (auto& it : properties) { QString key = it.first; auto materialProperty = it.second; if (!materialProperty->isNull()) { auto value = materialProperty->getDictionaryString(); dict.setItem(Py::String(key.toStdString()), Py::String(value.toStdString())); } } return dict; } Py::Dict MaterialPy::getLegacyProperties() const { Py::Dict dict; auto legacy = getMaterialPtr()->getLegacyProperties(); for (auto& it : legacy) { auto key = it.first; auto value = it.second; if (!value.isEmpty()) { dict.setItem(Py::String(key.toStdString()), Py::String(value.toStdString())); } } return dict; } PyObject* MaterialPy::getPhysicalValue(PyObject* args) { char* name; if (!PyArg_ParseTuple(args, "s", &name)) { return nullptr; } if (!getMaterialPtr()->hasPhysicalProperty(QString::fromStdString(name))) { Py_RETURN_NONE; } auto property = getMaterialPtr()->getPhysicalProperty(QString::fromStdString(name)); if (!property) { Py_RETURN_NONE; } if (property->getType() == MaterialValue::Array2D) { auto value = std::static_pointer_cast(property->getMaterialValue()); return new Array2DPy(new Array2D(*value)); } if (property->getType() == MaterialValue::Array3D) { auto value = std::static_pointer_cast(property->getMaterialValue()); return new Array3DPy(new Array3D(*value)); } QVariant value = property->getValue(); return _pyObjectFromVariant(value); } PyObject* MaterialPy::setPhysicalValue(PyObject* args) { char* name; char* value; if (!PyArg_ParseTuple(args, "ss", &name, &value)) { return nullptr; } getMaterialPtr()->setPhysicalValue(QString::fromStdString(name), QString::fromStdString(value)); Py_INCREF(Py_None); return Py_None; } PyObject* MaterialPy::getAppearanceValue(PyObject* args) { char* name; if (!PyArg_ParseTuple(args, "s", &name)) { return nullptr; } if (!getMaterialPtr()->hasAppearanceProperty(QString::fromStdString(name))) { Py_RETURN_NONE; } auto property = getMaterialPtr()->getAppearanceProperty(QString::fromStdString(name)); if (!property) { Py_RETURN_NONE; } if (property->getType() == MaterialValue::Array2D) { auto value = std::static_pointer_cast(property->getMaterialValue()); return new Array2DPy(new Array2D(*value)); } if (property->getType() == MaterialValue::Array3D) { auto value = std::static_pointer_cast(property->getMaterialValue()); return new Array3DPy(new Array3D(*value)); } QVariant value = property->getValue(); return _pyObjectFromVariant(value); } PyObject* MaterialPy::setAppearanceValue(PyObject* args) { char* name; char* value; if (!PyArg_ParseTuple(args, "ss", &name, &value)) { return nullptr; } getMaterialPtr()->setAppearanceValue(QString::fromStdString(name), QString::fromStdString(value)); Py_INCREF(Py_None); return Py_None; } PyObject* MaterialPy::setValue(PyObject* args) { char* name; char* value; PyObject* listObj; PyObject* arrayObj; if (PyArg_ParseTuple(args, "ss", &name, &value)) { getMaterialPtr()->setValue(QString::fromStdString(name), QString::fromStdString(value)); Py_Return; } PyErr_Clear(); if (PyArg_ParseTuple(args, "sO!", &name, &PyList_Type, &listObj)) { QList variantList; Py::List list(listObj); for (auto itemObj : list) { Py::String item(itemObj); QString value(QString::fromStdString(item.as_string())); QVariant variant = QVariant::fromValue(value); variantList.append(variant); } getMaterialPtr()->setValue(QString::fromStdString(name), variantList); Py_Return; } PyErr_Clear(); if (PyArg_ParseTuple(args, "sO!", &name, &(Array2DPy::Type), &arrayObj)) { auto array = static_cast(arrayObj); auto shared = std::make_shared(*array->getArray2DPtr()); getMaterialPtr()->setValue(QString::fromStdString(name), shared); Py_Return; } PyErr_Clear(); if (PyArg_ParseTuple(args, "sO!", &name, &(Array3DPy::Type), &arrayObj)) { auto array = static_cast(arrayObj); auto shared = std::make_shared(*array->getArray3DPtr()); getMaterialPtr()->setValue(QString::fromStdString(name), shared); Py_Return; } PyErr_SetString(PyExc_TypeError, "Either a string, a list, or an array are expected"); return nullptr; } Py::Dict MaterialPy::getPropertyObjects() const { Py::Dict dict; auto properties = getMaterialPtr()->getPhysicalProperties(); for (auto& it : properties) { QString key = it.first; auto materialProperty = it.second; // if (materialProperty->getType() == MaterialValue::Array2D) { // auto value = std::static_pointer_cast( // materialProperty->getMaterialValue()); // dict.setItem(Py::String(key.toStdString()), // Py::Object(new Array2DPy(new Array2D(*value)), true)); // } // else if (materialProperty->getType() == MaterialValue::Array3D) { // auto value = std::static_pointer_cast( // materialProperty->getMaterialValue()); // dict.setItem(Py::String(key.toStdString()), // Py::Object(new Array3DPy(new Array3D(*value)), true)); // } // else { dict.setItem( Py::String(key.toStdString()), Py::Object(new MaterialPropertyPy(new MaterialProperty(materialProperty)), true)); // } } properties = getMaterialPtr()->getAppearanceProperties(); for (auto& it : properties) { QString key = it.first; auto materialProperty = it.second; dict.setItem( Py::String(key.toStdString()), Py::Object(new MaterialPropertyPy(new MaterialProperty(materialProperty)), true)); } return dict; } PyObject* MaterialPy::keys() { return Py::new_reference_to(this->getProperties().keys()); } PyObject* MaterialPy::values() { return Py::new_reference_to(this->getProperties().values()); } Py_ssize_t MaterialPy::sequence_length(PyObject *self) { return static_cast(self)->getProperties().size(); } PyObject* MaterialPy::sequence_item(PyObject* self, Py_ssize_t item) { Py::List keys = static_cast(self)->getProperties().keys(); return Py::new_reference_to(keys.getItem(item)); } int MaterialPy::sequence_contains(PyObject* self, PyObject* key) { return PyDict_Contains(static_cast(self)->getProperties().ptr(), key); } PyObject* MaterialPy::mapping_subscript(PyObject* self, PyObject* key) { Py::Dict dict = static_cast(self)->getProperties(); return Py::new_reference_to(dict.getItem(Py::Object(key))); }