Material: Material appearance

Uses new material system for appearance

Each feature object now has a property called ShapeMaterial that
describes its physical properties. If it has a shape, it has a
material.

The ShapeColor attribute is replaced by a ShapeAppearance attribute.
This is a material list that describes all appearance properties, not
just diffuse color. As a list in can be used for all elements of a
shape, such as edges and faces.

A new widget is provided to allow the user to select materials in a
consistent fashion. It can also launch the material editor with its
more advanced capabilities.
This commit is contained in:
David Carter
2024-03-17 18:37:56 -04:00
committed by Chris Hennes
parent 37c38acd19
commit ba20441935
121 changed files with 4682 additions and 1685 deletions

View File

@@ -36,6 +36,7 @@
#include "ModelPropertyPy.h"
#include "ModelPy.h"
#include "UUIDsPy.h"
#include "PropertyMaterial.h"
namespace Materials
{
@@ -43,9 +44,9 @@ class Module: public Py::ExtensionModule<Module>
{
public:
Module()
: Py::ExtensionModule<Module>("Material")
: Py::ExtensionModule<Module>("Materials")
{
initialize("This module is the Material module."); // register with Python
initialize("This module is the Materials module."); // register with Python
}
~Module() override = default;
@@ -60,18 +61,18 @@ PyObject* initModule()
} // namespace Materials
PyMOD_INIT_FUNC(Material)
PyMOD_INIT_FUNC(Materials)
{
PyObject* module = Materials::initModule();
Base::Console().Log("Loading Material module... done\n");
Base::Interpreter().addType(&Materials::MaterialManagerPy ::Type, module, "MaterialManager");
Base::Interpreter().addType(&Materials::MaterialPy ::Type, module, "Material");
Base::Interpreter().addType(&Materials::ModelManagerPy ::Type, module, "ModelManager");
Base::Interpreter().addType(&Materials::ModelPropertyPy ::Type, module, "ModelProperty");
Base::Interpreter().addType(&Materials::ModelPy ::Type, module, "Model");
Base::Interpreter().addType(&Materials::UUIDsPy ::Type, module, "UUIDs");
Base::Interpreter().addType(&Materials::MaterialManagerPy::Type, module, "MaterialManager");
Base::Interpreter().addType(&Materials::MaterialPy::Type, module, "Material");
Base::Interpreter().addType(&Materials::ModelManagerPy::Type, module, "ModelManager");
Base::Interpreter().addType(&Materials::ModelPropertyPy::Type, module, "ModelProperty");
Base::Interpreter().addType(&Materials::ModelPy::Type, module, "Model");
Base::Interpreter().addType(&Materials::UUIDsPy::Type, module, "UUIDs");
// Initialize types
@@ -95,5 +96,7 @@ PyMOD_INIT_FUNC(Material)
Materials::Material2DArray ::init();
Materials::Material3DArray ::init();
Materials::PropertyMaterial ::init();
PyMOD_Return(module);
}

View File

@@ -20,7 +20,7 @@ include_directories(
)
link_directories(${YAML_CPP_LIBRARY_DIR})
set(Material_LIBS
set(Materials_LIBS
${Boost_LIBRARIES}
FreeCADApp
)
@@ -28,22 +28,23 @@ set(Material_LIBS
include_directories(
${QtConcurrent_INCLUDE_DIRS}
)
list(APPEND Material_LIBS
list(APPEND Materials_LIBS
${QtConcurrent_LIBRARIES}
)
if(yaml-cpp_VERSION VERSION_LESS 0.8.0)
list(APPEND Material_LIBS
list(APPEND Materials_LIBS
${YAML_CPP_LIBRARIES}
)
else()
list(APPEND Material_LIBS
list(APPEND Materials_LIBS
yaml-cpp::yaml-cpp
)
endif()
generate_from_xml(Array2DPy)
generate_from_xml(Array3DPy)
generate_from_xml(MaterialFilterPy)
generate_from_xml(MaterialManagerPy)
generate_from_xml(MaterialPy)
generate_from_xml(ModelManagerPy)
@@ -61,6 +62,8 @@ SET(Python_SRCS
MaterialManagerPyImpl.cpp
MaterialPy.xml
MaterialPyImpl.cpp
MaterialFilterPy.xml
MaterialFilterPyImpl.cpp
ModelManagerPy.xml
ModelManagerPyImpl.cpp
ModelPropertyPy.xml
@@ -72,7 +75,7 @@ SET(Python_SRCS
)
SOURCE_GROUP("Python" FILES ${Python_SRCS})
SET(Material_SRCS
SET(Materials_SRCS
${Python_SRCS}
AppMaterial.cpp
FolderTree.h
@@ -102,19 +105,21 @@ SET(Material_SRCS
ModelUuids.h
PreCompiled.cpp
PreCompiled.h
PropertyMaterial.cpp
PropertyMaterial.h
trim.h
)
if(FREECAD_USE_PCH)
add_definitions(-D_PreComp_)
GET_MSVC_PRECOMPILED_SOURCE("PreCompiled.cpp" PCH_SRCS ${Material_SRCS})
ADD_MSVC_PRECOMPILED_HEADER(Material PreCompiled.h PreCompiled.cpp PCH_SRCS)
GET_MSVC_PRECOMPILED_SOURCE("PreCompiled.cpp" PCH_SRCS ${Materials_SRCS})
ADD_MSVC_PRECOMPILED_HEADER(Materials PreCompiled.h PreCompiled.cpp PCH_SRCS)
endif(FREECAD_USE_PCH)
add_library(Material SHARED ${Material_SRCS})
target_link_libraries(Material ${Material_LIBS})
add_library(Materials SHARED ${Materials_SRCS})
target_link_libraries(Materials ${Materials_LIBS})
SET_BIN_DIR(Material Material /Mod/Material)
SET_PYTHON_PREFIX_SUFFIX(Material)
SET_BIN_DIR(Materials Materials /Mod/Material)
SET_PYTHON_PREFIX_SUFFIX(Materials)
INSTALL(TARGETS Material DESTINATION ${CMAKE_INSTALL_LIBDIR})
INSTALL(TARGETS Materials DESTINATION ${CMAKE_INSTALL_LIBDIR})

View File

@@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* Copyright (c) 2023-2024 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
@@ -25,7 +25,9 @@
#include <App/Application.h>
#include "Exceptions.h"
#include "MaterialFilter.h"
#include "MaterialManager.h"
#include "Materials.h"
@@ -56,6 +58,18 @@ bool MaterialFilter::modelIncluded(const std::shared_ptr<Material>& material) co
return true;
}
bool MaterialFilter::modelIncluded(const QString& uuid) const
{
MaterialManager manager;
try {
auto material = manager.getMaterial(uuid);
return modelIncluded(material);
}
catch (const MaterialNotFound&) {
}
return false;
}
void MaterialFilter::addRequired(const QString& uuid)
{
// Ignore any uuids already present

View File

@@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* Copyright (c) 2023-2024 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
@@ -56,9 +56,9 @@ public:
{
return _includeFolders;
}
void setIncludeEmptyFolders(bool include)
void setIncludeEmptyFolders(bool value)
{
_includeFolders = include;
_includeFolders = value;
}
/* Indicates if we should include materials in the older format
@@ -80,10 +80,26 @@ public:
* Models only need to be included in one set.
*/
bool modelIncluded(const std::shared_ptr<Material>& material) const;
bool modelIncluded(const QString& uuid) const;
/* Add model UUIDs for required models, or models that are both required
* and complete.
*/
void addRequired(const QString& uuid);
void addRequiredComplete(const QString& uuid);
/* These functions shouldn't normally be called directly. They are
* for use by conversion methods, such as MaterialFilterPy
*/
const QSet<QString>* getRequired() const
{
return &_required;
}
const QSet<QString>* getRequiredComplete() const
{
return &_requiredComplete;
}
private:
bool _includeFolders;
bool _includeLegacy;

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="BaseClassPy"
Name="MaterialFilterPy"
Twin="MaterialFilter"
TwinPointer="MaterialFilter"
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 filters.</UserDocu>
</Documentation>
<Attribute Name="IncludeEmptyFolders" ReadOnly="false">
<Documentation>
<UserDocu>Include empty folders in the material list.</UserDocu>
</Documentation>
<Parameter Name="IncludeEmptyFolders" Type="Boolean"/>
</Attribute>
<Attribute Name="IncludeLegacy" ReadOnly="false">
<Documentation>
<UserDocu>Include legacy materials in the material list.</UserDocu>
</Documentation>
<Parameter Name="IncludeLegacy" Type="Boolean"/>
</Attribute>
<Attribute Name="RequiredModels" ReadOnly="false">
<Documentation>
<UserDocu>Materials must include the specified models.</UserDocu>
</Documentation>
<Parameter Name="RequiredModels" Type="List"/>
</Attribute>
<Attribute Name="RequiredCompleteModels" ReadOnly="false">
<Documentation>
<UserDocu>Materials must have complete versions of the specified models.</UserDocu>
</Documentation>
<Parameter Name="RequiredCompleteModels" Type="List"/>
</Attribute>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,132 @@
/***************************************************************************
* 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 "MaterialFilterPy.h"
#include "MaterialFilterPy.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 MaterialFilterPy::representation() const
{
std::stringstream str;
str << "<MaterialFilter object at " << getMaterialFilterPtr() << ">";
return str.str();
}
PyObject* MaterialFilterPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper
{
// never create such objects with the constructor
return new MaterialFilterPy(new MaterialFilter());
}
// constructor method
int MaterialFilterPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
{
return 0;
}
Py::Boolean MaterialFilterPy::getIncludeEmptyFolders() const
{
return {getMaterialFilterPtr()->includeEmptyFolders()};
}
void MaterialFilterPy::setIncludeEmptyFolders(const Py::Boolean value)
{
getMaterialFilterPtr()->setIncludeEmptyFolders(value.isTrue());
}
Py::Boolean MaterialFilterPy::getIncludeLegacy() const
{
return {getMaterialFilterPtr()->includeLegacy()};
}
void MaterialFilterPy::setIncludeLegacy(const Py::Boolean value)
{
getMaterialFilterPtr()->setIncludeLegacy(value.isTrue());
}
Py::List MaterialFilterPy::getRequiredModels() const
{
auto listValue = getMaterialFilterPtr()->getRequired();
Py::List list;
for (auto& it : *listValue) {
list.append(Py::String(it.toStdString()));
}
return list;
}
void MaterialFilterPy::setRequiredModels(Py::List value)
{
for (const auto& it : value) {
Py::String uuid(it);
getMaterialFilterPtr()->addRequired(QString::fromStdString(uuid));
}
}
Py::List MaterialFilterPy::getRequiredCompleteModels() const
{
auto listValue = getMaterialFilterPtr()->getRequiredComplete();
Py::List list;
for (auto& it : *listValue) {
list.append(Py::String(it.toStdString()));
}
return list;
}
void MaterialFilterPy::setRequiredCompleteModels(Py::List value)
{
for (const auto& it : value) {
Py::String uuid(it);
getMaterialFilterPtr()->addRequiredComplete(QString::fromStdString(uuid));
}
}
PyObject* MaterialFilterPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
int MaterialFilterPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}

View File

@@ -21,9 +21,9 @@
#include "PreCompiled.h"
#ifndef _PreComp_
#include <QVector>
#include <QDirIterator>
#include <QFileInfo>
#include <QVector>
#endif
@@ -266,7 +266,7 @@ QString MaterialLibrary::getUUIDFromPath(const QString& path) const
}
bool MaterialLibrary::materialInTree(const std::shared_ptr<Material>& material,
const MaterialFilter* filter) const
const std::shared_ptr<Materials::MaterialFilter>& filter) const
{
if (!filter) {
// If there's no filter we always include
@@ -283,7 +283,7 @@ bool MaterialLibrary::materialInTree(const std::shared_ptr<Material>& material,
}
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
MaterialLibrary::getMaterialTree(const MaterialFilter* filter) const
MaterialLibrary::getMaterialTree(const std::shared_ptr<Materials::MaterialFilter>& filter) const
{
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>> materialTree =
std::make_shared<std::map<QString, std::shared_ptr<MaterialTreeNode>>>();

View File

@@ -79,7 +79,7 @@ public:
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 MaterialFilter* filter = nullptr) const;
getMaterialTree(const std::shared_ptr<Materials::MaterialFilter>& filter) const;
bool isReadOnly() const
{
@@ -99,7 +99,7 @@ protected:
void updatePaths(const QString& oldPath, const QString& newPath);
QString getUUIDFromPath(const QString& path) const;
bool materialInTree(const std::shared_ptr<Material>& material,
const MaterialFilter* filter) const;
const std::shared_ptr<Materials::MaterialFilter>& filter) const;
bool _readOnly;
std::unique_ptr<std::map<QString, std::shared_ptr<Material>>> _materialPathMap;

View File

@@ -27,12 +27,14 @@
#include <QMutexLocker>
#include <App/Application.h>
#include <App/Material.h>
#include "Exceptions.h"
#include "MaterialConfigLoader.h"
#include "MaterialLoader.h"
#include "MaterialManager.h"
#include "ModelManager.h"
#include "ModelUuids.h"
using namespace Materials;
@@ -108,6 +110,47 @@ bool MaterialManager::isMaterial(const QFileInfo& file) const
return false;
}
std::shared_ptr<Material> MaterialManager::defaultMaterial()
{
MaterialManager manager;
ParameterGrp::handle hGrp =
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
bool randomColor = hGrp->GetBool("RandomColor", false);
float r, g, b;
if (randomColor) {
auto fMax = (float)RAND_MAX;
r = (float)rand() / fMax;
g = (float)rand() / fMax;
b = (float)rand() / fMax;
}
else {
unsigned long shcol = hGrp->GetUnsigned("DefaultShapeColor", 3435980543UL);
r = ((shcol >> 24) & 0xff) / 255.0;
g = ((shcol >> 16) & 0xff) / 255.0;
b = ((shcol >> 8) & 0xff) / 255.0;
}
int initialTransparency = hGrp->GetInt("DefaultShapeTransparency", 0);
auto material = manager.getMaterial(defaultMaterialUUID());
if (material->hasAppearanceModel(ModelUUIDs::ModelUUID_Rendering_Basic)) {
material->getAppearanceProperty(QString::fromLatin1("DiffuseColor"))
->setColor(App::Color(r, g, b));
material->getAppearanceProperty(QString::fromLatin1("Transparency"))
->setFloat((float)initialTransparency / 100.0f);
}
return material;
}
QString MaterialManager::defaultMaterialUUID()
{
// Make this a preference
return QString::fromLatin1("7f9fd73b-50c9-41d8-b7b2-575a030c1eeb");
}
std::shared_ptr<Material> MaterialManager::getMaterial(const QString& uuid) const
{
try {
@@ -118,6 +161,13 @@ std::shared_ptr<Material> MaterialManager::getMaterial(const QString& uuid) cons
}
}
std::shared_ptr<Material> MaterialManager::getMaterial(const App::Material& material)
{
MaterialManager manager;
return manager.getMaterial(QString::fromStdString(material.uuid));
}
std::shared_ptr<Material> MaterialManager::getMaterialByPath(const QString& path) const
{
QString cleanPath = QDir::cleanPath(path);
@@ -126,7 +176,9 @@ std::shared_ptr<Material> MaterialManager::getMaterialByPath(const QString& path
if (cleanPath.startsWith(library->getDirectory())) {
try {
return library->getMaterialByPath(cleanPath);
} catch (const MaterialNotFound&) {}
}
catch (const MaterialNotFound&) {
}
// See if it's a new file saved by the old editor
{

View File

@@ -37,6 +37,11 @@
namespace fs = boost::filesystem;
namespace App
{
class Material;
}
namespace Materials
{
@@ -50,11 +55,15 @@ public:
MaterialManager();
~MaterialManager() override = default;
static std::shared_ptr<Material> defaultMaterial();
static QString defaultMaterialUUID();
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> getMaterials() const
{
return _materialMap;
}
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;
@@ -66,10 +75,16 @@ public:
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 MaterialFilter* filter = nullptr) const
const std::shared_ptr<Materials::MaterialFilter>& filter) const
{
return library->getMaterialTree(filter);
}
std::shared_ptr<std::map<QString, std::shared_ptr<MaterialTreeNode>>>
getMaterialTree(const std::shared_ptr<MaterialLibrary>& library) const
{
std::shared_ptr<Materials::MaterialFilter> filter;
return library->getMaterialTree(filter);
}
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

View File

@@ -103,69 +103,69 @@ int MaterialPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
Py::String MaterialPy::getLibraryName() const
{
auto library = getMaterialPtr()->getLibrary();
return Py::String(library ? library->getName().toStdString() : "");
return {library ? library->getName().toStdString() : ""};
}
Py::String MaterialPy::getLibraryRoot() const
{
auto library = getMaterialPtr()->getLibrary();
return Py::String(library ? library->getDirectoryPath().toStdString() : "");
return {library ? library->getDirectoryPath().toStdString() : ""};
}
Py::String MaterialPy::getLibraryIcon() const
{
auto library = getMaterialPtr()->getLibrary();
return Py::String(library ? library->getIconPath().toStdString() : "");
return {library ? library->getIconPath().toStdString() : ""};
}
Py::String MaterialPy::getName() const
{
return Py::String(getMaterialPtr()->getName().toStdString());
return {getMaterialPtr()->getName().toStdString()};
}
Py::String MaterialPy::getDirectory() const
{
return Py::String(getMaterialPtr()->getDirectory().toStdString());
return {getMaterialPtr()->getDirectory().toStdString()};
}
Py::String MaterialPy::getUUID() const
{
return Py::String(getMaterialPtr()->getUUID().toStdString());
return {getMaterialPtr()->getUUID().toStdString()};
}
Py::String MaterialPy::getDescription() const
{
return Py::String(getMaterialPtr()->getDescription().toStdString());
return {getMaterialPtr()->getDescription().toStdString()};
}
Py::String MaterialPy::getURL() const
{
return Py::String(getMaterialPtr()->getURL().toStdString());
return {getMaterialPtr()->getURL().toStdString()};
}
Py::String MaterialPy::getReference() const
{
return Py::String(getMaterialPtr()->getReference().toStdString());
return {getMaterialPtr()->getReference().toStdString()};
}
Py::String MaterialPy::getParent() const
{
return Py::String(getMaterialPtr()->getParentUUID().toStdString());
return {getMaterialPtr()->getParentUUID().toStdString()};
}
Py::String MaterialPy::getAuthorAndLicense() const
{
return Py::String(getMaterialPtr()->getAuthorAndLicense().toStdString());
return {getMaterialPtr()->getAuthorAndLicense().toStdString()};
}
Py::String MaterialPy::getAuthor() const
{
return Py::String(getMaterialPtr()->getAuthor().toStdString());
return {getMaterialPtr()->getAuthor().toStdString()};
}
Py::String MaterialPy::getLicense() const
{
return Py::String(getMaterialPtr()->getLicense().toStdString());
return {getMaterialPtr()->getLicense().toStdString()};
}
Py::List MaterialPy::getPhysicalModels() const
@@ -173,8 +173,8 @@ Py::List MaterialPy::getPhysicalModels() const
auto models = getMaterialPtr()->getPhysicalModels();
Py::List list;
for (auto it = models->begin(); it != models->end(); it++) {
list.append(Py::String(it->toStdString()));
for (auto it : *models) {
list.append(Py::String(it.toStdString()));
}
return list;
@@ -185,8 +185,8 @@ Py::List MaterialPy::getAppearanceModels() const
auto models = getMaterialPtr()->getAppearanceModels();
Py::List list;
for (auto it = models->begin(); it != models->end(); it++) {
list.append(Py::String(it->toStdString()));
for (auto it : *models) {
list.append(Py::String(it.toStdString()));
}
return list;
@@ -197,8 +197,8 @@ Py::List MaterialPy::getTags() const
auto& tags = getMaterialPtr()->getTags();
Py::List list;
for (auto it = tags.begin(); it != tags.end(); it++) {
list.append(Py::String(it->toStdString()));
for (auto it : tags) {
list.append(Py::String(it.toStdString()));
}
return list;
@@ -298,9 +298,9 @@ Py::Dict MaterialPy::getProperties() const
dict.setItem(Py::String("SourceURL"), Py::String(getMaterialPtr()->getURL().toStdString()));
auto properties = getMaterialPtr()->getPhysicalProperties();
for (auto it = properties.begin(); it != properties.end(); it++) {
QString key = it->first;
auto materialProperty = it->second;
for (auto& it : properties) {
QString key = it.first;
auto materialProperty = it.second;
if (!materialProperty->isNull()) {
auto value = materialProperty->getDictionaryString();
@@ -309,9 +309,9 @@ Py::Dict MaterialPy::getProperties() const
}
properties = getMaterialPtr()->getAppearanceProperties();
for (auto it = properties.begin(); it != properties.end(); it++) {
QString key = it->first;
auto materialProperty = it->second;
for (auto& it : properties) {
QString key = it.first;
auto materialProperty = it.second;
if (!materialProperty->isNull()) {
auto value = materialProperty->getDictionaryString();
@@ -327,9 +327,9 @@ Py::Dict MaterialPy::getPhysicalProperties() const
Py::Dict dict;
auto properties = getMaterialPtr()->getPhysicalProperties();
for (auto it = properties.begin(); it != properties.end(); it++) {
QString key = it->first;
auto materialProperty = it->second;
for (auto& it : properties) {
QString key = it.first;
auto materialProperty = it.second;
if (!materialProperty->isNull()) {
auto value = materialProperty->getDictionaryString();
@@ -345,9 +345,9 @@ Py::Dict MaterialPy::getAppearanceProperties() const
Py::Dict dict;
auto properties = getMaterialPtr()->getAppearanceProperties();
for (auto it = properties.begin(); it != properties.end(); it++) {
QString key = it->first;
auto materialProperty = it->second;
for (auto& it : properties) {
QString key = it.first;
auto materialProperty = it.second;
if (!materialProperty->isNull()) {
auto value = materialProperty->getDictionaryString();
@@ -425,7 +425,7 @@ PyObject* MaterialPy::getPhysicalValue(PyObject* args)
std::static_pointer_cast<Materials::Material2DArray>(property->getMaterialValue());
return new Array2DPy(new Material2DArray(*value));
}
else if (property->getType() == MaterialValue::Array3D) {
if (property->getType() == MaterialValue::Array3D) {
auto value =
std::static_pointer_cast<Materials::Material3DArray>(property->getMaterialValue());
return new Array3DPy(new Material3DArray(*value));

View File

@@ -34,6 +34,7 @@
#include "MaterialLibrary.h"
#include "MaterialManager.h"
#include "ModelManager.h"
#include "ModelUuids.h"
using namespace Materials;
@@ -142,6 +143,32 @@ QString MaterialProperty::getYAMLString() const
return _valuePtr->getYAMLString();
}
App::Color MaterialProperty::getColor() const
{
auto colorString = getValue().toString();
std::stringstream stream(colorString.toStdString());
char c;
stream >> c; // read "("
float red;
stream >> red;
stream >> c; // ","
float green;
stream >> green;
stream >> c; // ","
float blue;
stream >> blue;
stream >> c; // ","
float alpha = 1.0;
if (c == ',') {
stream >> alpha;
}
App::Color color(red, green, blue, alpha);
return color;
}
QString MaterialProperty::getDictionaryString() const
{
// This method produces a non-localized string. For a localized string use
@@ -377,6 +404,13 @@ void MaterialProperty::setURL(const QString& value)
_valuePtr->setValue(QVariant(value));
}
void MaterialProperty::setColor(const App::Color& value)
{
std::stringstream ss;
ss << "(" << value.r << ", " << value.g << ", " << value.b << ", " << value.a << ")";
_valuePtr->setValue(QVariant(QString::fromStdString(ss.str())));
}
MaterialProperty& MaterialProperty::operator=(const MaterialProperty& other)
{
if (this == &other) {
@@ -1434,6 +1468,23 @@ Material& Material::operator=(const Material& other)
return *this;
}
Material& Material::operator=(const App::Material& other)
{
if (!hasAppearanceModel(ModelUUIDs::ModelUUID_Rendering_Basic)) {
addAppearance(ModelUUIDs::ModelUUID_Rendering_Basic);
}
getAppearanceProperty(QString::fromLatin1("AmbientColor"))->setColor(other.ambientColor);
getAppearanceProperty(QString::fromLatin1("DiffuseColor"))->setColor(other.diffuseColor);
getAppearanceProperty(QString::fromLatin1("SpecularColor"))->setColor(other.specularColor);
getAppearanceProperty(QString::fromLatin1("EmissiveColor"))->setColor(other.emissiveColor);
getAppearanceProperty(QString::fromLatin1("Shininess"))->setFloat(other.shininess);
getAppearanceProperty(QString::fromLatin1("Transparency"))->setFloat(other.transparency);
// std::string uuid;
return *this;
}
/*
* Normalize models by removing any inherited models
*/
@@ -1509,3 +1560,50 @@ QStringList Material::inheritedAddedModels(const Material& parent) const
*/
void Material::inheritedPropertyDiff([[maybe_unused]] const QString& parent)
{}
/*
* Return an App::Material object describing the materials appearance, or DEFAULT if
* undefined.
*/
App::Material Material::getMaterialAppearance() const
{
App::Material material(App::Material::DEFAULT);
bool custom = false;
if (hasAppearanceProperty(QString::fromLatin1("AmbientColor"))) {
material.ambientColor =
getAppearanceProperty(QString::fromLatin1("AmbientColor"))->getColor();
custom = true;
}
if (hasAppearanceProperty(QString::fromLatin1("DiffuseColor"))) {
material.diffuseColor =
getAppearanceProperty(QString::fromLatin1("DiffuseColor"))->getColor();
custom = true;
}
if (hasAppearanceProperty(QString::fromLatin1("SpecularColor"))) {
material.specularColor =
getAppearanceProperty(QString::fromLatin1("SpecularColor"))->getColor();
custom = true;
}
if (hasAppearanceProperty(QString::fromLatin1("EmissiveColor"))) {
material.emissiveColor =
getAppearanceProperty(QString::fromLatin1("EmissiveColor"))->getColor();
custom = true;
}
if (hasAppearanceProperty(QString::fromLatin1("Shininess"))) {
material.shininess = getAppearanceProperty(QString::fromLatin1("Shininess"))->getFloat();
custom = true;
}
if (hasAppearanceProperty(QString::fromLatin1("Transparency"))) {
material.transparency =
getAppearanceProperty(QString::fromLatin1("Transparency"))->getFloat();
custom = true;
}
if (custom) {
material.setType(App::Material::USER_DEFINED);
material.uuid = getUUID().toStdString();
}
return material;
}

View File

@@ -31,6 +31,8 @@
#include <QTextStream>
#include <App/Application.h>
#include <App/Color.h>
#include <App/Material.h>
#include <Base/BaseClass.h>
#include <Mod/Material/MaterialGlobal.h>
@@ -79,11 +81,24 @@ public:
QString getString() const;
QString getYAMLString() const;
QString getDictionaryString() const; // Non-localized string
bool getBoolean() const;
int getInt() const;
double getFloat() const;
bool getBoolean() const
{
return getValue().toBool();
}
int getInt() const
{
return getValue().toInt();
}
double getFloat() const
{
return getValue().toFloat();
}
const Base::Quantity& getQuantity() const;
const QString getURL() const;
QString getURL() const
{
return getValue().toString();
}
App::Color getColor() const;
MaterialProperty& getColumn(int column);
const MaterialProperty& getColumn(int column) const;
@@ -109,6 +124,7 @@ public:
void setQuantity(const QString& value);
void setList(const QList<QVariant>& value);
void setURL(const QString& value);
void setColor(const App::Color& value);
MaterialProperty& operator=(const MaterialProperty& other);
friend QTextStream& operator<<(QTextStream& output, const MaterialProperty& property);
@@ -218,6 +234,8 @@ public:
return &_appearanceUuids;
}
App::Material getMaterialAppearance() const;
void setLibrary(const std::shared_ptr<MaterialLibrary>& library)
{
_library = library;
@@ -365,8 +383,24 @@ public:
void save(QTextStream& stream, bool overwrite, bool saveAsCopy, bool saveInherited);
/*
* Assignment operator
*/
Material& operator=(const Material& other);
/*
* Set the appearance properties
*/
Material& operator=(const App::Material& other);
bool operator==(const Material& other) const
{
if (&other == this) {
return true;
}
return getTypeId() == other.getTypeId() && _uuid == other._uuid;
}
protected:
void addModel(const QString& uuid);
static void removeUUID(QSet<QString>& uuidList, const QString& uuid);

View File

@@ -15,11 +15,6 @@
<Author Licence="LGPL" Name="DavidCarter" EMail="dcarter@davidcarter.ca" />
<UserDocu>Material property descriptions.</UserDocu>
</Documentation>
<!-- <Methode Name="mirror">
<Documentation>
<UserDocu>Performs the symmetrical transformation of this geometric object</UserDocu>
</Documentation>
</Methode> -->
<Attribute Name="Name" ReadOnly="true">
<Documentation>
<UserDocu>Property name.</UserDocu>

View File

@@ -0,0 +1,119 @@
/***************************************************************************
* 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 <QMetaType>
#include <QUuid>
#endif
#include <App/Application.h>
#include <Base/Writer.h>
#include <Gui/MetaTypes.h>
#include "MaterialPy.h"
#include "PropertyMaterial.h"
using namespace Materials;
/* TRANSLATOR Material::PropertyMaterial */
TYPESYSTEM_SOURCE(Materials::PropertyMaterial, App::Property)
PropertyMaterial::PropertyMaterial() = default;
PropertyMaterial::~PropertyMaterial() = default;
void PropertyMaterial::setValue(const Material& mat)
{
aboutToSetValue();
_material = mat;
hasSetValue();
}
void PropertyMaterial::setValue(const App::Material& mat)
{
aboutToSetValue();
_material = mat;
hasSetValue();
}
const Material& PropertyMaterial::getValue() const
{
return _material;
}
PyObject* PropertyMaterial::getPyObject()
{
return new MaterialPy(new Material(_material));
}
void PropertyMaterial::setPyObject(PyObject* value)
{
if (PyObject_TypeCheck(value, &(MaterialPy::Type))) {
setValue(*static_cast<MaterialPy*>(value)->getMaterialPtr());
}
else {
std::string error = std::string("type must be 'Material' not ");
error += value->ob_type->tp_name;
throw Base::TypeError(error);
}
}
void PropertyMaterial::Save(Base::Writer& writer) const
{
writer.Stream() << writer.ind() << "<PropertyMaterial uuid=\""
<< _material.getUUID().toStdString() << "\"/>" << std::endl;
}
void PropertyMaterial::Restore(Base::XMLReader& reader)
{
// read my Element
reader.readElement("PropertyMaterial");
// get the value of my Attribute
aboutToSetValue();
auto uuid = reader.getAttribute("uuid");
_material.setUUID(QString::fromLatin1(uuid));
hasSetValue();
}
const char* PropertyMaterial::getEditorName() const
{
if (testStatus(MaterialEdit)) {
return ""; //"Gui::PropertyEditor::PropertyMaterialItem";
}
return "";
}
App::Property* PropertyMaterial::Copy() const
{
PropertyMaterial* p = new PropertyMaterial();
p->_material = _material;
return p;
}
void PropertyMaterial::Paste(const App::Property& from)
{
aboutToSetValue();
_material = dynamic_cast<const PropertyMaterial&>(from)._material;
hasSetValue();
}

View File

@@ -0,0 +1,101 @@
/***************************************************************************
* 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/>. *
* *
**************************************************************************/
#ifndef MATERIAL_PROPERTYMATERIAL_H
#define MATERIAL_PROPERTYMATERIAL_H
#include <App/Property.h>
#include <Base/Reader.h>
#include "Materials.h"
namespace App
{
class Material;
}
namespace Materials
{
/** Material properties
* This is the father of all properties handling colors.
*/
class MaterialsExport PropertyMaterial: public App::Property
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
/**
* A constructor.
* A more elaborate description of the constructor.
*/
PropertyMaterial();
/**
* A destructor.
* A more elaborate description of the destructor.
*/
~PropertyMaterial() override;
/** Sets the property
*/
void setValue(const Material& mat);
/** Sets the appearance properties
*/
void setValue(const App::Material& mat);
/** This method returns a string representation of the property
*/
const Material& getValue() const;
PyObject* getPyObject() override;
void setPyObject(PyObject*) override;
void Save(Base::Writer& writer) const override;
void Restore(Base::XMLReader& reader) override;
const char* getEditorName() const override;
Property* Copy() const override;
void Paste(const Property& from) override;
unsigned int getMemSize() const override
{
return sizeof(_material);
}
bool isSame(const Property& other) const override
{
if (&other == this) {
return true;
}
return getTypeId() == other.getTypeId()
&& getValue() == static_cast<decltype(this)>(&other)->getValue();
}
private:
Material _material;
};
} // namespace Materials
#endif // MATERIAL_PROPERTYMATERIAL_H

View File

@@ -34,6 +34,7 @@ SET(Material_Icon_Files
# collect all the material cards:
SET(MaterialLib_Files
Resources/Materials/Standard/Default.FCMat
Resources/Materials/Standard/Aggregate/Concrete-EN-C35_45.FCMat
Resources/Materials/Standard/Aggregate/Concrete-Generic.FCMat
Resources/Materials/Standard/Aggregate/Reinforcement-FIB-B500.FCMat

View File

@@ -19,8 +19,9 @@ include_directories(
link_directories(${OCC_LIBRARY_DIR})
set(MatGui_LIBS
Material
Materials
FreeCADGui
Part
)
include_directories(
@@ -39,6 +40,8 @@ qt_add_resources(MatGui_QRC_SRCS Resources/Material.qrc ${Material_TR_QRC})
set(MatGui_UIC_SRCS
Array2D.ui
Array3D.ui
DlgDisplayProperties.ui
DlgMaterial.ui
DlgSettingsMaterial.ui
ImageEdit.ui
ListEdit.ui
@@ -67,6 +70,15 @@ SET(MatGui_SRCS
BaseDelegate.cpp
BaseDelegate.h
Command.cpp
DlgDisplayPropertiesImp.cpp
DlgDisplayPropertiesImp.h
DlgDisplayProperties.ui
DlgMaterialImp.cpp
DlgMaterialImp.h
DlgDisplayProperties.ui
DlgMaterialImp.cpp
DlgMaterialImp.h
DlgMaterial.ui
DlgSettingsMaterial.cpp
DlgSettingsMaterial.h
DlgSettingsMaterial.ui
@@ -88,6 +100,8 @@ SET(MatGui_SRCS
MaterialsEditor.cpp
MaterialsEditor.h
MaterialsEditor.ui
MaterialTreeWidget.cpp
MaterialTreeWidget.h
ModelSelect.cpp
ModelSelect.h
ModelSelect.ui

View File

@@ -25,8 +25,12 @@
#endif
#include <Gui/Command.h>
#include <Gui/Control.h>
#include <Gui/MainWindow.h>
#include <Gui/Selection.h>
#include "DlgDisplayPropertiesImp.h"
#include "DlgMaterialImp.h"
#include "MaterialSave.h"
#include "MaterialsEditor.h"
#include "ModelSelect.h"
@@ -71,6 +75,64 @@ bool CmdMaterialsEdit::isActive()
return true;
}
//===========================================================================
// Std_SetAppearance
//===========================================================================
DEF_STD_CMD_A(StdCmdSetAppearance)
StdCmdSetAppearance::StdCmdSetAppearance()
: Command("Std_SetAppearance")
{
sGroup = "Standard-View";
sMenuText = QT_TR_NOOP("Appearance...");
sToolTipText = QT_TR_NOOP("Sets the display properties of the selected object");
sWhatsThis = "Std_SetAppearance";
sStatusTip = QT_TR_NOOP("Sets the display properties of the selected object");
sPixmap = "Std_SetAppearance";
sAccel = "Ctrl+D";
eType = Alter3DView;
}
void StdCmdSetAppearance::activated(int iMsg)
{
Q_UNUSED(iMsg);
Gui::Control().showDialog(new MatGui::TaskDisplayProperties());
}
bool StdCmdSetAppearance::isActive()
{
return (Gui::Control().activeDialog() == nullptr) && (Gui::Selection().size() != 0);
}
//===========================================================================
// Std_SetMaterial
//===========================================================================
DEF_STD_CMD_A(StdCmdSetMaterial)
StdCmdSetMaterial::StdCmdSetMaterial()
: Command("Std_SetMaterial")
{
sGroup = "Standard-View";
sMenuText = QT_TR_NOOP("Material...");
sToolTipText = QT_TR_NOOP("Sets the material of the selected object");
sWhatsThis = "Std_SetMaterial";
sStatusTip = QT_TR_NOOP("Sets the material of the selected object");
sPixmap = "Materials_Edit";
// sAccel = "Ctrl+D";
// eType = Alter3DView;
}
void StdCmdSetMaterial::activated(int iMsg)
{
Q_UNUSED(iMsg);
Gui::Control().showDialog(new MatGui::TaskMaterial());
}
bool StdCmdSetMaterial::isActive()
{
return (Gui::Control().activeDialog() == nullptr) && (Gui::Selection().size() != 0);
}
//---------------------------------------------------------------
void CreateMaterialCommands()
@@ -78,4 +140,6 @@ void CreateMaterialCommands()
Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager();
rcCmdMgr.addCommand(new CmdMaterialsEdit());
rcCmdMgr.addCommand(new StdCmdSetAppearance());
rcCmdMgr.addCommand(new StdCmdSetMaterial());
}

View File

@@ -0,0 +1,524 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MatGui::DlgDisplayProperties</class>
<widget class="QDialog" name="MatGui::DlgDisplayProperties">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>290</width>
<height>503</height>
</rect>
</property>
<property name="windowTitle">
<string>Display properties</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox1">
<property name="title">
<string>Viewing mode</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>11</number>
</property>
<property name="topMargin">
<number>11</number>
</property>
<property name="rightMargin">
<number>11</number>
</property>
<property name="bottomMargin">
<number>11</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="textLabel1">
<property name="text">
<string>Document window:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="changeMode"/>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="textLabel1_3">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Plot mode:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="changePlot">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox2">
<property name="title">
<string>Display</string>
</property>
<layout class="QGridLayout">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="textLabel2">
<property name="text">
<string>Point size:</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>71</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSpinBox" name="spinPointSize">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>64</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="textLabel3">
<property name="text">
<string>Line width:</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>71</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSpinBox" name="spinLineWidth">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>64</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="textLabel1_2">
<property name="text">
<string>Transparency:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSlider" name="horizontalSlider">
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinTransparency">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Line transparency:</string>
</property>
</widget>
</item>
<item row="5" column="0">
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSlider" name="sliderLineTransparency">
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinLineTransparency">
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox3">
<property name="title">
<string>Material</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</item>
<item row="2" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Color plot:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Shape Appearance:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="Gui::ColorButton" name="buttonPointColor"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Point color:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Line color:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="buttonColorPlot">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="Gui::ColorButton" name="buttonLineColor"/>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="buttonUserDefinedMaterial">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>32767</height>
</size>
</property>
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="MatGui::MaterialTreeWidget" name="widgetMaterial" native="true"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>Gui::ColorButton</class>
<extends>QPushButton</extends>
<header>Gui/Widgets.h</header>
</customwidget>
<customwidget>
<class>MatGui::MaterialTreeWidget</class>
<extends>QWidget</extends>
<header>Mod/Material/Gui/MaterialTreeWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>changeMode</tabstop>
<tabstop>changePlot</tabstop>
<tabstop>buttonLineColor</tabstop>
<tabstop>spinPointSize</tabstop>
<tabstop>spinLineWidth</tabstop>
<tabstop>horizontalSlider</tabstop>
<tabstop>spinTransparency</tabstop>
<tabstop>sliderLineTransparency</tabstop>
<tabstop>spinLineTransparency</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>spinTransparency</sender>
<signal>valueChanged(int)</signal>
<receiver>horizontalSlider</receiver>
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>240</x>
<y>339</y>
</hint>
<hint type="destinationlabel">
<x>113</x>
<y>339</y>
</hint>
</hints>
</connection>
<connection>
<sender>horizontalSlider</sender>
<signal>valueChanged(int)</signal>
<receiver>spinTransparency</receiver>
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>113</x>
<y>339</y>
</hint>
<hint type="destinationlabel">
<x>240</x>
<y>339</y>
</hint>
</hints>
</connection>
<connection>
<sender>sliderLineTransparency</sender>
<signal>valueChanged(int)</signal>
<receiver>spinLineTransparency</receiver>
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>147</x>
<y>408</y>
</hint>
<hint type="destinationlabel">
<x>246</x>
<y>393</y>
</hint>
</hints>
</connection>
<connection>
<sender>spinLineTransparency</sender>
<signal>valueChanged(int)</signal>
<receiver>sliderLineTransparency</receiver>
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>254</x>
<y>402</y>
</hint>
<hint type="destinationlabel">
<x>98</x>
<y>404</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,700 @@
/***************************************************************************
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#include <QDockWidget>
#include <QSignalBlocker>
#include <algorithm>
#include <boost_signals2.hpp>
#endif
#include <Base/Console.h>
#include <Gui/Application.h>
#include <Gui/DlgMaterialPropertiesImp.h>
#include <Gui/DockWindowManager.h>
#include <Gui/Document.h>
#include <Gui/Selection.h>
#include <Gui/ViewProvider.h>
#include <Gui/WaitCursor.h>
#include <Mod/Material/App/ModelUuids.h>
#include "DlgDisplayPropertiesImp.h"
#include "ui_DlgDisplayProperties.h"
using namespace MatGui;
using namespace std;
namespace sp = std::placeholders;
/* TRANSLATOR Gui::Dialog::DlgDisplayPropertiesImp */
#if 0 // needed for Qt's lupdate utility
qApp->translate("QDockWidget", "Display properties");
#endif
class DlgDisplayPropertiesImp::Private
{
using DlgDisplayPropertiesImp_Connection = boost::signals2::connection;
public:
Ui::DlgDisplayProperties ui;
bool floating;
DlgDisplayPropertiesImp_Connection connectChangedObject;
static void setElementColor(const std::vector<Gui::ViewProvider*>& views,
const char* property,
Gui::ColorButton* buttonColor)
{
bool hasElementColor = false;
for (const auto& view : views) {
if (auto* prop = dynamic_cast<App::PropertyColor*>(view->getPropertyByName(property))) {
App::Color color = prop->getValue();
QSignalBlocker block(buttonColor);
buttonColor->setColor(color.asValue<QColor>());
hasElementColor = true;
break;
}
}
buttonColor->setEnabled(hasElementColor);
}
static void setElementAppearance(const std::vector<Gui::ViewProvider*>& views,
const char* property,
Gui::ColorButton* buttonColor)
{
bool hasElementColor = false;
for (const auto& view : views) {
if (auto* prop =
dynamic_cast<App::PropertyMaterial*>(view->getPropertyByName(property))) {
App::Color color = prop->getDiffuseColor();
QSignalBlocker block(buttonColor);
buttonColor->setColor(color.asValue<QColor>());
hasElementColor = true;
break;
}
}
buttonColor->setEnabled(hasElementColor);
}
static void setDrawStyle(const std::vector<Gui::ViewProvider*>& views,
const char* property,
QSpinBox* spinbox)
{
bool hasDrawStyle = false;
for (const auto& view : views) {
if (auto* prop = dynamic_cast<App::PropertyFloat*>(view->getPropertyByName(property))) {
QSignalBlocker block(spinbox);
spinbox->setValue(int(prop->getValue()));
hasDrawStyle = true;
break;
}
}
spinbox->setEnabled(hasDrawStyle);
}
static void setTransparency(const std::vector<Gui::ViewProvider*>& views,
const char* property,
QSpinBox* spinbox,
QSlider* slider)
{
bool hasTransparency = false;
for (const auto& view : views) {
if (auto* prop =
dynamic_cast<App::PropertyInteger*>(view->getPropertyByName(property))) {
QSignalBlocker blockSpinBox(spinbox);
spinbox->setValue(prop->getValue());
QSignalBlocker blockSlider(slider);
slider->setValue(prop->getValue());
hasTransparency = true;
break;
}
}
spinbox->setEnabled(hasTransparency);
slider->setEnabled(hasTransparency);
}
};
/**
* Constructs a DlgDisplayPropertiesImp which is a child of 'parent', with the
* name 'name' and widget flags set to 'f'
*
* The dialog will by default be modeless, unless you set 'modal' to
* true to construct a modal dialog.
*/
DlgDisplayPropertiesImp::DlgDisplayPropertiesImp(bool floating, QWidget* parent, Qt::WindowFlags fl)
: QDialog(parent, fl)
, d(new Private)
{
d->ui.setupUi(this);
setupConnections();
d->ui.textLabel1_3->hide();
d->ui.changePlot->hide();
d->ui.buttonLineColor->setModal(false);
d->ui.buttonPointColor->setModal(false);
d->floating = floating;
// Create a filter to only include current format materials
// that contain the basic render model.
auto filter = std::make_shared<Materials::MaterialFilter>();
filter->setIncludeEmptyFolders(false);
filter->setIncludeLegacy(false);
filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Rendering_Basic);
d->ui.widgetMaterial->setFilter(filter);
std::vector<Gui::ViewProvider*> views = getSelection();
setDisplayModes(views);
setMaterial(views);
setColorPlot(views);
setShapeAppearance(views);
setLineColor(views);
setPointColor(views);
setPointSize(views);
setLineWidth(views);
setTransparency(views);
setLineTransparency(views);
// embed this dialog into a dockable widget container
if (floating) {
Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance();
QDockWidget* dw =
pDockMgr->addDockWindow("Display properties", this, Qt::AllDockWidgetAreas);
dw->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
dw->setFloating(true);
dw->show();
}
Gui::Selection().Attach(this);
// NOLINTBEGIN
d->connectChangedObject = Gui::Application::Instance->signalChangedObject.connect(
std::bind(&DlgDisplayPropertiesImp::slotChangedObject, this, sp::_1, sp::_2));
// NOLINTEND
}
/**
* Destroys the object and frees any allocated resources
*/
DlgDisplayPropertiesImp::~DlgDisplayPropertiesImp()
{
// no need to delete child widgets, Qt does it all for us
d->connectChangedObject.disconnect();
Gui::Selection().Detach(this);
}
void DlgDisplayPropertiesImp::setupConnections()
{
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
connect(d->ui.changeMode,
qOverload<const QString&>(&QComboBox::activated),
this,
&DlgDisplayPropertiesImp::onChangeModeActivated);
connect(d->ui.changePlot,
qOverload<const QString&>(&QComboBox::activated),
this,
&DlgDisplayPropertiesImp::onChangePlotActivated);
#else
connect(d->ui.changeMode,
&QComboBox::textActivated,
this,
&DlgDisplayPropertiesImp::onChangeModeActivated);
connect(d->ui.changePlot,
&QComboBox::textActivated,
this,
&DlgDisplayPropertiesImp::onChangePlotActivated);
#endif
connect(d->ui.spinTransparency,
qOverload<int>(&QSpinBox::valueChanged),
this,
&DlgDisplayPropertiesImp::onSpinTransparencyValueChanged);
connect(d->ui.spinPointSize,
qOverload<int>(&QSpinBox::valueChanged),
this,
&DlgDisplayPropertiesImp::onSpinPointSizeValueChanged);
connect(d->ui.buttonLineColor,
&Gui::ColorButton::changed,
this,
&DlgDisplayPropertiesImp::onButtonLineColorChanged);
connect(d->ui.buttonPointColor,
&Gui::ColorButton::changed,
this,
&DlgDisplayPropertiesImp::onButtonPointColorChanged);
connect(d->ui.spinLineWidth,
qOverload<int>(&QSpinBox::valueChanged),
this,
&DlgDisplayPropertiesImp::onSpinLineWidthValueChanged);
connect(d->ui.spinLineTransparency,
qOverload<int>(&QSpinBox::valueChanged),
this,
&DlgDisplayPropertiesImp::onSpinLineTransparencyValueChanged);
connect(d->ui.buttonUserDefinedMaterial,
&Gui::ColorButton::clicked,
this,
&DlgDisplayPropertiesImp::onButtonUserDefinedMaterialClicked);
connect(d->ui.buttonColorPlot,
&Gui::ColorButton::clicked,
this,
&DlgDisplayPropertiesImp::onButtonColorPlotClicked);
connect(d->ui.widgetMaterial,
&MaterialTreeWidget::materialSelected,
this,
&DlgDisplayPropertiesImp::onMaterialSelected);
}
void DlgDisplayPropertiesImp::changeEvent(QEvent* e)
{
if (e->type() == QEvent::LanguageChange) {
d->ui.retranslateUi(this);
}
QDialog::changeEvent(e);
}
/// @cond DOXERR
void DlgDisplayPropertiesImp::OnChange(Gui::SelectionSingleton::SubjectType& rCaller,
Gui::SelectionSingleton::MessageType Reason)
{
Q_UNUSED(rCaller);
if (Reason.Type == Gui::SelectionChanges::AddSelection
|| Reason.Type == Gui::SelectionChanges::RmvSelection
|| Reason.Type == Gui::SelectionChanges::SetSelection
|| Reason.Type == Gui::SelectionChanges::ClrSelection) {
std::vector<Gui::ViewProvider*> views = getSelection();
setDisplayModes(views);
setMaterial(views);
setColorPlot(views);
setShapeAppearance(views);
setLineColor(views);
setPointColor(views);
setPointSize(views);
setLineWidth(views);
setTransparency(views);
setLineTransparency(views);
}
}
/// @endcond
void DlgDisplayPropertiesImp::slotChangedObject(const Gui::ViewProvider& obj,
const App::Property& prop)
{
// This method gets called if a property of any view provider is changed.
// We pick out all the properties for which we need to update this dialog.
std::vector<Gui::ViewProvider*> Provider = getSelection();
auto vp = std::find_if(Provider.begin(), Provider.end(), [&obj](Gui::ViewProvider* v) {
return v == &obj;
});
if (vp != Provider.end()) {
const char* name = obj.getPropertyName(&prop);
// this is not a property of the view provider but of the document object
if (!name) {
return;
}
std::string prop_name = name;
if (prop.is<App::PropertyColor>()) {
App::Color value = static_cast<const App::PropertyColor&>(prop).getValue();
if (prop_name == "LineColor") {
bool blocked = d->ui.buttonLineColor->blockSignals(true);
d->ui.buttonLineColor->setColor(QColor((int)(255.0f * value.r),
(int)(255.0f * value.g),
(int)(255.0f * value.b)));
d->ui.buttonLineColor->blockSignals(blocked);
}
else if (prop_name == "PointColor") {
bool blocked = d->ui.buttonPointColor->blockSignals(true);
d->ui.buttonPointColor->setColor(QColor((int)(255.0f * value.r),
(int)(255.0f * value.g),
(int)(255.0f * value.b)));
d->ui.buttonPointColor->blockSignals(blocked);
}
}
else if (prop.isDerivedFrom<App::PropertyMaterialList>()) {
auto& value = static_cast<const App::PropertyMaterialList&>(prop).getValue();
if (prop_name == "ShapeAppearance") {
// bool blocked = d->ui.buttonColor->blockSignals(true);
// auto color = value.diffuseColor;
// d->ui.buttonColor->setColor(QColor((int)(255.0f * color.r),
// (int)(255.0f * color.g),
// (int)(255.0f * color.b)));
// d->ui.buttonColor->blockSignals(blocked);
}
}
else if (prop.isDerivedFrom<App::PropertyInteger>()) {
long value = static_cast<const App::PropertyInteger&>(prop).getValue();
if (prop_name == "Transparency") {
bool blocked = d->ui.spinTransparency->blockSignals(true);
d->ui.spinTransparency->setValue(value);
d->ui.spinTransparency->blockSignals(blocked);
blocked = d->ui.horizontalSlider->blockSignals(true);
d->ui.horizontalSlider->setValue(value);
d->ui.horizontalSlider->blockSignals(blocked);
}
else if (prop_name == "LineTransparency") {
bool blocked = d->ui.spinLineTransparency->blockSignals(true);
d->ui.spinLineTransparency->setValue(value);
d->ui.spinLineTransparency->blockSignals(blocked);
blocked = d->ui.sliderLineTransparency->blockSignals(true);
d->ui.sliderLineTransparency->setValue(value);
d->ui.sliderLineTransparency->blockSignals(blocked);
}
}
else if (prop.isDerivedFrom<App::PropertyFloat>()) {
double value = static_cast<const App::PropertyFloat&>(prop).getValue();
if (prop_name == "PointSize") {
bool blocked = d->ui.spinPointSize->blockSignals(true);
d->ui.spinPointSize->setValue((int)value);
d->ui.spinPointSize->blockSignals(blocked);
}
else if (prop_name == "LineWidth") {
bool blocked = d->ui.spinLineWidth->blockSignals(true);
d->ui.spinLineWidth->setValue((int)value);
d->ui.spinLineWidth->blockSignals(blocked);
}
}
}
}
/**
* Destroys the dock window this object is embedded into without destroying itself.
*/
void DlgDisplayPropertiesImp::reject()
{
if (d->floating) {
// closes the dock window
Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance();
pDockMgr->removeDockWindow(this);
}
QDialog::reject();
}
/**
* Opens a dialog that allows to modify the 'ShapeMaterial' property of all selected view providers.
*/
void DlgDisplayPropertiesImp::onButtonUserDefinedMaterialClicked()
{
std::vector<Gui::ViewProvider*> Provider = getSelection();
Gui::Dialog::DlgMaterialPropertiesImp dlg("ShapeMaterial", this);
dlg.setViewProviders(Provider);
dlg.exec();
// d->ui.buttonColor->setColor(dlg.diffuseColor());
}
/**
* Opens a dialog that allows to modify the 'ShapeMaterial' property of all selected view providers.
*/
void DlgDisplayPropertiesImp::onButtonColorPlotClicked()
{
std::vector<Gui::ViewProvider*> Provider = getSelection();
static QPointer<Gui::Dialog::DlgMaterialPropertiesImp> dlg = nullptr;
if (!dlg) {
dlg = new Gui::Dialog::DlgMaterialPropertiesImp("TextureMaterial", this);
}
dlg->setModal(false);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->setViewProviders(Provider);
dlg->show();
}
/**
* Sets the 'Display' property of all selected view providers.
*/
void DlgDisplayPropertiesImp::onChangeModeActivated(const QString& s)
{
Gui::WaitCursor wc;
std::vector<Gui::ViewProvider*> Provider = getSelection();
for (auto it : Provider) {
if (auto* prop =
dynamic_cast<App::PropertyEnumeration*>(it->getPropertyByName("DisplayMode"))) {
prop->setValue(static_cast<const char*>(s.toLatin1()));
}
}
}
void DlgDisplayPropertiesImp::onChangePlotActivated(const QString& s)
{
Base::Console().Log("Plot = %s\n", (const char*)s.toLatin1());
}
/**
* Sets the 'Transparency' property of all selected view providers.
*/
void DlgDisplayPropertiesImp::onSpinTransparencyValueChanged(int transparency)
{
std::vector<Gui::ViewProvider*> Provider = getSelection();
for (auto it : Provider) {
if (auto* prop =
dynamic_cast<App::PropertyInteger*>(it->getPropertyByName("Transparency"))) {
prop->setValue(transparency);
}
}
}
/**
* Sets the 'PointSize' property of all selected view providers.
*/
void DlgDisplayPropertiesImp::onSpinPointSizeValueChanged(int pointsize)
{
std::vector<Gui::ViewProvider*> Provider = getSelection();
for (auto it : Provider) {
if (auto* prop = dynamic_cast<App::PropertyFloat*>(it->getPropertyByName("PointSize"))) {
prop->setValue(static_cast<double>(pointsize));
}
}
}
/**
* Sets the 'LineWidth' property of all selected view providers.
*/
void DlgDisplayPropertiesImp::onSpinLineWidthValueChanged(int linewidth)
{
std::vector<Gui::ViewProvider*> Provider = getSelection();
for (auto it : Provider) {
if (auto* prop = dynamic_cast<App::PropertyFloat*>(it->getPropertyByName("LineWidth"))) {
prop->setValue(static_cast<double>(linewidth));
}
}
}
void DlgDisplayPropertiesImp::onButtonLineColorChanged()
{
std::vector<Gui::ViewProvider*> Provider = getSelection();
QColor s = d->ui.buttonLineColor->color();
App::Color c(s.red() / 255.0, s.green() / 255.0, s.blue() / 255.0);
for (auto it : Provider) {
if (auto* prop = dynamic_cast<App::PropertyColor*>(it->getPropertyByName("LineColor"))) {
prop->setValue(c);
}
}
}
void DlgDisplayPropertiesImp::onButtonPointColorChanged()
{
std::vector<Gui::ViewProvider*> Provider = getSelection();
QColor s = d->ui.buttonPointColor->color();
App::Color c(s.red() / 255.0, s.green() / 255.0, s.blue() / 255.0);
for (auto it : Provider) {
if (auto* prop = dynamic_cast<App::PropertyColor*>(it->getPropertyByName("PointColor"))) {
prop->setValue(c);
}
}
}
void DlgDisplayPropertiesImp::onSpinLineTransparencyValueChanged(int transparency)
{
std::vector<Gui::ViewProvider*> Provider = getSelection();
for (auto it : Provider) {
if (auto* prop =
dynamic_cast<App::PropertyInteger*>(it->getPropertyByName("LineTransparency"))) {
prop->setValue(transparency);
}
}
}
void DlgDisplayPropertiesImp::setDisplayModes(const std::vector<Gui::ViewProvider*>& views)
{
QStringList commonModes, modes;
for (auto it = views.begin(); it != views.end(); ++it) {
if (auto* prop =
dynamic_cast<App::PropertyEnumeration*>((*it)->getPropertyByName("DisplayMode"))) {
if (!prop->hasEnums()) {
return;
}
std::vector<std::string> value = prop->getEnumVector();
if (it == views.begin()) {
for (const auto& jt : value) {
commonModes << QLatin1String(jt.c_str());
}
}
else {
for (const auto& jt : value) {
if (commonModes.contains(QLatin1String(jt.c_str()))) {
modes << QLatin1String(jt.c_str());
}
}
commonModes = modes;
modes.clear();
}
}
}
d->ui.changeMode->clear();
d->ui.changeMode->addItems(commonModes);
d->ui.changeMode->setDisabled(commonModes.isEmpty());
// find the display mode to activate
for (const auto& view : views) {
if (auto* prop =
dynamic_cast<App::PropertyEnumeration*>(view->getPropertyByName("DisplayMode"))) {
QString activeMode = QString::fromLatin1(prop->getValueAsString());
int index = d->ui.changeMode->findText(activeMode);
if (index != -1) {
d->ui.changeMode->setCurrentIndex(index);
break;
}
}
}
}
void DlgDisplayPropertiesImp::setMaterial(const std::vector<Gui::ViewProvider*>& views)
{
bool material = false;
App::Material::MaterialType matType = App::Material::DEFAULT;
for (auto view : views) {
if (auto* prop =
dynamic_cast<App::PropertyMaterial*>(view->getPropertyByName("ShapeMaterial"))) {
material = true;
matType = prop->getValue().getType();
break;
}
}
d->ui.buttonUserDefinedMaterial->setEnabled(material);
}
void DlgDisplayPropertiesImp::setColorPlot(const std::vector<Gui::ViewProvider*>& views)
{
bool material = false;
for (auto view : views) {
auto* prop =
dynamic_cast<App::PropertyMaterial*>(view->getPropertyByName("TextureMaterial"));
if (prop) {
material = true;
break;
}
}
d->ui.buttonColorPlot->setEnabled(material);
}
void DlgDisplayPropertiesImp::setShapeAppearance(const std::vector<Gui::ViewProvider*>& views)
{
Q_UNUSED(views)
// Private::setElementAppearance(views, "ShapeColor", d->ui.buttonColor);
}
void DlgDisplayPropertiesImp::setLineColor(const std::vector<Gui::ViewProvider*>& views)
{
Private::setElementColor(views, "LineColor", d->ui.buttonLineColor);
}
void DlgDisplayPropertiesImp::setPointColor(const std::vector<Gui::ViewProvider*>& views)
{
Private::setElementColor(views, "PointColor", d->ui.buttonPointColor);
}
void DlgDisplayPropertiesImp::setPointSize(const std::vector<Gui::ViewProvider*>& views)
{
Private::setDrawStyle(views, "PointSize", d->ui.spinPointSize);
}
void DlgDisplayPropertiesImp::setLineWidth(const std::vector<Gui::ViewProvider*>& views)
{
Private::setDrawStyle(views, "LineWidth", d->ui.spinLineWidth);
}
void DlgDisplayPropertiesImp::setTransparency(const std::vector<Gui::ViewProvider*>& views)
{
Private::setTransparency(views, "Transparency", d->ui.spinTransparency, d->ui.horizontalSlider);
}
void DlgDisplayPropertiesImp::setLineTransparency(const std::vector<Gui::ViewProvider*>& views)
{
Private::setTransparency(views,
"LineTransparency",
d->ui.spinLineTransparency,
d->ui.sliderLineTransparency);
}
std::vector<Gui::ViewProvider*> DlgDisplayPropertiesImp::getSelection() const
{
std::vector<Gui::ViewProvider*> views;
// get the complete selection
std::vector<Gui::SelectionSingleton::SelObj> sel = Gui::Selection().getCompleteSelection();
for (const auto& it : sel) {
Gui::ViewProvider* view =
Gui::Application::Instance->getDocument(it.pDoc)->getViewProvider(it.pObject);
views.push_back(view);
}
return views;
}
void DlgDisplayPropertiesImp::onMaterialSelected(
const std::shared_ptr<Materials::Material>& material)
{
std::vector<Gui::ViewProvider*> Provider = getSelection();
for (auto it : Provider) {
if (auto* prop = dynamic_cast<App::PropertyMaterialList*>(
it->getPropertyByName("ShapeAppearance"))) {
App::Material mat;
mat.ambientColor =
material->getAppearanceProperty(QString::fromLatin1("AmbientColor"))->getColor();
mat.diffuseColor =
material->getAppearanceProperty(QString::fromLatin1("DiffuseColor"))->getColor();
mat.emissiveColor =
material->getAppearanceProperty(QString::fromLatin1("EmissiveColor"))->getColor();
mat.specularColor =
material->getAppearanceProperty(QString::fromLatin1("SpecularColor"))->getColor();
mat.shininess =
material->getAppearanceProperty(QString::fromLatin1("Shininess"))->getFloat();
mat.transparency =
material->getAppearanceProperty(QString::fromLatin1("Transparency"))->getFloat();
prop->setValue(mat);
}
}
}
// ----------------------------------------------------------------------------
/* TRANSLATOR Gui::Dialog::TaskDisplayProperties */
TaskDisplayProperties::TaskDisplayProperties()
{
this->setButtonPosition(TaskDisplayProperties::North);
widget = new DlgDisplayPropertiesImp(false);
addTaskBox(widget);
}
TaskDisplayProperties::~TaskDisplayProperties() = default;
QDialogButtonBox::StandardButtons TaskDisplayProperties::getStandardButtons() const
{
return QDialogButtonBox::Close;
}
bool TaskDisplayProperties::reject()
{
widget->reject();
return (widget->result() == QDialog::Rejected);
}
#include "moc_DlgDisplayPropertiesImp.cpp"

View File

@@ -0,0 +1,136 @@
/***************************************************************************
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#ifndef MATGUI_DIALOG_DLGDISPLAYPROPERTIES_IMP_H
#define MATGUI_DIALOG_DLGDISPLAYPROPERTIES_IMP_H
#include <QDialog>
#include <memory>
#include <vector>
#include <App/Material.h>
#include <Gui/Selection.h>
#include <Gui/TaskView/TaskDialog.h>
#include <Gui/TaskView/TaskView.h>
#include <Mod/Material/App/Materials.h>
namespace App
{
class Property;
}
namespace MatGui
{
class ViewProvider;
class Command;
/**
* The DlgDisplayPropertiesImp class implements a dialog containing all available document
* templates to create a new document.
* \author Jürgen Riegel
*/
class DlgDisplayPropertiesImp: public QDialog, public Gui::SelectionSingleton::ObserverType
{
Q_OBJECT
public:
explicit DlgDisplayPropertiesImp(bool floating,
QWidget* parent = nullptr,
Qt::WindowFlags fl = Qt::WindowFlags());
~DlgDisplayPropertiesImp() override;
/// Observer message from the Selection
void OnChange(Gui::SelectionSingleton::SubjectType& rCaller,
Gui::SelectionSingleton::MessageType Reason) override;
void showDefaultButtons(bool);
void reject() override;
private Q_SLOTS:
void onChangeModeActivated(const QString&);
void onChangePlotActivated(const QString&);
void onSpinTransparencyValueChanged(int);
void onSpinPointSizeValueChanged(int);
void onButtonLineColorChanged();
void onButtonPointColorChanged();
void onSpinLineWidthValueChanged(int);
void onSpinLineTransparencyValueChanged(int);
void onButtonUserDefinedMaterialClicked();
void onButtonColorPlotClicked();
void onMaterialSelected(const std::shared_ptr<Materials::Material>& material);
protected:
void changeEvent(QEvent* e) override;
private:
void setupConnections();
void slotChangedObject(const Gui::ViewProvider&, const App::Property& Prop);
void setDisplayModes(const std::vector<Gui::ViewProvider*>&);
void setMaterial(const std::vector<Gui::ViewProvider*>&);
void setColorPlot(const std::vector<Gui::ViewProvider*>&);
void setShapeAppearance(const std::vector<Gui::ViewProvider*>&);
void setLineColor(const std::vector<Gui::ViewProvider*>&);
void setPointColor(const std::vector<Gui::ViewProvider*>&);
void setPointSize(const std::vector<Gui::ViewProvider*>&);
void setLineWidth(const std::vector<Gui::ViewProvider*>&);
void setTransparency(const std::vector<Gui::ViewProvider*>&);
void setLineTransparency(const std::vector<Gui::ViewProvider*>&);
std::vector<Gui::ViewProvider*> getSelection() const;
private:
class Private;
std::unique_ptr<Private> d;
};
class TaskDisplayProperties: public Gui::TaskView::TaskDialog
{
Q_OBJECT
public:
TaskDisplayProperties();
~TaskDisplayProperties() override;
public:
bool reject() override;
bool isAllowedAlterDocument() const override
{
return true;
}
bool isAllowedAlterView() const override
{
return true;
}
bool isAllowedAlterSelection() const override
{
return true;
}
QDialogButtonBox::StandardButtons getStandardButtons() const override;
private:
DlgDisplayPropertiesImp* widget;
};
} // namespace MatGui
#endif // MATGUI_DIALOG_DLGDISPLAYPROPERTIES_IMP_H

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MatGui::DlgMaterial</class>
<widget class="QDialog" name="MatGui::DlgMaterial">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>290</width>
<height>503</height>
</rect>
</property>
<property name="windowTitle">
<string>Material</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="MatGui::MaterialTreeWidget" name="widgetMaterial" native="true"/>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>MatGui::MaterialTreeWidget</class>
<extends>QWidget</extends>
<header>Mod/Material/Gui/MaterialTreeWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,282 @@
/***************************************************************************
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#include <QDockWidget>
#include <QSignalBlocker>
#include <QString>
#include <algorithm>
#include <boost_signals2.hpp>
#endif
#include <Base/Console.h>
#include <Gui/Application.h>
#include <Gui/DlgMaterialPropertiesImp.h>
#include <Gui/DockWindowManager.h>
#include <Gui/Document.h>
#include <Gui/Selection.h>
#include <Gui/ViewProvider.h>
#include <Gui/WaitCursor.h>
#include <Mod/Material/App/Exceptions.h>
#include <Mod/Material/App/MaterialManager.h>
#include <Mod/Material/App/ModelUuids.h>
#include <Mod/Part/App/PartFeature.h>
#include "DlgMaterialImp.h"
#include "ui_DlgMaterial.h"
using namespace MatGui;
using namespace std;
namespace sp = std::placeholders;
/* TRANSLATOR Gui::Dialog::DlgMaterialImp */
#if 0 // needed for Qt's lupdate utility
qApp->translate("QDockWidget", "Material");
#endif
class DlgMaterialImp::Private
{
using DlgMaterialImp_Connection = boost::signals2::connection;
public:
Ui::DlgMaterial ui;
bool floating;
DlgMaterialImp_Connection connectChangedObject;
};
/**
* Constructs a DlgMaterialImp which is a child of 'parent', with the
* name 'name' and widget flags set to 'f'
*
* The dialog will by default be modeless, unless you set 'modal' to
* true to construct a modal dialog.
*/
DlgMaterialImp::DlgMaterialImp(bool floating, QWidget* parent, Qt::WindowFlags fl)
: QDialog(parent, fl)
, d(new Private)
{
d->ui.setupUi(this);
setupConnections();
d->floating = floating;
// // Create a filter to only include current format materials
// // that contain the basic render model.
// auto filter = std::make_shared<Materials::MaterialFilter>();
// filter->setIncludeEmptyFolders(false);
// filter->setIncludeLegacy(false);
// filter->addRequiredComplete(Materials::ModelUUIDs::ModelUUID_Rendering_Basic);
// d->ui.widgetMaterial->setFilter(filter);
std::vector<App::DocumentObject*> objects = getSelectionObjects();
setMaterial(objects);
// embed this dialog into a dockable widget container
if (floating) {
Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance();
QDockWidget* dw =
pDockMgr->addDockWindow("Display properties", this, Qt::AllDockWidgetAreas);
dw->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
dw->setFloating(true);
dw->show();
}
Gui::Selection().Attach(this);
// NOLINTBEGIN
d->connectChangedObject = Gui::Application::Instance->signalChangedObject.connect(
std::bind(&DlgMaterialImp::slotChangedObject, this, sp::_1, sp::_2));
// NOLINTEND
}
/**
* Destroys the object and frees any allocated resources
*/
DlgMaterialImp::~DlgMaterialImp()
{
// no need to delete child widgets, Qt does it all for us
d->connectChangedObject.disconnect();
Gui::Selection().Detach(this);
}
void DlgMaterialImp::setupConnections()
{
connect(d->ui.widgetMaterial,
&MaterialTreeWidget::materialSelected,
this,
&DlgMaterialImp::onMaterialSelected);
}
void DlgMaterialImp::changeEvent(QEvent* e)
{
if (e->type() == QEvent::LanguageChange) {
d->ui.retranslateUi(this);
}
QDialog::changeEvent(e);
}
/// @cond DOXERR
void DlgMaterialImp::OnChange(Gui::SelectionSingleton::SubjectType& rCaller,
Gui::SelectionSingleton::MessageType Reason)
{
Q_UNUSED(rCaller);
if (Reason.Type == Gui::SelectionChanges::AddSelection
|| Reason.Type == Gui::SelectionChanges::RmvSelection
|| Reason.Type == Gui::SelectionChanges::SetSelection
|| Reason.Type == Gui::SelectionChanges::ClrSelection) {
std::vector<App::DocumentObject*> objects = getSelectionObjects();
setMaterial(objects);
}
}
/// @endcond
void DlgMaterialImp::slotChangedObject(const Gui::ViewProvider& obj, const App::Property& prop)
{
// This method gets called if a property of any view provider is changed.
// We pick out all the properties for which we need to update this dialog.
std::vector<Gui::ViewProvider*> Provider = getSelection();
auto vp = std::find_if(Provider.begin(), Provider.end(), [&obj](Gui::ViewProvider* v) {
return v == &obj;
});
if (vp != Provider.end()) {
const char* name = obj.getPropertyName(&prop);
// this is not a property of the view provider but of the document object
if (!name) {
return;
}
std::string prop_name = name;
if (prop.isDerivedFrom<App::PropertyMaterial>()) {
auto& value = static_cast<const App::PropertyMaterial&>(prop).getValue();
if (prop_name == "ShapeMaterial") {
// bool blocked = d->ui.buttonColor->blockSignals(true);
// auto color = value.diffuseColor;
// d->ui.buttonColor->setColor(QColor((int)(255.0f * color.r),
// (int)(255.0f * color.g),
// (int)(255.0f * color.b)));
// d->ui.buttonColor->blockSignals(blocked);
}
}
}
}
/**
* Destroys the dock window this object is embedded into without destroying itself.
*/
void DlgMaterialImp::reject()
{
if (d->floating) {
// closes the dock window
Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance();
pDockMgr->removeDockWindow(this);
}
QDialog::reject();
}
void DlgMaterialImp::setMaterial(const std::vector<App::DocumentObject*>& objects)
{
for (auto it : objects) {
if (auto* obj = dynamic_cast<Part::Feature*>(it)) {
auto material = obj->ShapeMaterial.getValue();
try {
std::string mat = material.getUUID().toStdString();
d->ui.widgetMaterial->setMaterial(material.getUUID());
return;
}
catch (const Materials::MaterialNotFound&) {
}
}
}
d->ui.widgetMaterial->setMaterial(Materials::MaterialManager::defaultMaterialUUID());
}
std::vector<Gui::ViewProvider*> DlgMaterialImp::getSelection() const
{
std::vector<Gui::ViewProvider*> views;
// get the complete selection
std::vector<Gui::SelectionSingleton::SelObj> sel = Gui::Selection().getCompleteSelection();
for (const auto& it : sel) {
Gui::ViewProvider* view =
Gui::Application::Instance->getDocument(it.pDoc)->getViewProvider(it.pObject);
views.push_back(view);
}
return views;
}
std::vector<App::DocumentObject*> DlgMaterialImp::getSelectionObjects() const
{
std::vector<App::DocumentObject*> objects;
// get the complete selection
std::vector<Gui::SelectionSingleton::SelObj> sel = Gui::Selection().getCompleteSelection();
for (const auto& it : sel) {
objects.push_back(it.pObject);
}
return objects;
}
void DlgMaterialImp::onMaterialSelected(const std::shared_ptr<Materials::Material>& material)
{
std::string mat = material->getUUID().toStdString();
std::vector<App::DocumentObject*> objects = getSelectionObjects();
for (auto it : objects) {
if (auto* obj = dynamic_cast<Part::Feature*>(it)) {
obj->ShapeMaterial.setValue(*material);
}
}
}
// ----------------------------------------------------------------------------
/* TRANSLATOR Gui::Dialog::TaskMaterial */
TaskMaterial::TaskMaterial()
{
this->setButtonPosition(TaskMaterial::North);
widget = new DlgMaterialImp(false);
taskbox = new Gui::TaskView::TaskBox(QPixmap(), widget->windowTitle(), true, nullptr);
taskbox->groupLayout()->addWidget(widget);
Content.push_back(taskbox);
}
TaskMaterial::~TaskMaterial() = default;
QDialogButtonBox::StandardButtons TaskMaterial::getStandardButtons() const
{
return QDialogButtonBox::Close;
}
bool TaskMaterial::reject()
{
widget->reject();
return (widget->result() == QDialog::Rejected);
}
#include "moc_DlgMaterialImp.cpp"

View File

@@ -0,0 +1,119 @@
/***************************************************************************
* Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#ifndef MATGUI_DIALOGMATERIALIMP_H
#define MATGUI_DIALOGMATERIALIMP_H
#include <QDialog>
#include <memory>
#include <vector>
#include <App/Material.h>
#include <Gui/Selection.h>
#include <Gui/TaskView/TaskDialog.h>
#include <Gui/TaskView/TaskView.h>
#include <Mod/Material/App/Materials.h>
namespace App
{
class Property;
}
namespace MatGui
{
class ViewProvider;
class Command;
/**
* The DlgMaterialImp class implements a dialog containing all available document
* templates to create a new document.
* \author Jürgen Riegel
*/
class DlgMaterialImp: public QDialog, public Gui::SelectionSingleton::ObserverType
{
Q_OBJECT
public:
explicit DlgMaterialImp(bool floating,
QWidget* parent = nullptr,
Qt::WindowFlags fl = Qt::WindowFlags());
~DlgMaterialImp() override;
/// Observer message from the Selection
void OnChange(Gui::SelectionSingleton::SubjectType& rCaller,
Gui::SelectionSingleton::MessageType Reason) override;
void showDefaultButtons(bool);
void reject() override;
private Q_SLOTS:
void onMaterialSelected(const std::shared_ptr<Materials::Material>& material);
protected:
void changeEvent(QEvent* e) override;
private:
void setupConnections();
void slotChangedObject(const Gui::ViewProvider&, const App::Property& Prop);
void setMaterial(const std::vector<App::DocumentObject*>&);
std::vector<Gui::ViewProvider*> getSelection() const;
std::vector<App::DocumentObject*> getSelectionObjects() const;
private:
class Private;
std::unique_ptr<Private> d;
};
class TaskMaterial: public Gui::TaskView::TaskDialog
{
Q_OBJECT
public:
TaskMaterial();
~TaskMaterial() override;
public:
bool reject() override;
bool isAllowedAlterDocument() const override
{
return true;
}
bool isAllowedAlterView() const override
{
return true;
}
bool isAllowedAlterSelection() const override
{
return true;
}
QDialogButtonBox::StandardButtons getStandardButtons() const override;
private:
DlgMaterialImp* widget;
Gui::TaskView::TaskBox* taskbox;
};
} // namespace MatGui
#endif // MATGUI_DIALOGMATERIALIMP_H

View File

@@ -0,0 +1,461 @@
/***************************************************************************
* 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 <QContextMenuEvent>
#include <QMenu>
#endif
#include <cstring>
#include <QHBoxLayout>
#include <QSpacerItem>
#include <QVBoxLayout>
#include <App/Color.h>
#include <Base/Console.h>
#include <Base/Tools.h>
#include <Gui/Command.h>
#include <Mod/Material/App/Exceptions.h>
#include <Mod/Material/App/MaterialFilter.h>
#include <Mod/Material/App/ModelUuids.h>
#include "MaterialTreeWidget.h"
#include "MaterialsEditor.h"
#include "ui_MaterialsEditor.h"
using Base::Console;
using namespace MatGui;
/** Constructs a Material tree widget.
*/
MaterialTreeWidget::MaterialTreeWidget(std::shared_ptr<Materials::MaterialFilter> filter,
QWidget* parent)
: QWidget(parent)
, m_expanded(false)
, _filter(filter)
{
setup();
}
MaterialTreeWidget::MaterialTreeWidget(QWidget* parent)
: QWidget(parent)
, m_expanded(false)
, _filter(nullptr)
{
setup();
}
void MaterialTreeWidget::setup()
{
getFavorites();
getRecents();
createLayout();
createMaterialTree();
}
/**
* Destroys the widget and detaches it from its parameter group.
*/
MaterialTreeWidget::~MaterialTreeWidget() = default;
void MaterialTreeWidget::createLayout()
{
m_material = new QLineEdit(this);
m_expand = new QPushButton(this);
m_expand->setIcon(style()->standardIcon(QStyle::SP_TitleBarUnshadeButton));
m_materialTree = new QTreeView(this);
m_editor = new QPushButton(tr("Launch editor"), this);
// m_materialTree->setSelectionModel(QAbstractItemView::SingleSelection);
m_materialTree->setSelectionMode(QAbstractItemView::SingleSelection);
m_materialTree->setSelectionBehavior(QAbstractItemView::SelectItems);
auto materialLayout = new QHBoxLayout();
materialLayout->addWidget(m_material);
materialLayout->addWidget(m_expand);
auto treeLayout = new QHBoxLayout();
treeLayout->addWidget(m_materialTree);
auto buttonLayout = new QHBoxLayout();
buttonLayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Preferred));
buttonLayout->addWidget(m_editor);
auto layout = new QVBoxLayout();
layout->setContentsMargins(0, 9, 0, 9);
layout->addItem(materialLayout);
layout->addItem(treeLayout);
layout->addItem(buttonLayout);
setLayout(layout);
// Start in an unexpanded state. Store the state?
openWidgetState(false);
connect(m_expand, &QPushButton::clicked, this, &MaterialTreeWidget::expandClicked);
connect(m_editor, &QPushButton::clicked, this, &MaterialTreeWidget::editorClicked);
}
void MaterialTreeWidget::openWidgetState(bool open)
{
m_materialTree->setVisible(open);
m_editor->setVisible(open);
m_expanded = open;
if (open) {
m_expand->setIcon(style()->standardIcon(QStyle::SP_TitleBarShadeButton));
}
else {
m_expand->setIcon(style()->standardIcon(QStyle::SP_TitleBarUnshadeButton));
}
}
void MaterialTreeWidget::expandClicked(bool checked)
{
Q_UNUSED(checked)
// Toggle the open state
openWidgetState(!m_expanded);
}
void MaterialTreeWidget::editorClicked(bool checked)
{
Q_UNUSED(checked)
MaterialsEditor dialog(_filter, this);
dialog.setModal(true);
if (dialog.exec() == QDialog::Accepted) {
// updateMaterialGeneral();
// _material->resetEditState();
// refreshMaterialTree();
// _materialSelected = true;
auto material = dialog.getMaterial();
updateMaterialTree();
setMaterial(material->getUUID());
}
// Gui::Application::Instance->commandManager().runCommandByName("Materials_Edit");
// Toggle the open state
// openWidgetState(!m_expanded);
}
void MaterialTreeWidget::updateMaterial(const QString& uuid)
{
if (uuid.isEmpty() || uuid == m_uuid) {
return;
}
m_uuid = uuid;
// Fetch the material from the manager
auto material = std::make_shared<Materials::Material>();
try {
material = std::make_shared<Materials::Material>(*getMaterialManager().getMaterial(uuid));
}
catch (Materials::ModelNotFound const&) {
Base::Console().Log("*** Unable to load material '%s'\n", uuid.toStdString().c_str());
}
m_materialDisplay = material->getName();
m_material->setText(m_materialDisplay);
}
bool MaterialTreeWidget::findInTree(const QStandardItem& node,
QModelIndex* index,
const QString& uuid)
{
auto vv = node.data(Qt::UserRole);
if (vv.isValid() && vv == uuid) {
*index = node.index();
return true;
}
if (node.hasChildren()) {
int rows = node.rowCount();
for (int i = 0; i < node.rowCount(); i++) {
auto child = node.child(i);
if (findInTree(*child, index, uuid)) {
return true;
}
}
}
return false;
}
QModelIndex MaterialTreeWidget::findInTree(const QString& uuid)
{
auto model = dynamic_cast<QStandardItemModel*>(m_materialTree->model());
auto root = model->invisibleRootItem();
QModelIndex index;
if (findInTree(*root, &index, uuid)) {
return index;
}
return {};
}
void MaterialTreeWidget::setMaterial(const QString& uuid)
{
if (uuid.isEmpty() || uuid == m_uuid) {
return;
}
updateMaterial(uuid);
// Now select the material in the tree
auto index = findInTree(uuid);
if (index.isValid()) {
QItemSelectionModel* selectionModel = m_materialTree->selectionModel();
selectionModel->select(index, QItemSelectionModel::SelectCurrent);
}
}
QString MaterialTreeWidget::getMaterialUUID() const
{
return m_uuid;
}
void MaterialTreeWidget::setFilter(std::shared_ptr<Materials::MaterialFilter> filter)
{
_filter.reset();
_filter = filter;
updateMaterialTree();
}
void MaterialTreeWidget::updateMaterialTree()
{
_favorites.clear();
_recents.clear();
auto model = dynamic_cast<QStandardItemModel*>(m_materialTree->model());
model->clear();
getFavorites();
getRecents();
fillMaterialTree();
}
void MaterialTreeWidget::getFavorites()
{
_favorites.clear();
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/Favorites");
auto count = param->GetInt("Favorites", 0);
for (int i = 0; static_cast<long>(i) < count; i++) {
QString key = QString::fromLatin1("FAV%1").arg(i);
QString uuid = QString::fromStdString(param->GetASCII(key.toStdString().c_str(), ""));
if (!_filter || _filter->modelIncluded(uuid)) {
_favorites.push_back(uuid);
}
}
}
void MaterialTreeWidget::getRecents()
{
_recents.clear();
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/Recent");
_recentMax = static_cast<int>(param->GetInt("RecentMax", 5));
auto count = param->GetInt("Recent", 0);
for (int i = 0; static_cast<long>(i) < count; i++) {
QString key = QString::fromLatin1("MRU%1").arg(i);
QString uuid = QString::fromStdString(param->GetASCII(key.toStdString().c_str(), ""));
if (!_filter || _filter->modelIncluded(uuid)) {
_recents.push_back(uuid);
}
}
}
void MaterialTreeWidget::createMaterialTree()
{
auto model = new QStandardItemModel(this);
m_materialTree->setModel(model);
m_materialTree->setHeaderHidden(true);
// This needs to be done after the model is set
QItemSelectionModel* selectionModel = m_materialTree->selectionModel();
connect(selectionModel,
&QItemSelectionModel::selectionChanged,
this,
&MaterialTreeWidget::onSelectMaterial);
connect(m_materialTree, &QTreeView::doubleClicked, this, &MaterialTreeWidget::onDoubleClick);
fillMaterialTree();
}
void MaterialTreeWidget::fillMaterialTree()
{
auto model = dynamic_cast<QStandardItemModel*>(m_materialTree->model());
auto lib = new QStandardItem(tr("Favorites"));
lib->setFlags(Qt::ItemIsEnabled);
addExpanded(model, lib);
addFavorites(lib);
lib = new QStandardItem(tr("Recent"));
lib->setFlags(Qt::ItemIsEnabled);
addExpanded(model, lib);
addRecents(lib);
// // Create a filter to only include current format materials
// // that contain the basic render model.
// Materials::MaterialFilter filter;
// filter.setIncludeEmptyFolders(false);
// filter.setIncludeLegacy(false);
// filter.addRequired(Materials::ModelUUIDs::ModelUUID_Rendering_Basic);
auto libraries = _materialManager.getMaterialLibraries();
for (const auto& library : *libraries) {
lib = new QStandardItem(library->getName());
lib->setFlags(Qt::ItemIsEnabled);
addExpanded(model, lib);
QIcon icon(library->getIconPath());
QIcon folderIcon(QString::fromStdString(":/icons/folder.svg"));
auto modelTree = _materialManager.getMaterialTree(library, _filter);
addMaterials(*lib, modelTree, folderIcon, icon);
}
}
void MaterialTreeWidget::addExpanded(QStandardItem* parent, QStandardItem* child)
{
parent->appendRow(child);
m_materialTree->setExpanded(child->index(), true);
}
void MaterialTreeWidget::addExpanded(QStandardItemModel* model, QStandardItem* child)
{
model->appendRow(child);
m_materialTree->setExpanded(child->index(), true);
}
void MaterialTreeWidget::addRecents(QStandardItem* parent)
{
for (auto& uuid : _recents) {
try {
auto material = getMaterialManager().getMaterial(uuid);
QIcon icon = QIcon(material->getLibrary()->getIconPath());
auto card = new QStandardItem(icon, material->getName());
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
card->setData(QVariant(uuid), Qt::UserRole);
addExpanded(parent, card);
}
catch (const Materials::MaterialNotFound&) {
}
}
}
void MaterialTreeWidget::addFavorites(QStandardItem* parent)
{
for (auto& uuid : _favorites) {
try {
auto material = getMaterialManager().getMaterial(uuid);
QIcon icon = QIcon(material->getLibrary()->getIconPath());
auto card = new QStandardItem(icon, material->getName());
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
card->setData(QVariant(uuid), Qt::UserRole);
addExpanded(parent, card);
}
catch (const Materials::MaterialNotFound&) {
}
}
}
void MaterialTreeWidget::addMaterials(
QStandardItem& parent,
const std::shared_ptr<std::map<QString, std::shared_ptr<Materials::MaterialTreeNode>>>&
modelTree,
const QIcon& folderIcon,
const QIcon& icon)
{
for (auto& mat : *modelTree) {
auto nodePtr = mat.second;
if (nodePtr->getType() == Materials::MaterialTreeNode::DataNode) {
auto material = nodePtr->getData();
QString uuid = material->getUUID();
// Base::Console().Log("Material path '%s'\n",
// material->getDirectory().toStdString().c_str());
// auto card = new QStandardItem(icon, material->getName());
auto card = new QStandardItem(icon, mat.first);
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
card->setData(QVariant(uuid), Qt::UserRole);
addExpanded(&parent, card);
}
else {
auto node = new QStandardItem(folderIcon, mat.first);
addExpanded(&parent, node);
node->setFlags(Qt::ItemIsEnabled);
auto treeMap = nodePtr->getFolder();
addMaterials(*node, treeMap, folderIcon, icon);
}
}
}
void MaterialTreeWidget::onSelectMaterial(const QItemSelection& selected,
const QItemSelection& deselected)
{
Q_UNUSED(deselected);
// Get the UUID before changing the underlying data model
QString uuid;
auto model = dynamic_cast<QStandardItemModel*>(m_materialTree->model());
QModelIndexList indexes = selected.indexes();
for (auto it = indexes.begin(); it != indexes.end(); it++) {
QStandardItem* item = model->itemFromIndex(*it);
if (item) {
uuid = item->data(Qt::UserRole).toString();
break;
}
}
updateMaterial(uuid);
std::string _uuid = uuid.toStdString();
Q_EMIT materialSelected(getMaterialManager().getMaterial(uuid));
}
void MaterialTreeWidget::onDoubleClick(const QModelIndex& index)
{
auto model = dynamic_cast<QStandardItemModel*>(m_materialTree->model());
auto item = model->itemFromIndex(index);
if (item) {
auto uuid = item->data(Qt::UserRole).toString();
updateMaterial(uuid);
}
}

View File

@@ -0,0 +1,177 @@
/***************************************************************************
* 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 MATGUI_MATERIALTREEWIDGET_H
#define MATGUI_MATERIALTREEWIDGET_H
#include <memory>
#include <QCheckBox>
#include <QComboBox>
#include <QFontComboBox>
#include <QLineEdit>
#include <QPushButton>
#include <QRadioButton>
#include <QStandardItem>
#include <QStandardItemModel>
#include <QTreeView>
#include <FCGlobal.h>
#include <Base/Parameter.h>
#include <Gui/WidgetFactory.h>
#include <Mod/Material/App/MaterialFilter.h>
#include <Mod/Material/App/MaterialManager.h>
#include <Mod/Material/App/Materials.h>
namespace MatGui
{
class CommandManager;
class WidgetFactoryInst;
/** The Material Tree widget class
* This widget is intended for use wherever materials are used. It is a light weight
* alternative to the full Materials editor.
*
* The widget itself is the combination of a number of smaller widgets. A simple text
* field shows any currently selected material. An arrow will expand a tree to show
* the widget library, allowing the user to select the material they require.
*
* When expanded, the user will be presented the option to launch the full material
* editor. This will allow them to create/copy/modify as required.
*
* Additionally, they will be given the option to create a material card based on the
* current settings.
*
* \author David Carter
*/
class MatGuiExport MaterialTreeWidget: public QWidget
{
Q_OBJECT
public:
explicit MaterialTreeWidget(std::shared_ptr<Materials::MaterialFilter> filter,
QWidget* parent = nullptr);
explicit MaterialTreeWidget(QWidget* parent = nullptr);
~MaterialTreeWidget() override;
// void setEntryName( const QByteArray& name );
// QByteArray entryName() const;
// /** Does the same as setEntryName().
// * This function is added for convenience because the ui compiler
// * will use this function if the attribute stdset isn't set to 0 in a .ui file.
// */
// void setPrefEntry(const QByteArray& name);
// void setParamGrpPath( const QByteArray& path );
// QByteArray paramGrpPath() const;
// /** Does the same as setParamGrpPath().
// * This function is added for convenience because the ui compiler
// * will use this function if the attribute stdset isn't set to 0 in a .ui file.
// */
// void setPrefPath(const QByteArray& name);
// void OnChange(Base::Subject<const char*> &rCaller, const char * sReason) override;
// void onSave();
// void onRestore();
/** Set the material by specifying its UUID
*/
void setMaterial(const QString& uuid);
/** get the material UUID
*/
QString getMaterialUUID() const;
/** Set the material filter
*/
void setFilter(std::shared_ptr<Materials::MaterialFilter> filter);
Q_SIGNALS:
/** Emits this signal when a material has been selected */
void materialSelected(const std::shared_ptr<Materials::Material>& material);
private Q_SLOTS:
void expandClicked(bool checked);
void editorClicked(bool checked);
void onSelectMaterial(const QItemSelection& selected, const QItemSelection& deselected);
void onDoubleClick(const QModelIndex& index);
private:
void setup();
QLineEdit* m_material;
QPushButton* m_expand;
QTreeView* m_materialTree;
QPushButton* m_editor;
bool m_expanded;
QString m_materialDisplay;
QString m_uuid;
std::list<QString> _favorites;
std::list<QString> _recents;
std::shared_ptr<Materials::MaterialFilter> _filter;
int _recentMax;
Materials::MaterialManager _materialManager;
// friends
friend class Gui::WidgetFactoryInst;
protected:
// bool m_Restored = false;
Materials::MaterialManager& getMaterialManager()
{
return _materialManager;
}
void getFavorites();
void getRecents();
/** Create the widgets UI objects
*/
void createLayout();
bool findInTree(const QStandardItem& node, QModelIndex* index, const QString& uuid);
QModelIndex findInTree(const QString& uuid);
void updateMaterial(const QString& uuid);
void createMaterialTree();
void fillMaterialTree();
void updateMaterialTree();
void addExpanded(QStandardItem* parent, QStandardItem* child);
void addExpanded(QStandardItemModel* model, QStandardItem* child);
void addRecents(QStandardItem* parent);
void addFavorites(QStandardItem* parent);
void addMaterials(
QStandardItem& parent,
const std::shared_ptr<std::map<QString, std::shared_ptr<Materials::MaterialTreeNode>>>&
modelTree,
const QIcon& folderIcon,
const QIcon& icon);
void openWidgetState(bool open);
};
} // namespace MatGui
#endif // MATGUI_MATERIALTREEWIDGET_H

View File

@@ -59,14 +59,33 @@ using namespace MatGui;
/* TRANSLATOR MatGui::MaterialsEditor */
MaterialsEditor::MaterialsEditor(std::shared_ptr<Materials::MaterialFilter> filter, QWidget* parent)
: QDialog(parent)
, ui(new Ui_MaterialsEditor)
, _material(std::make_shared<Materials::Material>())
, _materialSelected(false)
, _rendered(nullptr)
, _recentMax(0)
, _filter(filter)
{
setup();
}
MaterialsEditor::MaterialsEditor(QWidget* parent)
: QDialog(parent)
, ui(new Ui_MaterialsEditor)
, _material(std::make_shared<Materials::Material>())
, _materialSelected(false)
, _rendered(nullptr)
, _edited(false)
, _recentMax(0)
, _filter(nullptr)
{
setup();
}
void MaterialsEditor::setup()
{
Gui::WaitCursor wc;
ui->setupUi(this);
_warningIcon = QIcon(QString::fromStdString(":/icons/Warning.svg"));
@@ -158,7 +177,9 @@ void MaterialsEditor::getFavorites()
for (int i = 0; static_cast<long>(i) < count; i++) {
QString key = QString::fromLatin1("FAV%1").arg(i);
QString uuid = QString::fromStdString(param->GetASCII(key.toStdString().c_str(), ""));
_favorites.push_back(uuid);
if (!_filter || _filter->modelIncluded(uuid)) {
_favorites.push_back(uuid);
}
}
}
@@ -234,7 +255,9 @@ void MaterialsEditor::getRecents()
for (int i = 0; static_cast<long>(i) < count; i++) {
QString key = QString::fromLatin1("MRU%1").arg(i);
QString uuid = QString::fromStdString(param->GetASCII(key.toStdString().c_str(), ""));
_recents.push_back(uuid);
if (!_filter || _filter->modelIncluded(uuid)) {
_recents.push_back(uuid);
}
}
}
@@ -342,7 +365,6 @@ void MaterialsEditor::propertyChange(const QString& property, const QString valu
updatePreview();
}
update();
_edited = true;
}
void MaterialsEditor::onURL(bool checked)
@@ -486,6 +508,7 @@ void MaterialsEditor::onNewMaterial(bool checked)
// Create a new material
_material = std::make_shared<Materials::Material>();
setMaterialDefaults();
_materialSelected = false;
}
void MaterialsEditor::onInheritNewMaterial(bool checked)
@@ -548,6 +571,7 @@ void MaterialsEditor::saveMaterial()
updateMaterialGeneral();
_material->resetEditState();
refreshMaterialTree();
_materialSelected = true;
}
}
@@ -1147,6 +1171,7 @@ void MaterialsEditor::onSelectMaterial(const QItemSelection& selected,
updateMaterial();
_material->resetEditState();
_materialSelected = true;
}
void MaterialsEditor::onDoubleClick(const QModelIndex& index)
@@ -1162,6 +1187,7 @@ void MaterialsEditor::onDoubleClick(const QModelIndex& index)
}
}
_materialSelected = true;
accept();
}

View File

@@ -36,6 +36,7 @@
#include <Base/Handle.h>
#include <Base/Parameter.h>
#include <Mod/Material/App/MaterialFilter.h>
#include <Mod/Material/App/MaterialManager.h>
#include <Mod/Material/App/Materials.h>
#include <Mod/Material/App/ModelManager.h>
@@ -52,6 +53,8 @@ class MaterialsEditor: public QDialog
Q_OBJECT
public:
explicit MaterialsEditor(std::shared_ptr<Materials::MaterialFilter> filter,
QWidget* parent = nullptr);
explicit MaterialsEditor(QWidget* parent = nullptr);
~MaterialsEditor() override = default;
@@ -96,6 +99,15 @@ public:
void onDoubleClick(const QModelIndex& index);
void onContextMenu(const QPoint& pos);
bool isMaterialSelected() const
{
return _materialSelected;
}
std::shared_ptr<Materials::Material> getMaterial()
{
return _material;
}
protected:
int confirmSave(QWidget* parent);
void saveMaterial();
@@ -106,11 +118,14 @@ private:
Materials::ModelManager _modelManager;
std::shared_ptr<Materials::Material> _material;
AppearancePreview* _rendered;
bool _edited;
bool _materialSelected;
std::list<QString> _favorites;
std::list<QString> _recents;
int _recentMax;
QIcon _warningIcon;
std::shared_ptr<Materials::MaterialFilter> _filter;
void setup();
void saveWindow();
void saveMaterialTreeChildren(const Base::Reference<ParameterGrp>& param,

View File

@@ -685,9 +685,9 @@ class MaterialEditor:
from importFCMat import write
write(filename, d)
import Material
import Materials
# Load the material
manager = Material.MaterialManager()
manager = Materials.MaterialManager()
manager.getMaterialByPath(filename)
self.edited = False
self.updateCardsInCombo()

View File

@@ -26,7 +26,7 @@
// Material
#ifndef MaterialsExport
#ifdef Material_EXPORTS
#ifdef Materials_EXPORTS
#define MaterialsExport FREECAD_DECL_EXPORT
#else
#define MaterialsExport FREECAD_DECL_IMPORT

View File

@@ -0,0 +1,15 @@
---
# File created by ConvertFCMat.py
General:
UUID: "7f9fd73b-50c9-41d8-b7b2-575a030c1eeb"
Author: "David Carter"
License: "GPL-2.0-or-later"
Name: "Default"
Description: "Generic material with density of 1"
Inherits:
Default:
UUID: "5dbb7be6-8b63-479b-ab4c-87be02ead973"
Models:
Density:
UUID: '454661e5-265b-4320-8e6f-fcf6223ac3af'
Density: "1 kg/m^3"

View File

@@ -35,7 +35,12 @@ AppearanceModel:
Type: 'File'
Units: ''
URL: ''
Description: " "
Description: "Path to file containing a texture image. Only used if TextureImage is unpopulated"
TextureImage:
Type: 'Image'
Units: ''
URL: ''
Description: "Embedded texture image"
TextureScaling:
DisplayName: "Texture Scaling"
Type: 'Float'

View File

@@ -24,7 +24,7 @@
from os import walk
import unittest
import FreeCAD
import Material
import Materials
from materialtests.TestModels import ModelTestCases
from materialtests.TestMaterials import MaterialTestCases

View File

@@ -30,7 +30,7 @@ import os
import FreeCAD
from materialtools.cardutils import get_material_template
import Material
import Materials
if FreeCAD.GuiUp:
from PySide import QtGui
@@ -99,7 +99,7 @@ def decode(name):
# https://github.com/berndhahnebach/FreeCAD_bhb/commits/materialdev
def read(filename):
materialManager = Material.MaterialManager()
materialManager = Materials.MaterialManager()
material = materialManager.getMaterialByPath(filename)
return material.Properties

View File

@@ -26,7 +26,7 @@ Test module for FreeCAD material cards and APIs
import unittest
import FreeCAD
import Material
import Materials
parseQuantity = FreeCAD.Units.parseQuantity
@@ -37,9 +37,9 @@ class MaterialTestCases(unittest.TestCase):
def setUp(self):
""" Setup function to initialize test data """
self.ModelManager = Material.ModelManager()
self.MaterialManager = Material.MaterialManager()
self.uuids = Material.UUIDs()
self.ModelManager = Materials.ModelManager()
self.MaterialManager = Materials.MaterialManager()
self.uuids = Materials.UUIDs()
def testMaterialManager(self):
""" Ensure the MaterialManager has been initialized correctly """

View File

@@ -26,7 +26,7 @@ Test module for FreeCAD material models
import unittest
import FreeCAD
import Material
import Materials
parseQuantity = FreeCAD.Units.parseQuantity
@@ -36,8 +36,8 @@ class ModelTestCases(unittest.TestCase):
"""
def setUp(self):
""" Setup function to initialize test data """
self.ModelManager = Material.ModelManager()
self.uuids = Material.UUIDs()
self.ModelManager = Materials.ModelManager()
self.uuids = Materials.UUIDs()
def testModelManager(self):
""" Ensure we can access ModelManager member functions """

View File

@@ -28,7 +28,7 @@ from os.path import join
from pathlib import Path
import FreeCAD
import Material
import Materials
unicode = str
@@ -260,7 +260,7 @@ def output_resources(resources):
# used in material editor and FEM material task panels
def import_materials(category='Solid', template=False):
materialManager = Material.MaterialManager()
materialManager = Materials.MaterialManager()
mats = materialManager.Materials
materials = {}
cards = {}