Merge pull request #10690 from davesrocketshop/material_merge

Material: Material handling enhancements
This commit is contained in:
Chris Hennes
2023-09-27 16:15:06 -05:00
committed by GitHub
402 changed files with 19356 additions and 4377 deletions

View File

@@ -47,6 +47,13 @@ runs:
testCommand: ${{ inputs.builddir }}/tests/Tests_run --gtest_output=json:${{ inputs.reportdir }}core_gtest_results.json
testLogFile: ${{ inputs.reportdir }}core_gtest_test_log.txt
testName: Core
- name: C++ Material tests
id: material
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
with:
testCommand: ${{ inputs.builddir }}/tests/Material_tests_run --gtest_output=json:${{ inputs.reportdir }}material_gtest_results.json
testLogFile: ${{ inputs.reportdir }}material_gtest_test_log.txt
testName: Material
- name: C++ Mesh tests
id: mesh
uses: ./.github/workflows/actions/runCPPTests/runSingleTest

View File

@@ -51,6 +51,7 @@ CheckInterModuleDependencies()
FreeCADLibpackChecks()
SetupDoxygen()
SetupLibFmt()
SetupYamlCpp()
if(NOT FREECAD_LIBPACK_USE OR FREECAD_LIBPACK_CHECKFILE_CLBUNDLER)
SetupPython()
SetupPCL()

View File

@@ -0,0 +1,4 @@
macro(SetupYamlCpp)
# -------------------------------- YamlCpp --------------------------------
find_package(yaml-cpp REQUIRED)
endmacro(SetupYamlCpp)

View File

@@ -25,8 +25,8 @@
#include <Base/Console.h>
#include <Base/PyObjectBase.h>
#include <Gui/Application.h>
#include <Gui/Language/Translator.h>
#include <Gui/WidgetFactory.h>
#include <Gui/Language/Translator.h>
#include "DlgSettingsFemCcxImp.h"
#include "DlgSettingsFemElmerImp.h"
@@ -34,18 +34,25 @@
#include "DlgSettingsFemGeneralImp.h"
#include "DlgSettingsFemGmshImp.h"
#include "DlgSettingsFemInOutVtkImp.h"
#include "DlgSettingsFemMaterialImp.h"
#include "DlgSettingsFemMystranImp.h"
#include "DlgSettingsFemZ88Imp.h"
#include "PropertyFemMeshItem.h"
#include "ViewProviderAnalysis.h"
#include "ViewProviderFemMesh.h"
#include "ViewProviderFemMeshShape.h"
#include "ViewProviderFemMeshShapeNetgen.h"
#include "ViewProviderSetElements.h"
#include "ViewProviderSetFaces.h"
#include "ViewProviderSetGeometry.h"
#include "ViewProviderSetNodes.h"
#include "ViewProviderSolver.h"
#include "ViewProviderFemConstraint.h"
#include "ViewProviderFemConstraintBearing.h"
#include "ViewProviderFemConstraintContact.h"
#include "ViewProviderFemConstraintDisplacement.h"
#include "ViewProviderFemConstraintFixed.h"
#include "ViewProviderFemConstraintFluidBoundary.h"
#include "ViewProviderFemConstraintForce.h"
#include "ViewProviderFemConstraintFluidBoundary.h"
#include "ViewProviderFemConstraintGear.h"
#include "ViewProviderFemConstraintHeatflux.h"
#include "ViewProviderFemConstraintInitialTemperature.h"
@@ -56,15 +63,7 @@
#include "ViewProviderFemConstraintSpring.h"
#include "ViewProviderFemConstraintTemperature.h"
#include "ViewProviderFemConstraintTransform.h"
#include "ViewProviderFemMesh.h"
#include "ViewProviderFemMeshShape.h"
#include "ViewProviderFemMeshShapeNetgen.h"
#include "ViewProviderResult.h"
#include "ViewProviderSetElements.h"
#include "ViewProviderSetFaces.h"
#include "ViewProviderSetGeometry.h"
#include "ViewProviderSetNodes.h"
#include "ViewProviderSolver.h"
#include "Workbench.h"
#ifdef FC_USE_VTK
@@ -178,7 +177,6 @@ PyMOD_INIT_FUNC(FemGui)
new Gui::PrefPageProducer<FemGui::DlgSettingsFemElmerImp>(QT_TRANSLATE_NOOP("QObject", "FEM"));
new Gui::PrefPageProducer<FemGui::DlgSettingsFemMystranImp>(QT_TRANSLATE_NOOP("QObject", "FEM"));
new Gui::PrefPageProducer<FemGui::DlgSettingsFemZ88Imp>(QT_TRANSLATE_NOOP("QObject", "FEM"));
new Gui::PrefPageProducer<FemGui::DlgSettingsFemMaterialImp>(QT_TRANSLATE_NOOP("QObject", "FEM"));
// register preferences pages on Import-Export
new Gui::PrefPageProducer<FemGui::DlgSettingsFemExportAbaqusImp>(QT_TRANSLATE_NOOP("QObject", "Import-Export"));

View File

@@ -1,8 +1,8 @@
# Many warnings caused by vtk
if(CMAKE_COMPILER_IS_CLANGXX)
add_compile_options(-Wno-pedantic) # needed for vtk headers
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0)
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0)
add_compile_options(-Wno-inconsistent-missing-override)
endif()
elseif(CMAKE_COMPILER_IS_GNUCXX)
@@ -15,12 +15,10 @@ else(MSVC)
add_definitions(-DHAVE_LIMITS_H -DHAVE_CONFIG_H)
endif(MSVC)
if(BUILD_FEM_NETGEN)
add_definitions(-DFCWithNetgen)
endif(BUILD_FEM_NETGEN)
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/src
@@ -37,18 +35,15 @@ include_directories(
link_directories(${OCC_LIBRARY_DIR})
link_directories(${SMESH_LIB_PATH})
set(FemGui_LIBS
Fem
FreeCADGui
PartGui
)
generate_from_xml(ViewProviderFemMeshPy)
generate_from_xml(ViewProviderFemPostPipelinePy)
SET(Python_SRCS
ViewProviderFemMeshPy.xml
ViewProviderFemMeshPyImp.cpp
@@ -57,7 +52,6 @@ SET(Python_SRCS
)
SOURCE_GROUP("Python" FILES ${Python_SRCS})
set(FemGui_UIC_SRCS
DlgSettingsFemCcx.ui
DlgSettingsFemElmer.ui
@@ -65,7 +59,6 @@ set(FemGui_UIC_SRCS
DlgSettingsFemGeneral.ui
DlgSettingsFemGmsh.ui
DlgSettingsFemInOutVtk.ui
DlgSettingsFemMaterial.ui
DlgSettingsFemMystran.ui
DlgSettingsFemZ88.ui
TaskCreateNodeSet.ui
@@ -88,6 +81,7 @@ set(FemGui_UIC_SRCS
TaskAnalysisInfo.ui
TaskDriver.ui
)
if(BUILD_FEM_VTK)
set(FemGui_UIC_SRCS
${FemGui_UIC_SRCS}
@@ -126,9 +120,6 @@ SET(FemGui_DLG_SRCS
DlgSettingsFemInOutVtk.ui
DlgSettingsFemInOutVtkImp.cpp
DlgSettingsFemInOutVtkImp.h
DlgSettingsFemMaterial.ui
DlgSettingsFemMaterialImp.cpp
DlgSettingsFemMaterialImp.h
DlgSettingsFemMystran.ui
DlgSettingsFemMystranImp.cpp
DlgSettingsFemMystranImp.h
@@ -186,14 +177,13 @@ SET(FemGui_DLG_SRCS
)
SOURCE_GROUP("Constraint-Dialogs" FILES ${FemGui_DLG_SRCS})
set (Fem_TR_QRC ${CMAKE_CURRENT_BINARY_DIR}/Resources/Fem_translation.qrc)
set(Fem_TR_QRC ${CMAKE_CURRENT_BINARY_DIR}/Resources/Fem_translation.qrc)
qt_find_and_add_translation(QM_SRCS "Resources/translations/*_*.ts"
${CMAKE_CURRENT_BINARY_DIR}/Resources/translations)
qt_create_resource_file(${Fem_TR_QRC} ${QM_SRCS})
qt_add_resources(FemResource_SRCS Resources/Fem.qrc ${Fem_TR_QRC})
SOURCE_GROUP("Resources" FILES ${FemResource_SRCS})
SET(FemGui_SRCS_ViewProvider
ViewProviderFemMesh.cpp
ViewProviderFemMesh.h
@@ -254,7 +244,6 @@ SET(FemGui_SRCS_ViewProvider
)
SOURCE_GROUP("ViewProvider" FILES ${FemGui_SRCS_ViewProvider})
SET(FemGui_SRCS_TaskBoxes
TaskObjectName.ui
TaskObjectName.cpp
@@ -272,6 +261,7 @@ SET(FemGui_SRCS_TaskBoxes
TaskTetParameter.cpp
TaskTetParameter.h
)
if(BUILD_FEM_VTK)
SET(FemGui_SRCS_TaskBoxes
${FemGui_SRCS_TaskBoxes}
@@ -291,8 +281,8 @@ if(BUILD_FEM_VTK)
TaskPostWarpVector.ui
)
endif(BUILD_FEM_VTK)
SOURCE_GROUP("Task_Boxes" FILES ${FemGui_SRCS_TaskBoxes})
SOURCE_GROUP("Task_Boxes" FILES ${FemGui_SRCS_TaskBoxes})
SET(FemGui_SRCS_TaskDlg
TaskDlgCreateNodeSet.h
@@ -304,7 +294,6 @@ SET(FemGui_SRCS_TaskDlg
)
SOURCE_GROUP("Task_Dialogs" FILES ${FemGui_SRCS_TaskDlg})
SET(FemGui_SRCS_Module
AppFemGui.cpp
AppFemGuiPy.cpp
@@ -325,7 +314,6 @@ SET(FemGui_SRCS_Module
)
SOURCE_GROUP("Module" FILES ${FemGui_SRCS_Module})
if(BUILD_FEM_VTK)
SET(FemGui_SRCS_Post
ViewProviderFemPostObject.h
@@ -336,11 +324,10 @@ if(BUILD_FEM_VTK)
ViewProviderFemPostFunction.cpp
ViewProviderFemPostFilter.h
ViewProviderFemPostFilter.cpp
)
)
SOURCE_GROUP("PostObjects" FILES ${FemGui_SRCS_Post})
endif(BUILD_FEM_VTK)
SET(FemGui_SRCS
${Python_SRCS}
${FemGui_DLG_SRCS}
@@ -365,13 +352,10 @@ SET(FemGuiIcon_SVG
add_library(FemGui SHARED ${FemGui_SRCS} ${FemGuiIcon_SVG})
target_link_libraries(FemGui ${FemGui_LIBS} ${VTK_LIBRARIES})
fc_copy_sources(FemGui "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Mod/Fem" ${FemGuiIcon_SVG})
INSTALL(FILES ${FemGuiIcon_SVG} DESTINATION "${CMAKE_INSTALL_DATADIR}/Mod/Fem/Resources/icons")
# Python modules ui files, they are copied as they are, thus the need not to be added to Fem.qrc
# see https://forum.freecad.org/viewtopic.php?f=10&t=25833
SET(FemGuiPythonUI_SRCS
@@ -409,10 +393,7 @@ fc_copy_sources(FemPythonUi "${CMAKE_BINARY_DIR}/Mod/Fem" ${FemGuiPythonUI_SRCS}
INSTALL(FILES ${FemGuiPythonUI_SRCS} DESTINATION Mod/Fem/Resources/ui)
SET_BIN_DIR(FemGui FemGui /Mod/Fem)
SET_PYTHON_PREFIX_SUFFIX(FemGui)
INSTALL(TARGETS FemGui DESTINATION ${CMAKE_INSTALL_LIBDIR})

View File

@@ -1,75 +0,0 @@
/***************************************************************************
* Copyright (c) 2018 FreeCAD Developers *
* Author: Bernd Hahnebach <bernd@bimstatik.ch> *
* Based on src/Mod/Fem/Gui/DlgSettingsFemElmerImp.cpp *
* *
* 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"
#include "DlgSettingsFemMaterialImp.h"
#include "ui_DlgSettingsFemMaterial.h"
using namespace FemGui;
DlgSettingsFemMaterialImp::DlgSettingsFemMaterialImp(QWidget* parent)
: PreferencePage(parent)
, ui(new Ui_DlgSettingsFemMaterialImp)
{
ui->setupUi(this);
}
DlgSettingsFemMaterialImp::~DlgSettingsFemMaterialImp() = default;
void DlgSettingsFemMaterialImp::saveSettings()
{
ui->cb_use_built_in_materials->onSave();
ui->cb_use_mat_from_config_dir->onSave();
ui->cb_use_mat_from_custom_dir->onSave();
ui->fc_custom_mat_dir->onSave();
ui->cb_delete_duplicates->onSave();
ui->cb_sort_by_resources->onSave();
}
void DlgSettingsFemMaterialImp::loadSettings()
{
ui->cb_use_built_in_materials->onRestore();
ui->cb_use_mat_from_config_dir->onRestore();
ui->cb_use_mat_from_custom_dir->onRestore();
ui->fc_custom_mat_dir->onRestore();
ui->cb_delete_duplicates->onRestore();
ui->cb_sort_by_resources->onRestore();
}
/**
* Sets the strings of the subwidgets using the current language.
*/
void DlgSettingsFemMaterialImp::changeEvent(QEvent* e)
{
if (e->type() == QEvent::LanguageChange) {
ui->retranslateUi(this);
}
else {
QWidget::changeEvent(e);
}
}
#include "moc_DlgSettingsFemMaterialImp.cpp"

View File

@@ -1,55 +0,0 @@
/**************************************************************************
* Copyright (c) 2018 FreeCAD Developers *
* Author: Bernd Hahnebach <bernd@bimstatik.ch> *
* Based on src/Mod/Fem/Gui/DlgSettingsFemElmer.h *
* *
* 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 FEMGUI_DLGSETTINGSFEMMATERIALIMP_H
#define FEMGUI_DLGSETTINGSFEMMATERIALIMP_H
#include <Gui/PropertyPage.h>
#include <memory>
namespace FemGui
{
class Ui_DlgSettingsFemMaterialImp;
class DlgSettingsFemMaterialImp: public Gui::Dialog::PreferencePage
{
Q_OBJECT
public:
explicit DlgSettingsFemMaterialImp(QWidget* parent = nullptr);
~DlgSettingsFemMaterialImp() override;
protected:
void saveSettings() override;
void loadSettings() override;
void changeEvent(QEvent* e) override;
private:
std::unique_ptr<Ui_DlgSettingsFemMaterialImp> ui;
};
} // namespace FemGui
#endif // FEMGUI_DLGSETTINGSFEMMATERIALIMP_H

View File

@@ -0,0 +1,73 @@
/***************************************************************************
* 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_
#endif
#include <Base/Console.h>
#include <Base/Interpreter.h>
#include <Base/PyObjectBase.h>
// #include "Model.h"
#include "MaterialManagerPy.h"
#include "MaterialPy.h"
#include "ModelManagerPy.h"
#include "ModelPropertyPy.h"
#include "ModelPy.h"
namespace Materials
{
class Module: public Py::ExtensionModule<Module>
{
public:
Module()
: Py::ExtensionModule<Module>("Material")
{
initialize("This module is the Material module."); // register with Python
}
~Module() override = default;
private:
};
PyObject* initModule()
{
return Base::Interpreter().addModule(new Module);
}
} // namespace Materials
PyMOD_INIT_FUNC(Material)
{
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");
PyMOD_Return(module);
}

View File

@@ -0,0 +1,99 @@
if(MSVC)
add_definitions(-DFCAppMaterial -DHAVE_ACOSH -DHAVE_ASINH -DHAVE_ATANH)
else(MSVC)
add_definitions(-DHAVE_LIMITS_H -DHAVE_CONFIG_H)
endif(MSVC)
add_definitions(-DYAML_CPP_STATIC_DEFINE)
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_BINARY_DIR}/src
${CMAKE_SOURCE_DIR}/src
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${Boost_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS}
${XercesC_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIR}
${YAML_CPP_INCLUDE_DIR}
)
link_directories(${YAML_CPP_LIBRARY_DIR})
set(Material_LIBS
${Boost_LIBRARIES}
FreeCADApp
)
include_directories(
${QtConcurrent_INCLUDE_DIRS}
)
list(APPEND Material_LIBS
${QtConcurrent_LIBRARIES}
${YAML_CPP_LIBRARIES}
)
generate_from_xml(MaterialManagerPy)
generate_from_xml(MaterialPy)
generate_from_xml(ModelManagerPy)
generate_from_xml(ModelPropertyPy)
generate_from_xml(ModelPy)
SET(Python_SRCS
Exceptions.h
MaterialManagerPy.xml
MaterialManagerPyImpl.cpp
MaterialPy.xml
MaterialPyImpl.cpp
ModelManagerPy.xml
ModelManagerPyImpl.cpp
ModelPropertyPy.xml
ModelPropertyPyImpl.cpp
ModelPy.xml
ModelPyImpl.cpp
)
SOURCE_GROUP("Python" FILES ${Python_SRCS})
SET(Material_SRCS
${Python_SRCS}
AppMaterial.cpp
FolderTree.h
MaterialConfigLoader.cpp
MaterialConfigLoader.h
MaterialLibrary.cpp
MaterialLibrary.h
MaterialLoader.cpp
MaterialLoader.h
MaterialManager.cpp
MaterialManager.h
Materials.cpp
Materials.h
MaterialValue.cpp
MaterialValue.h
Model.cpp
Model.h
ModelLibrary.cpp
ModelLibrary.h
ModelLoader.cpp
ModelLoader.h
ModelManager.cpp
ModelManager.h
ModelUuids.h
PreCompiled.cpp
PreCompiled.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)
endif(FREECAD_USE_PCH)
add_library(Material SHARED ${Material_SRCS})
target_link_libraries(Material ${Material_LIBS})
SET_BIN_DIR(Material Material /Mod/Material)
SET_PYTHON_PREFIX_SUFFIX(Material)
INSTALL(TARGETS Material DESTINATION ${CMAKE_INSTALL_LIBDIR})

View File

@@ -0,0 +1,153 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_EXCEPTIONS_H
#define MATERIAL_EXCEPTIONS_H
#include <Base/BaseClass.h>
#include <Base/Exception.h>
namespace Materials
{
class Uninitialized: public Base::Exception
{
public:
Uninitialized()
{}
explicit Uninitialized(const char* msg)
{
this->setMessage(msg);
}
~Uninitialized() noexcept override = default;
};
class ModelNotFound: public Base::Exception
{
public:
ModelNotFound()
{}
explicit ModelNotFound(const char* msg)
{
this->setMessage(msg);
}
~ModelNotFound() noexcept override = default;
};
class MaterialNotFound: public Base::Exception
{
public:
MaterialNotFound()
{}
explicit MaterialNotFound(const char* msg)
{
this->setMessage(msg);
}
~MaterialNotFound() noexcept override = default;
};
class PropertyNotFound: public Base::Exception
{
public:
PropertyNotFound()
{}
explicit PropertyNotFound(const char* msg)
{
this->setMessage(msg);
}
~PropertyNotFound() noexcept override = default;
};
class LibraryNotFound: public Base::Exception
{
public:
LibraryNotFound()
{}
explicit LibraryNotFound(const char* msg)
{
this->setMessage(msg);
}
~LibraryNotFound() noexcept override = default;
};
class InvalidModel: public Base::Exception
{
public:
InvalidModel()
{}
explicit InvalidModel(const char* msg)
{
this->setMessage(msg);
}
~InvalidModel() noexcept override = default;
};
class InvalidRow: public Base::Exception
{
public:
InvalidRow()
{}
explicit InvalidRow(char* msg)
{
this->setMessage(msg);
}
~InvalidRow() noexcept override = default;
};
class InvalidColumn: public Base::Exception
{
public:
InvalidColumn()
{}
explicit InvalidColumn(char* msg)
{
this->setMessage(msg);
}
~InvalidColumn() noexcept override = default;
};
class InvalidIndex: public Base::Exception
{
public:
InvalidIndex()
{}
explicit InvalidIndex(char* msg)
{
this->setMessage(msg);
}
~InvalidIndex() noexcept override = default;
};
class UnknownValueType: public Base::Exception
{
public:
UnknownValueType()
{}
explicit UnknownValueType(char* msg)
{
this->setMessage(msg);
}
~UnknownValueType() noexcept override = default;
};
} // namespace Materials
#endif // MATERIAL_EXCEPTIONS_H

View File

@@ -0,0 +1,86 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_FOLDERTREE_H
#define MATERIAL_FOLDERTREE_H
#include <QString>
#include <map>
namespace Materials
{
template<class T>
class FolderTreeNode
{
public:
enum NodeType
{
DataNode,
FolderNode
};
FolderTreeNode()
{}
virtual ~FolderTreeNode() = default;
NodeType getType() const
{
return _type;
}
void setType(NodeType type)
{
_type = type;
}
const std::shared_ptr<std::map<QString, FolderTreeNode<T>*>> getFolder() const
{
return _folder;
}
std::shared_ptr<std::map<QString, FolderTreeNode<T>*>> getFolder()
{
return _folder;
}
const T* getData() const
{
return _data;
}
void setFolder(std::shared_ptr<std::map<QString, FolderTreeNode<T>*>> folder)
{
setType(FolderNode);
_folder = folder;
}
void setData(const T* data)
{
setType(DataNode);
_data = data;
}
private:
NodeType _type;
std::shared_ptr<std::map<QString, FolderTreeNode<T>*>> _folder;
const T* _data;
};
} // namespace Materials
#endif // MATERIAL_FOLDERTREE_H

View File

@@ -0,0 +1,395 @@
/***************************************************************************
* 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 <QDirIterator>
#include <QFileInfo>
#include <QString>
#endif
#include <QUuid>
#include <App/Application.h>
#include <Base/Interpreter.h>
#include <fstream>
#include "MaterialConfigLoader.h"
#include "MaterialLoader.h"
#include "Model.h"
#include "ModelUuids.h"
using namespace Materials;
MaterialConfigLoader::MaterialConfigLoader()
{}
bool MaterialConfigLoader::isConfigStyle(const QString& path)
{
std::ifstream infile(path.toStdString());
// Check the first 2 lines for a ";"
for (int i = 0; i < 2; i++) {
std::string line;
if (!std::getline(infile, line)) {
return false;
}
if (line.at(0) != ';') {
return false;
}
}
return true;
}
QString MaterialConfigLoader::getAuthorAndLicense(const QString& path)
{
std::ifstream infile(path.toStdString());
QString noAuthor;
// Skip the first line
std::string line;
if (!std::getline(infile, line)) {
return noAuthor;
}
// The second line has it in a comment
if (!std::getline(infile, line)) {
return noAuthor;
}
std::size_t found = line.find(";");
if (found != std::string::npos) {
return QString::fromStdString(trim_copy(line.substr(found + 1)));
}
return noAuthor;
}
void MaterialConfigLoader::addVectorRendering(const QSettings& fcmat, Material* finalModel)
{
QString sectionFillPattern = value(fcmat, "VectorRendering/SectionFillPattern", "");
QString sectionLinewidth = value(fcmat, "VectorRendering/SectionLinewidth", "");
QString sectionColor = value(fcmat, "VectorRendering/SectionColor", "");
QString viewColor = value(fcmat, "VectorRendering/ViewColor", "");
QString viewFillPattern = value(fcmat, "VectorRendering/ViewFillPattern", "");
QString viewLinewidth = value(fcmat, "VectorRendering/ViewLinewidth", "");
if (sectionFillPattern.length() + sectionLinewidth.length() + sectionColor.length()
+ viewColor.length() + viewFillPattern.length() + viewLinewidth.length()
> 0) {
finalModel->addAppearance(ModelUUID_Rendering_Vector);
}
// Now add the data
setAppearanceValue(finalModel, "SectionFillPattern", sectionFillPattern);
setAppearanceValue(finalModel, "SectionLinewidth", sectionLinewidth);
setAppearanceValue(finalModel, "SectionColor", sectionColor);
setAppearanceValue(finalModel, "ViewColor", viewColor);
setAppearanceValue(finalModel, "ViewFillPattern", viewFillPattern);
setAppearanceValue(finalModel, "ViewLinewidth", viewLinewidth);
}
void MaterialConfigLoader::addRendering(const QSettings& fcmat, Material* finalModel)
{
QString ambientColor = value(fcmat, "Rendering/AmbientColor", "");
QString diffuseColor = value(fcmat, "Rendering/DiffuseColor", "");
QString emissiveColor = value(fcmat, "Rendering/EmissiveColor", "");
QString shininess = value(fcmat, "Rendering/Shininess", "");
QString specularColor = value(fcmat, "Rendering/SpecularColor", "");
QString transparency = value(fcmat, "Rendering/Transparency", "");
QString texturePath = value(fcmat, "Rendering/TexturePath", "");
QString textureScaling = value(fcmat, "Rendering/TextureScaling", "");
QString fragmentShader = value(fcmat, "Rendering/FragmentShader", "");
QString vertexShader = value(fcmat, "Rendering/VertexShader", "");
// Check which model we need
bool useTexture = false;
bool useAdvanced = false;
bool useBasic = false;
if (texturePath.length() + textureScaling.length() > 0) {
useTexture = true;
}
if (fragmentShader.length() + vertexShader.length() > 0) {
useAdvanced = true;
}
if (ambientColor.length() + diffuseColor.length() + emissiveColor.length() + shininess.length()
+ specularColor.length() + transparency.length()
> 0) {
useBasic = true;
}
if (useAdvanced) {
finalModel->addAppearance(ModelUUID_Rendering_Advanced);
}
else if (useTexture) {
finalModel->addAppearance(ModelUUID_Rendering_Texture);
}
else if (useBasic) {
finalModel->addAppearance(ModelUUID_Rendering_Basic);
}
// Now add the data
setAppearanceValue(finalModel, "AmbientColor", ambientColor);
setAppearanceValue(finalModel, "DiffuseColor", diffuseColor);
setAppearanceValue(finalModel, "EmissiveColor", emissiveColor);
setAppearanceValue(finalModel, "Shininess", shininess);
setAppearanceValue(finalModel, "SpecularColor", specularColor);
setAppearanceValue(finalModel, "Transparency", transparency);
setAppearanceValue(finalModel, "TexturePath", texturePath);
setAppearanceValue(finalModel, "TextureScaling", textureScaling);
setAppearanceValue(finalModel, "FragmentShader", fragmentShader);
setAppearanceValue(finalModel, "VertexShader", vertexShader);
}
void MaterialConfigLoader::addCosts(const QSettings& fcmat, Material* finalModel)
{
QString productURL = value(fcmat, "Cost/ProductURL", "");
QString specificPrice = value(fcmat, "Cost/SpecificPrice", "");
QString vendor = value(fcmat, "Cost/Vendor", "");
if (productURL.length() + specificPrice.length() + vendor.length() > 0) {
finalModel->addPhysical(ModelUUID_Costs_Default);
}
// Now add the data
setPhysicalValue(finalModel, "ProductURL", productURL);
setPhysicalValue(finalModel, "SpecificPrice", specificPrice);
setPhysicalValue(finalModel, "Vendor", vendor);
}
void MaterialConfigLoader::addArchitectural(const QSettings& fcmat, Material* finalModel)
{
QString color = value(fcmat, "Architectural/Color", "");
QString environmentalEfficiencyClass =
value(fcmat, "Architectural/EnvironmentalEfficiencyClass", "");
QString executionInstructions = value(fcmat, "Architectural/ExecutionInstructions", "");
QString finish = value(fcmat, "Architectural/Finish", "");
QString fireResistanceClass = value(fcmat, "Architectural/FireResistanceClass", "");
QString model = value(fcmat, "Architectural/Model", "");
QString soundTransmissionClass = value(fcmat, "Architectural/SoundTransmissionClass", "");
QString unitsPerQuantity = value(fcmat, "Architectural/UnitsPerQuantity", "");
if (color.length() + environmentalEfficiencyClass.length() + executionInstructions.length()
+ finish.length() + fireResistanceClass.length() + model.length()
+ soundTransmissionClass.length() + unitsPerQuantity.length()
> 0) {
finalModel->addPhysical(ModelUUID_Architectural_Default);
}
// Now add the data
setPhysicalValue(finalModel, "Color", color);
setPhysicalValue(finalModel, "EnvironmentalEfficiencyClass", environmentalEfficiencyClass);
setPhysicalValue(finalModel, "ExecutionInstructions", executionInstructions);
setPhysicalValue(finalModel, "Finish", finish);
setPhysicalValue(finalModel, "FireResistanceClass", fireResistanceClass);
setPhysicalValue(finalModel, "Model", model);
setPhysicalValue(finalModel, "SoundTransmissionClass", soundTransmissionClass);
setPhysicalValue(finalModel, "UnitsPerQuantity", unitsPerQuantity);
}
void MaterialConfigLoader::addElectromagnetic(const QSettings& fcmat, Material* finalModel)
{
QString relativePermittivity = value(fcmat, "Electromagnetic/RelativePermittivity", "");
QString electricalConductivity = value(fcmat, "Electromagnetic/ElectricalConductivity", "");
QString relativePermeability = value(fcmat, "Electromagnetic/RelativePermeability", "");
if (relativePermittivity.length() + electricalConductivity.length()
+ relativePermeability.length()
> 0) {
finalModel->addPhysical(ModelUUID_Electromagnetic_Default);
}
// Now add the data
setPhysicalValue(finalModel, "RelativePermittivity", relativePermittivity);
setPhysicalValue(finalModel, "ElectricalConductivity", electricalConductivity);
setPhysicalValue(finalModel, "RelativePermeability", relativePermeability);
}
void MaterialConfigLoader::addThermal(const QSettings& fcmat, Material* finalModel)
{
QString specificHeat = value(fcmat, "Thermal/SpecificHeat", "");
QString thermalConductivity = value(fcmat, "Thermal/ThermalConductivity", "");
QString thermalExpansionCoefficient = value(fcmat, "Thermal/ThermalExpansionCoefficient", "");
if (specificHeat.length() + thermalConductivity.length() + thermalExpansionCoefficient.length()
> 0) {
finalModel->addPhysical(ModelUUID_Thermal_Default);
}
// Now add the data
setPhysicalValue(finalModel, "SpecificHeat", specificHeat);
setPhysicalValue(finalModel, "ThermalConductivity", thermalConductivity);
setPhysicalValue(finalModel, "ThermalExpansionCoefficient", thermalExpansionCoefficient);
}
void MaterialConfigLoader::addFluid(const QSettings& fcmat, Material* finalModel)
{
QString density = value(fcmat, "Fluidic/Density", "");
QString dynamicViscosity = value(fcmat, "Fluidic/DynamicViscosity", "");
QString kinematicViscosity = value(fcmat, "Fluidic/KinematicViscosity", "");
QString prandtlNumber = value(fcmat, "Fluidic/PrandtlNumber", "");
// Check which model we need
bool useDensity = false;
bool useFluid = false;
if (density.length() > 0) {
useDensity = true;
}
if (dynamicViscosity.length() + kinematicViscosity.length() + prandtlNumber.length() > 0) {
useFluid = true;
}
if (useFluid) {
finalModel->addPhysical(ModelUUID_Fluid_Default);
}
else if (useDensity) {
finalModel->addPhysical(ModelUUID_Mechanical_Density);
}
// Now add the data
setPhysicalValue(finalModel, "Density", density);
setPhysicalValue(finalModel, "DynamicViscosity", dynamicViscosity);
setPhysicalValue(finalModel, "KinematicViscosity", kinematicViscosity);
setPhysicalValue(finalModel, "PrandtlNumber", prandtlNumber);
}
void MaterialConfigLoader::addMechanical(const QSettings& fcmat, Material* finalModel)
{
QString density = value(fcmat, "Mechanical/Density", "");
QString bulkModulus = value(fcmat, "Mechanical/BulkModulus", "");
QString poissonRatio = value(fcmat, "Mechanical/PoissonRatio", "");
QString shearModulus = value(fcmat, "Mechanical/ShearModulus", "");
QString youngsModulus = value(fcmat, "Mechanical/YoungsModulus", "");
QString angleOfFriction = value(fcmat, "Mechanical/AngleOfFriction", "");
QString compressiveStrength = value(fcmat, "Mechanical/CompressiveStrength", "");
QString fractureToughness = value(fcmat, "Mechanical/FractureToughness", "");
QString ultimateStrain = value(fcmat, "Mechanical/UltimateStrain", "");
QString ultimateTensileStrength = value(fcmat, "Mechanical/UltimateTensileStrength", "");
QString yieldStrength = value(fcmat, "Mechanical/YieldStrength", "");
QString stiffness = value(fcmat, "Mechanical/Stiffness", "");
// Check which model we need
bool useDensity = false;
bool useIso = false;
bool useLinearElastic = false;
if (density.length() > 0) {
useDensity = true;
}
if (bulkModulus.length() + poissonRatio.length() + shearModulus.length()
+ youngsModulus.length()
> 0) {
useIso = true;
}
if (angleOfFriction.length() + compressiveStrength.length() + fractureToughness.length()
+ ultimateStrain.length() + ultimateTensileStrength.length() + yieldStrength.length()
+ stiffness.length()
> 0) {
useLinearElastic = true;
}
if (useLinearElastic) {
finalModel->addPhysical(ModelUUID_Mechanical_LinearElastic);
}
else {
if (useIso) {
finalModel->addPhysical(ModelUUID_Mechanical_IsotropicLinearElastic);
}
if (useDensity) {
finalModel->addPhysical(ModelUUID_Mechanical_Density);
}
}
// Now add the data
setPhysicalValue(finalModel, "Density", density);
setPhysicalValue(finalModel, "BulkModulus", bulkModulus);
setPhysicalValue(finalModel, "PoissonRatio", poissonRatio);
setPhysicalValue(finalModel, "ShearModulus", shearModulus);
setPhysicalValue(finalModel, "YoungsModulus", youngsModulus);
setPhysicalValue(finalModel, "AngleOfFriction", angleOfFriction);
setPhysicalValue(finalModel, "CompressiveStrength", compressiveStrength);
setPhysicalValue(finalModel, "FractureToughness", fractureToughness);
setPhysicalValue(finalModel, "UltimateStrain", ultimateStrain);
setPhysicalValue(finalModel, "UltimateTensileStrength", ultimateTensileStrength);
setPhysicalValue(finalModel, "YieldStrength", yieldStrength);
setPhysicalValue(finalModel, "Stiffness", stiffness);
}
Material* MaterialConfigLoader::getMaterialFromPath(const MaterialLibrary& library,
const QString& path)
{
QString authorAndLicense = getAuthorAndLicense(path);
QSettings fcmat(path, QSettings::IniFormat);
// General section
// QString name = value(fcmat, "Name", ""); - always get the name from the filename
QFileInfo filepath(path);
QString name =
filepath.fileName().remove(QString::fromStdString(".FCMat"), Qt::CaseInsensitive);
QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
QString description = value(fcmat, "Description", "");
QString sourceReference = value(fcmat, "ReferenceSource", "");
QString sourceURL = value(fcmat, "SourceURL", "");
Material* finalModel = new Material(library, path, uuid, name);
finalModel->setAuthorAndLicense(authorAndLicense);
finalModel->setDescription(description);
finalModel->setReference(sourceReference);
finalModel->setURL(sourceURL);
QString father = value(fcmat, "Father", "");
if (father.length() > 0) {
finalModel->addPhysical(ModelUUID_Legacy_Father);
// Now add the data
setPhysicalValue(finalModel, "Father", father);
}
QString kindOfMaterial = value(fcmat, "KindOfMaterial", "");
QString materialNumber = value(fcmat, "MaterialNumber", "");
QString norm = value(fcmat, "Norm", "");
QString standardCode = value(fcmat, "StandardCode", "");
if (kindOfMaterial.length() + materialNumber.length() + norm.length() + standardCode.length()
> 0) {
finalModel->addPhysical(ModelUUID_Legacy_MaterialStandard);
// Now add the data
setPhysicalValue(finalModel, "KindOfMaterial", kindOfMaterial);
setPhysicalValue(finalModel, "MaterialNumber", materialNumber);
setPhysicalValue(finalModel, "StandardCode", norm); // Norm is the same as StandardCode
setPhysicalValue(finalModel, "StandardCode", standardCode);
}
// Add the remaining sections
addMechanical(fcmat, finalModel);
addFluid(fcmat, finalModel);
addThermal(fcmat, finalModel);
addElectromagnetic(fcmat, finalModel);
addArchitectural(fcmat, finalModel);
addCosts(fcmat, finalModel);
addRendering(fcmat, finalModel);
addVectorRendering(fcmat, finalModel);
return finalModel;
}

View File

@@ -0,0 +1,80 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_MATERIALCONFIGLOADER_H
#define MATERIAL_MATERIALCONFIGLOADER_H
#include <QDir>
#include <QSettings>
#include <QString>
#include "Materials.h"
namespace Materials
{
class MaterialConfigLoader
{
public:
MaterialConfigLoader();
virtual ~MaterialConfigLoader() = default;
static bool isConfigStyle(const QString& path);
static Material* getMaterialFromPath(const MaterialLibrary& library, const QString& path);
private:
static QString
value(const QSettings& fcmat, const std::string& name, const std::string& defaultValue)
{
return fcmat.value(QString::fromStdString(name), QString::fromStdString(defaultValue))
.toString();
}
static void
setPhysicalValue(Material* finalModel, const std::string& name, const QString& value)
{
if (value.length() > 0) {
finalModel->setPhysicalValue(QString::fromStdString(name), value);
}
}
static void
setAppearanceValue(Material* finalModel, const std::string& name, const QString& value)
{
if (value.length() > 0) {
finalModel->setAppearanceValue(QString::fromStdString(name), value);
}
}
static QString getAuthorAndLicense(const QString& path);
static void addMechanical(const QSettings& fcmat, Material* finalModel);
static void addFluid(const QSettings& fcmat, Material* finalModel);
static void addThermal(const QSettings& fcmat, Material* finalModel);
static void addElectromagnetic(const QSettings& fcmat, Material* finalModel);
static void addArchitectural(const QSettings& fcmat, Material* finalModel);
static void addCosts(const QSettings& fcmat, Material* finalModel);
static void addRendering(const QSettings& fcmat, Material* finalModel);
static void addVectorRendering(const QSettings& fcmat, Material* finalModel);
};
} // namespace Materials
#endif // MATERIAL_MATERIALCONFIGLOADER_H

View File

@@ -0,0 +1,160 @@
/***************************************************************************
* 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_
#endif
#include <App/Application.h>
#include "MaterialLibrary.h"
#include "Materials.h"
#include "ModelManager.h"
using namespace Materials;
/* TRANSLATOR Material::Materials */
std::unique_ptr<std::map<QString, Material*>> MaterialLibrary::_materialPathMap =
std::make_unique<std::map<QString, Material*>>();
TYPESYSTEM_SOURCE(Materials::MaterialLibrary, LibraryBase)
MaterialLibrary::MaterialLibrary()
{}
MaterialLibrary::MaterialLibrary(const QString& libraryName,
const QString& dir,
const QString& icon,
bool readOnly)
: LibraryBase(libraryName, dir, icon)
, _readOnly(readOnly)
{}
void MaterialLibrary::createPath(const QString& path)
{
Q_UNUSED(path)
}
Material* MaterialLibrary::saveMaterial(Material& material, const QString& path, bool saveAsCopy)
{
QString filePath = getLocalPath(path);
Base::Console().Log("\tfilePath = '%s'\n", filePath.toStdString().c_str());
QFile file(filePath);
// Update UUID if required
// if name changed true
if (material.getName() != file.fileName()) {
material.newUuid();
}
// if overwrite false having warned the user
// if old format true, but already set
QFileInfo info(file);
QDir fileDir(info.path());
if (!fileDir.exists()) {
if (!fileDir.mkpath(info.path())) {
Base::Console().Error("Unable to create directory path '%s'\n",
info.path().toStdString().c_str());
}
}
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream stream(&file);
stream.setCodec("UTF-8");
stream.setGenerateByteOrderMark(true);
// Write the contents
material.setLibrary(*this);
material.setDirectory(getRelativePath(path));
material.save(stream, saveAsCopy);
}
return addMaterial(material, path);
}
Material* MaterialLibrary::addMaterial(const Material& material, const QString& path)
{
QString filePath = getRelativePath(path);
Material* newMaterial = new Material(material);
newMaterial->setLibrary(*this);
newMaterial->setDirectory(filePath);
try {
// If there's already a material at that path we'll replace it
Material* old = _materialPathMap->at(filePath);
delete old;
}
catch (const std::out_of_range&) {
}
(*_materialPathMap)[filePath] = newMaterial;
return newMaterial;
}
const Material& MaterialLibrary::getMaterialByPath(const QString& path) const
{
// Base::Console().Log("MaterialLibrary::getMaterialByPath(%s)\n", path.toStdString().c_str());
// for (auto itp = _materialPathMap->begin(); itp != _materialPathMap->end(); itp++) {
// Base::Console().Log("\tpath = '%s'\n", itp->first.toStdString().c_str());
// }
QString filePath = getRelativePath(path);
try {
Material* material = _materialPathMap->at(filePath);
return *material;
}
catch (std::out_of_range& e) {
throw MaterialNotFound();
}
}
const QString MaterialLibrary::getUUIDFromPath(const QString& path) const
{
QString filePath = getRelativePath(path);
try {
Material* material = _materialPathMap->at(filePath);
return material->getUUID();
}
catch (std::out_of_range& e) {
throw MaterialNotFound();
}
}
TYPESYSTEM_SOURCE(Materials::MaterialExternalLibrary, MaterialLibrary::MaterialLibrary)
MaterialExternalLibrary::MaterialExternalLibrary()
{}
MaterialExternalLibrary::MaterialExternalLibrary(const QString& libraryName,
const QString& dir,
const QString& icon,
bool readOnly)
: MaterialLibrary(libraryName, dir, icon, readOnly)
{}
MaterialExternalLibrary::~MaterialExternalLibrary()
{
// delete directory;
}

View File

@@ -0,0 +1,96 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_MATERIALLIBRARY_H
#define MATERIAL_MATERIALLIBRARY_H
#include <Mod/Material/MaterialGlobal.h>
#include <Base/BaseClass.h>
#include <QDir>
#include <QString>
#include <QVariant>
#include "Model.h"
#include "ModelLibrary.h"
namespace Materials
{
class Material;
class MaterialsExport MaterialLibrary: public LibraryBase
{
TYPESYSTEM_HEADER();
public:
MaterialLibrary();
explicit MaterialLibrary(const QString& libraryName,
const QString& dir,
const QString& icon,
bool readOnly = true);
~MaterialLibrary() override = default;
bool operator==(const MaterialLibrary& library) const
{
return LibraryBase::operator==(library);
}
bool operator!=(const MaterialLibrary& library) const
{
return !operator==(library);
}
const Material& getMaterialByPath(const QString& path) const;
void createPath(const QString& path);
Material* saveMaterial(Material& material, const QString& path, bool saveAsCopy);
Material* addMaterial(const Material& material, const QString& path);
bool isReadOnly() const
{
return _readOnly;
}
protected:
const QString getUUIDFromPath(const QString& path) const;
bool _readOnly;
static std::unique_ptr<std::map<QString, Material*>> _materialPathMap;
};
class MaterialsExport MaterialExternalLibrary: public MaterialLibrary
{
TYPESYSTEM_HEADER();
public:
MaterialExternalLibrary();
explicit MaterialExternalLibrary(const QString& libraryName,
const QString& dir,
const QString& icon,
bool readOnly = true);
~MaterialExternalLibrary() override;
};
} // namespace Materials
Q_DECLARE_METATYPE(Materials::MaterialLibrary)
Q_DECLARE_METATYPE(Materials::MaterialExternalLibrary)
#endif // MATERIAL_MATERIALLIBRARY_H

View File

@@ -0,0 +1,445 @@
/***************************************************************************
* 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 <QString>
#endif
#include <App/Application.h>
#include <Base/Interpreter.h>
#include <QDirIterator>
#include <QFileInfo>
#include "MaterialConfigLoader.h"
#include "MaterialLoader.h"
#include "Model.h"
#include "ModelManager.h"
using namespace Materials;
MaterialEntry::MaterialEntry()
{}
MaterialEntry::MaterialEntry(const MaterialLibrary& library,
const QString& modelName,
const QString& dir,
const QString& modelUuid)
: _library(library)
, _name(modelName)
, _directory(dir)
, _uuid(modelUuid)
{}
MaterialYamlEntry::MaterialYamlEntry(const MaterialLibrary& library,
const QString& modelName,
const QString& dir,
const QString& modelUuid,
const YAML::Node& modelData)
: MaterialEntry(library, modelName, dir, modelUuid)
, _model(modelData)
{}
// MaterialYamlEntry::~MaterialYamlEntry()
// {}
QString MaterialYamlEntry::yamlValue(const YAML::Node& node,
const std::string& key,
const std::string& defaultValue)
{
if (node[key]) {
return QString::fromStdString(node[key].as<std::string>());
}
return QString::fromStdString(defaultValue);
}
void MaterialYamlEntry::addToTree(std::shared_ptr<std::map<QString, Material*>> materialMap)
{
std::set<QString> exclude;
exclude.insert(QString::fromStdString("General"));
exclude.insert(QString::fromStdString("Inherits"));
auto yamlModel = getModel();
auto library = getLibrary();
auto name = getName();
auto directory = getDirectory();
QString uuid = getUUID();
QString authorAndLicense = yamlValue(yamlModel["General"], "AuthorAndLicense", "");
QString description = yamlValue(yamlModel["General"], "Description", "");
Material* finalModel = new Material(library, directory, uuid, name);
finalModel->setAuthorAndLicense(authorAndLicense);
finalModel->setDescription(description);
// Add inheritance list
if (yamlModel["Inherits"]) {
auto inherits = yamlModel["Inherits"];
for (auto it = inherits.begin(); it != inherits.end(); it++) {
std::string nodeName = it->second["UUID"].as<std::string>();
finalModel->setParentUUID(
QString::fromStdString(nodeName)); // Should only be one. Need to check
}
}
// Add material models
if (yamlModel["Models"]) {
auto models = yamlModel["Models"];
for (auto it = models.begin(); it != models.end(); it++) {
std::string modelName = (it->first).as<std::string>();
// Add the model uuid
auto modelNode = models[modelName];
std::string modelUUID = modelNode["UUID"].as<std::string>();
finalModel->addPhysical(QString::fromStdString(modelUUID));
// Add the property values
auto properties = yamlModel["Models"][modelName];
for (auto itp = properties.begin(); itp != properties.end(); itp++) {
std::string propertyName = (itp->first).as<std::string>();
std::string propertyValue = (itp->second).as<std::string>();
if (finalModel->hasPhysicalProperty(QString::fromStdString(propertyName))) {
finalModel->setPhysicalValue(QString::fromStdString(propertyName),
QString::fromStdString(propertyValue));
}
else if (propertyName != "UUID") {
Base::Console().Log("\tProperty '%s' is not described by any model. Ignored\n",
propertyName.c_str());
}
}
}
}
// Add appearance models
if (yamlModel["AppearanceModels"]) {
auto models = yamlModel["AppearanceModels"];
for (auto it = models.begin(); it != models.end(); it++) {
std::string modelName = (it->first).as<std::string>();
// Add the model uuid
auto modelNode = models[modelName];
std::string modelUUID = modelNode["UUID"].as<std::string>();
finalModel->addAppearance(QString::fromStdString(modelUUID));
// Add the property values
auto properties = yamlModel["AppearanceModels"][modelName];
for (auto itp = properties.begin(); itp != properties.end(); itp++) {
std::string propertyName = (itp->first).as<std::string>();
std::string propertyValue = (itp->second).as<std::string>();
if (finalModel->hasAppearanceProperty(QString::fromStdString(propertyName))) {
finalModel->setAppearanceValue(QString::fromStdString(propertyName),
QString::fromStdString(propertyValue));
}
else if (propertyName != "UUID") {
Base::Console().Log("\tProperty '%s' is not described by any model. Ignored\n",
propertyName.c_str());
}
}
}
}
QString path = QDir(directory).absolutePath();
// Base::Console().Log("\tPath '%s'\n", path.toStdString().c_str());
(*materialMap)[uuid] = library.addMaterial(*finalModel, path);
}
std::unique_ptr<std::map<QString, MaterialEntry*>> MaterialLoader::_materialEntryMap = nullptr;
MaterialLoader::MaterialLoader(std::shared_ptr<std::map<QString, Material*>> materialMap,
std::shared_ptr<std::list<MaterialLibrary*>> libraryList)
: _materialMap(materialMap)
, _libraryList(libraryList)
{
loadLibraries();
}
/*
* Destroys the object and frees any allocated resources
*/
MaterialLoader::~MaterialLoader()
{}
void MaterialLoader::addLibrary(MaterialLibrary* model)
{
_libraryList->push_back(model);
}
MaterialEntry* MaterialLoader::getMaterialFromPath(MaterialLibrary& library,
const QString& path) const
{
MaterialEntry* model = nullptr;
// Used for debugging
std::string uuid;
std::string pathName = path.toStdString();
if (MaterialConfigLoader::isConfigStyle(path)) {
Base::Console().Log("Old format .FCMat file: '%s'\n", pathName.c_str());
Material* material = MaterialConfigLoader::getMaterialFromPath(library, path);
if (material) {
(*_materialMap)[material->getUUID()] = library.addMaterial(*material, path);
}
// Return the nullptr as there are no intermediate steps to take, such
// as checking inheritance
return model;
}
YAML::Node yamlroot;
try {
yamlroot = YAML::LoadFile(pathName);
const std::string uuid = yamlroot["General"]["UUID"].as<std::string>();
// Always get the name from the filename
// QString name = QString::fromStdString(yamlroot["General"]["Name"].as<std::string>());
QFileInfo filepath(path);
QString name =
filepath.fileName().remove(QString::fromStdString(".FCMat"), Qt::CaseInsensitive);
model = new MaterialYamlEntry(library, name, path, QString::fromStdString(uuid), yamlroot);
// showYaml(yamlroot);
}
catch (YAML::Exception const& e) {
Base::Console().Error("YAML parsing error: '%s'\n", pathName.c_str());
Base::Console().Error("\t'%s'\n", e.what());
showYaml(yamlroot);
}
return model;
}
void MaterialLoader::showYaml(const YAML::Node& yaml)
{
std::stringstream out;
out << yaml;
std::string logData = out.str();
Base::Console().Log("%s\n", logData.c_str());
}
void MaterialLoader::dereference(Material* material)
{
// Avoid recursion
if (material->getDereferenced()) {
return;
}
// Base::Console().Log("Dereferencing material '%s'.\n",
// material->getName().toStdString().c_str());
auto parentUUID = material->getParentUUID();
if (parentUUID.size() > 0) {
Material* parent;
try {
parent = (*_materialMap)[parentUUID];
}
catch (std::out_of_range& e) {
Base::Console().Log(
"Unable to apply inheritance for material '%s', parent '%s' not found.\n",
material->getName().toStdString().c_str(),
parentUUID.toStdString().c_str());
}
// Ensure the parent has been dereferenced
dereference(parent);
// Add physical models
auto modelVector = parent->getPhysicalModels();
for (auto model = modelVector->begin(); model != modelVector->end(); model++) {
if (!material->hasPhysicalModel(*model)) {
material->addPhysical(*model);
}
}
// Add appearance models
modelVector = parent->getAppearanceModels();
for (auto model = modelVector->begin(); model != modelVector->end(); model++) {
if (!material->hasAppearanceModel(*model)) {
material->addAppearance(*model);
}
}
// Add values
auto properties = parent->getPhysicalProperties();
for (auto itp = properties.begin(); itp != properties.end(); itp++) {
auto name = itp->first;
auto property = static_cast<const MaterialProperty>(itp->second);
if (material->getPhysicalProperty(name).isNull()) {
material->getPhysicalProperty(name).setValue(property.getValue());
}
}
properties = parent->getAppearanceProperties();
for (auto itp = properties.begin(); itp != properties.end(); itp++) {
auto name = itp->first;
auto property = static_cast<const MaterialProperty>(itp->second);
if (material->getAppearanceProperty(name).isNull()) {
material->getAppearanceProperty(name).setValue(property.getValue());
}
}
}
material->markDereferenced();
}
void MaterialLoader::loadLibrary(MaterialLibrary& library)
{
if (_materialEntryMap == nullptr) {
_materialEntryMap = std::make_unique<std::map<QString, MaterialEntry*>>();
}
QDirIterator it(library.getDirectory(), QDirIterator::Subdirectories);
while (it.hasNext()) {
auto pathname = it.next();
QFileInfo file(pathname);
if (file.isFile()) {
if (file.suffix().toStdString() == "FCMat") {
QString libraryName = file.baseName();
auto model = getMaterialFromPath(library, file.canonicalFilePath());
if (model) {
(*_materialEntryMap)[model->getUUID()] = model;
}
}
}
}
for (auto it = _materialEntryMap->begin(); it != _materialEntryMap->end(); it++) {
it->second->addToTree(_materialMap);
}
}
void MaterialLoader::loadLibraries()
{
auto _libraryList = getMaterialLibraries();
if (_libraryList) {
for (auto it = _libraryList->begin(); it != _libraryList->end(); it++) {
loadLibrary(**it);
}
}
for (auto it = _materialMap->begin(); it != _materialMap->end(); it++) {
dereference(it->second);
}
}
std::shared_ptr<std::list<MaterialLibrary*>> MaterialLoader::getMaterialLibraries()
{
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/Resources");
bool useBuiltInMaterials = param->GetBool("UseBuiltInMaterials", true);
bool useMatFromModules = param->GetBool("UseMaterialsFromWorkbenches", true);
bool useMatFromConfigDir = param->GetBool("UseMaterialsFromConfigDir", true);
bool useMatFromCustomDir = param->GetBool("UseMaterialsFromCustomDir", true);
if (useBuiltInMaterials) {
QString resourceDir = QString::fromStdString(App::Application::getResourceDir()
+ "/Mod/Material/Resources/Materials");
auto libData = new MaterialLibrary(QString::fromStdString("System"),
resourceDir,
QString::fromStdString(":/icons/freecad.svg"),
true);
_libraryList->push_back(libData);
}
if (useMatFromModules) {
auto moduleParam = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/Resources/Modules");
for (auto& group : moduleParam->GetGroups()) {
// auto module = moduleParam->GetGroup(group->GetGroupName());
auto moduleName = QString::fromStdString(group->GetGroupName());
auto materialDir = QString::fromStdString(group->GetASCII("ModuleDir", ""));
auto materialIcon = QString::fromStdString(group->GetASCII("ModuleIcon", ""));
auto materialReadOnly = group->GetBool("ModuleReadOnly", true);
if (materialDir.length() > 0) {
QDir dir(materialDir);
if (dir.exists()) {
auto libData = new MaterialLibrary(moduleName,
materialDir,
materialIcon,
materialReadOnly);
_libraryList->push_back(libData);
}
}
}
}
if (useMatFromConfigDir) {
QString resourceDir =
QString::fromStdString(App::Application::getUserAppDataDir() + "/Material");
if (!resourceDir.isEmpty()) {
QDir materialDir(resourceDir);
if (materialDir.exists()) {
auto libData =
new MaterialLibrary(QString::fromStdString("User"),
resourceDir,
QString::fromStdString(":/icons/preferences-general.svg"),
false);
_libraryList->push_back(libData);
}
}
}
if (useMatFromCustomDir) {
QString resourceDir = QString::fromStdString(param->GetASCII("CustomMaterialsDir", ""));
if (!resourceDir.isEmpty()) {
QDir materialDir(resourceDir);
if (materialDir.exists()) {
auto libData = new MaterialLibrary(QString::fromStdString("Custom"),
resourceDir,
QString::fromStdString(":/icons/user.svg"),
false);
_libraryList->push_back(libData);
}
}
}
return _libraryList;
}
std::shared_ptr<std::list<QString>>
MaterialLoader::getMaterialFolders(const MaterialLibrary& library)
{
std::shared_ptr<std::list<QString>> pathList = std::make_shared<std::list<QString>>();
QDirIterator it(library.getDirectory(), QDirIterator::Subdirectories);
while (it.hasNext()) {
auto pathname = it.next();
QFileInfo file(pathname);
if (file.isDir()) {
QString path = QDir(library.getDirectory()).relativeFilePath(file.absoluteFilePath());
if (!path.startsWith(QString::fromStdString("."))) {
pathList->push_back(path);
}
}
}
return pathList;
}

View File

@@ -0,0 +1,128 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_MATERIALLOADER_H
#define MATERIAL_MATERIALLOADER_H
#include <QDir>
#include <QString>
#include <yaml-cpp/yaml.h>
#include "Materials.h"
#include "trim.h"
namespace Materials
{
class MaterialEntry
{
public:
MaterialEntry();
explicit MaterialEntry(const MaterialLibrary& library,
const QString& modelName,
const QString& dir,
const QString& modelUuid);
virtual ~MaterialEntry() = default;
virtual void addToTree(std::shared_ptr<std::map<QString, Material*>> materialMap) = 0;
const MaterialLibrary& getLibrary() const
{
return _library;
}
const QString getName() const
{
return _name;
}
const QString getDirectory() const
{
return _directory;
}
const QString getUUID() const
{
return _uuid;
}
protected:
MaterialLibrary _library;
QString _name;
QString _directory;
QString _uuid;
};
class MaterialYamlEntry: public MaterialEntry
{
public:
explicit MaterialYamlEntry(const MaterialLibrary& library,
const QString& modelName,
const QString& dir,
const QString& modelUuid,
const YAML::Node& modelData);
~MaterialYamlEntry() override = default;
void addToTree(std::shared_ptr<std::map<QString, Material*>> materialMap) override;
const YAML::Node& getModel() const
{
return _model;
}
YAML::Node* getModelPtr()
{
return &_model;
}
private:
MaterialYamlEntry();
QString
yamlValue(const YAML::Node& node, const std::string& key, const std::string& defaultValue);
YAML::Node _model;
};
class MaterialLoader
{
public:
explicit MaterialLoader(std::shared_ptr<std::map<QString, Material*>> materialMap,
std::shared_ptr<std::list<MaterialLibrary*>> libraryList);
virtual ~MaterialLoader();
std::shared_ptr<std::list<MaterialLibrary*>> getMaterialLibraries();
static std::shared_ptr<std::list<QString>> getMaterialFolders(const MaterialLibrary& library);
static void showYaml(const YAML::Node& yaml);
private:
MaterialLoader();
void addToTree(MaterialEntry* model);
void dereference(Material* material);
MaterialEntry* getMaterialFromPath(MaterialLibrary& library, const QString& path) const;
void addLibrary(MaterialLibrary* model);
void loadLibrary(MaterialLibrary& library);
void loadLibraries();
static std::unique_ptr<std::map<QString, MaterialEntry*>> _materialEntryMap;
std::shared_ptr<std::map<QString, Material*>> _materialMap;
std::shared_ptr<std::list<MaterialLibrary*>> _libraryList;
};
} // namespace Materials
#endif // MATERIAL_MATERIALLOADER_H

View File

@@ -0,0 +1,276 @@
/***************************************************************************
* 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_
#endif
#include <QMutexLocker>
#include <App/Application.h>
#include "Exceptions.h"
#include "MaterialLoader.h"
#include "MaterialManager.h"
#include "ModelManager.h"
using namespace Materials;
/* TRANSLATOR Material::Materials */
std::shared_ptr<std::list<MaterialLibrary*>> MaterialManager::_libraryList = nullptr;
std::shared_ptr<std::map<QString, Material*>> MaterialManager::_materialMap = nullptr;
QMutex MaterialManager::_mutex;
TYPESYSTEM_SOURCE(Materials::MaterialManager, Base::BaseClass)
MaterialManager::MaterialManager()
{
// TODO: Add a mutex or similar
initLibraries();
}
void MaterialManager::initLibraries()
{
QMutexLocker locker(&_mutex);
if (_materialMap == nullptr) {
// Load the models first
ModelManager* manager = new ModelManager();
Q_UNUSED(manager)
delete manager;
_materialMap = std::make_shared<std::map<QString, Material*>>();
if (_libraryList == nullptr) {
_libraryList = std::make_shared<std::list<MaterialLibrary*>>();
}
// Load the libraries
MaterialLoader loader(_materialMap, _libraryList);
}
}
void MaterialManager::saveMaterial(MaterialLibrary* library,
Material& material,
const QString& path,
bool saveAsCopy)
{
Material* newMaterial = library->saveMaterial(material, path, saveAsCopy);
try {
Material* old = _materialMap->at(newMaterial->getUUID());
if (old) {
delete old;
}
}
catch (const std::out_of_range&) {
}
(*_materialMap)[material.getUUID()] = newMaterial;
}
bool MaterialManager::isMaterial(const fs::path& p)
{
if (!fs::is_regular_file(p)) {
return false;
}
// check file extension
if (p.extension() == ".FCMat") {
return true;
}
return false;
}
const Material& MaterialManager::getMaterial(const QString& uuid) const
{
try {
return *(_materialMap->at(uuid));
}
catch (std::out_of_range& e) {
throw MaterialNotFound();
}
}
const Material& MaterialManager::getMaterialByPath(const QString& path) const
{
QString cleanPath = QDir::cleanPath(path);
for (auto library : *_libraryList) {
Base::Console().Log("MaterialManager::getMaterialByPath() Checking library '%s'->'%s'\n",
library->getName().toStdString().c_str(),
library->getDirectory().toStdString().c_str());
if (cleanPath.startsWith(library->getDirectory())) {
Base::Console().Log("MaterialManager::getMaterialByPath() Library '%s'\n",
library->getDirectory().toStdString().c_str());
Base::Console().Log("MaterialManager::getMaterialByPath() Path '%s'\n",
cleanPath.toStdString().c_str());
return library->getMaterialByPath(cleanPath);
}
}
Base::Console().Log("MaterialManager::getMaterialByPath() Library not found for path '%s'\n",
cleanPath.toStdString().c_str());
throw MaterialNotFound();
}
const Material& MaterialManager::getMaterialByPath(const QString& path, const QString& lib) const
{
auto library = getLibrary(lib); // May throw LibraryNotFound
return library->getMaterialByPath(path); // May throw MaterialNotFound
}
MaterialLibrary* MaterialManager::getLibrary(const QString& name) const
{
for (auto library : *_libraryList) {
if (library->getName() == name) {
return library;
}
}
throw LibraryNotFound();
}
std::shared_ptr<std::list<MaterialLibrary*>> MaterialManager::getMaterialLibraries()
{
if (_libraryList == nullptr) {
if (_materialMap == nullptr) {
_materialMap = std::make_shared<std::map<QString, Material*>>();
}
_libraryList = std::make_shared<std::list<MaterialLibrary*>>();
// Load the libraries
MaterialLoader loader(_materialMap, _libraryList);
}
return _libraryList;
}
std::shared_ptr<std::map<QString, MaterialTreeNode*>>
MaterialManager::getMaterialTree(const MaterialLibrary& library) const
{
std::shared_ptr<std::map<QString, MaterialTreeNode*>> materialTree =
std::make_shared<std::map<QString, MaterialTreeNode*>>();
for (auto it = _materialMap->begin(); it != _materialMap->end(); it++) {
auto filename = it->first;
auto material = it->second;
if (material->getLibrary() == library) {
fs::path path = material->getDirectory().toStdString();
// Start at the root
auto node = materialTree;
for (auto itp = path.begin(); itp != path.end(); itp++) {
if (QString::fromStdString(itp->string())
.endsWith(QString::fromStdString(".FCMat"))) {
MaterialTreeNode* child = new MaterialTreeNode();
child->setData(material);
(*node)[QString::fromStdString(itp->string())] = child;
}
else {
// Add the folder only if it's not already there
QString folderName = QString::fromStdString(itp->string());
std::shared_ptr<std::map<QString, MaterialTreeNode*>> mapPtr;
if (node->count(folderName) == 0) {
mapPtr = std::make_shared<std::map<QString, MaterialTreeNode*>>();
MaterialTreeNode* child = new MaterialTreeNode();
child->setFolder(mapPtr);
(*node)[folderName] = child;
node = mapPtr;
}
else {
node = (*node)[folderName]->getFolder();
}
}
}
}
}
auto folderList = getMaterialFolders(library);
for (auto folder : *folderList) {
fs::path path = folder.toStdString();
// Start at the root
auto node = materialTree;
for (auto itp = path.begin(); itp != path.end(); itp++) {
// Add the folder only if it's not already there
QString folderName = QString::fromStdString(itp->string());
if (node->count(folderName) == 0) {
std::shared_ptr<std::map<QString, MaterialTreeNode*>> mapPtr =
std::make_shared<std::map<QString, MaterialTreeNode*>>();
MaterialTreeNode* child = new MaterialTreeNode();
child->setFolder(mapPtr);
(*node)[folderName] = child;
node = mapPtr;
}
else {
node = (*node)[folderName]->getFolder();
}
}
}
return materialTree;
}
std::shared_ptr<std::list<QString>>
MaterialManager::getMaterialFolders(const MaterialLibrary& library) const
{
return MaterialLoader::getMaterialFolders(library);
}
std::shared_ptr<std::map<QString, Material*>> MaterialManager::materialsWithModel(QString uuid)
{
std::shared_ptr<std::map<QString, Material*>> dict =
std::make_shared<std::map<QString, Material*>>();
for (auto it = _materialMap->begin(); it != _materialMap->end(); it++) {
QString key = it->first;
Material* material = it->second;
if (material->hasModel(uuid)) {
(*dict)[key] = material;
}
}
return dict;
}
std::shared_ptr<std::map<QString, Material*>>
MaterialManager::materialsWithModelComplete(QString uuid)
{
std::shared_ptr<std::map<QString, Material*>> dict =
std::make_shared<std::map<QString, Material*>>();
for (auto it = _materialMap->begin(); it != _materialMap->end(); it++) {
QString key = it->first;
Material* material = it->second;
if (material->isModelComplete(uuid)) {
(*dict)[key] = material;
}
}
return dict;
}

View File

@@ -0,0 +1,87 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_MATERIALMANAGER_H
#define MATERIAL_MATERIALMANAGER_H
#include <Mod/Material/MaterialGlobal.h>
#include <QMutex>
#include <boost/filesystem.hpp>
#include "FolderTree.h"
#include "Materials.h"
namespace fs = boost::filesystem;
namespace Materials
{
typedef FolderTreeNode<Material> MaterialTreeNode;
class MaterialsExport MaterialManager: public Base::BaseClass
{
TYPESYSTEM_HEADER();
public:
MaterialManager();
~MaterialManager() override = default;
std::shared_ptr<std::map<QString, Material*>> getMaterials()
{
return _materialMap;
}
const Material& getMaterial(const QString& uuid) const;
const Material& getMaterialByPath(const QString& path) const;
const Material& getMaterialByPath(const QString& path, const QString& library) const;
MaterialLibrary* getLibrary(const QString& name) const;
// Library management
static std::shared_ptr<std::list<MaterialLibrary*>> getMaterialLibraries();
std::shared_ptr<std::map<QString, MaterialTreeNode*>>
getMaterialTree(const MaterialLibrary& library) const;
std::shared_ptr<std::list<QString>> getMaterialFolders(const MaterialLibrary& library) const;
void createPath(MaterialLibrary* library, const QString& path)
{
library->createPath(path);
}
void saveMaterial(MaterialLibrary* library,
Material& material,
const QString& path,
bool saveAsCopy = true);
static bool isMaterial(const fs::path& p);
std::shared_ptr<std::map<QString, Material*>> materialsWithModel(QString uuid);
std::shared_ptr<std::map<QString, Material*>> materialsWithModelComplete(QString uuid);
private:
static std::shared_ptr<std::list<MaterialLibrary*>> _libraryList;
static std::shared_ptr<std::map<QString, Material*>> _materialMap;
static QMutex _mutex;
static void initLibraries();
};
} // namespace Materials
#endif // MATERIAL_MATERIALMANAGER_H

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateMaterial xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaMaterial_Module.xsd">
<PythonExport
Father="BaseClassPy"
Name="MaterialManagerPy"
Twin="MaterialManager"
TwinPointer="MaterialManager"
Include="Mod/Material/App/MaterialManager.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 descriptions.</UserDocu>
</Documentation>
<Methode Name="getMaterial">
<Documentation>
<UserDocu>Get a material object by specifying its UUID</UserDocu>
</Documentation>
</Methode>
<Methode Name="getMaterialByPath">
<Documentation>
<UserDocu>Get a material object by specifying its path and library name</UserDocu>
</Documentation>
</Methode>
<Attribute Name="MaterialLibraries" ReadOnly="true">
<Documentation>
<UserDocu>List of Material libraries.</UserDocu>
</Documentation>
<Parameter Name="MaterialLibraries" Type="List"/>
</Attribute>
<Attribute Name="Materials" ReadOnly="true">
<Documentation>
<UserDocu>List of Materials.</UserDocu>
</Documentation>
<Parameter Name="Materials" Type="Dict"/>
</Attribute>
<Methode Name="materialsWithModel">
<Documentation>
<UserDocu>Get a list of materials implementing the specified model</UserDocu>
</Documentation>
</Methode>
<Methode Name="materialsWithModelComplete">
<Documentation>
<UserDocu>Get a list of materials implementing the specified model, with values for all properties</UserDocu>
</Documentation>
</Methode>
</PythonExport>
</GenerateMaterial>

View File

@@ -0,0 +1,200 @@
/***************************************************************************
* 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 <boost/uuid/uuid_io.hpp>
#endif
#include "Exceptions.h"
#include "MaterialManager.h"
#include "MaterialManagerPy.h"
#include "MaterialPy.h"
#include "Materials.h"
#include "MaterialManagerPy.cpp"
using namespace Materials;
// returns a string which represents the object e.g. when printed in python
std::string MaterialManagerPy::representation() const
{
std::stringstream str;
str << "<MaterialManager object at " << getMaterialManagerPtr() << ">";
return str.str();
}
PyObject* MaterialManagerPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper
{
// never create such objects with the constructor
return new MaterialManagerPy(new MaterialManager());
}
// constructor method
int MaterialManagerPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
{
return 0;
}
PyObject* MaterialManagerPy::getMaterial(PyObject* args)
{
char* uuid;
if (!PyArg_ParseTuple(args, "s", &uuid)) {
return nullptr;
}
try {
const Material& material =
getMaterialManagerPtr()->getMaterial(QString::fromStdString(uuid));
return new MaterialPy(new Material(material));
}
catch (const MaterialNotFound&) {
PyErr_SetString(PyExc_LookupError, "Material not found");
return nullptr;
}
}
PyObject* MaterialManagerPy::getMaterialByPath(PyObject* args)
{
char* path;
char* lib = "";
if (!PyArg_ParseTuple(args, "s|s", &path, &lib)) {
return nullptr;
}
QString libPath(QString::fromStdString(lib));
if (!libPath.isEmpty()) {
try {
const Material& material =
getMaterialManagerPtr()->getMaterialByPath(QString::fromStdString(path), libPath);
return new MaterialPy(new Material(material));
}
catch (const MaterialNotFound&) {
PyErr_SetString(PyExc_LookupError, "Material not found");
return nullptr;
}
catch (const LibraryNotFound&) {
PyErr_SetString(PyExc_LookupError, "Library not found");
return nullptr;
}
}
try {
const Material& material =
getMaterialManagerPtr()->getMaterialByPath(QString::fromStdString(path));
return new MaterialPy(new Material(material));
}
catch (const MaterialNotFound&) {
PyErr_SetString(PyExc_LookupError, "Material not found");
return nullptr;
}
}
Py::List MaterialManagerPy::getMaterialLibraries() const
{
auto libraries = getMaterialManagerPtr()->getMaterialLibraries();
Py::List list;
for (auto it = libraries->begin(); it != libraries->end(); it++) {
MaterialLibrary* lib = *it;
Py::Tuple libTuple(3);
libTuple.setItem(0, Py::String(lib->getName().toStdString()));
libTuple.setItem(1, Py::String(lib->getDirectoryPath().toStdString()));
libTuple.setItem(2, Py::String(lib->getIconPath().toStdString()));
list.append(libTuple);
}
return list;
}
Py::Dict MaterialManagerPy::getMaterials() const
{
Py::Dict dict;
auto materials = getMaterialManagerPtr()->getMaterials();
for (auto it = materials->begin(); it != materials->end(); it++) {
QString key = it->first;
Material* material = it->second;
PyObject* materialPy = new MaterialPy(new Material(*material));
dict.setItem(Py::String(key.toStdString()), Py::Object(materialPy, true));
}
// return Py::new_reference_to(dict);
return dict;
}
PyObject* MaterialManagerPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
int MaterialManagerPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}
PyObject* MaterialManagerPy::materialsWithModel(PyObject* args)
{
char* uuid;
if (!PyArg_ParseTuple(args, "s", &uuid)) {
return nullptr;
}
auto materials = getMaterialManagerPtr()->materialsWithModel(QString::fromStdString(uuid));
PyObject* dict = PyDict_New();
for (auto it = materials->begin(); it != materials->end(); it++) {
QString key = it->first;
Material* material = it->second;
PyObject* materialPy = new MaterialPy(new Material(*material));
PyDict_SetItem(dict, PyUnicode_FromString(key.toStdString().c_str()), materialPy);
}
return dict;
}
PyObject* MaterialManagerPy::materialsWithModelComplete(PyObject* args)
{
char* uuid;
if (!PyArg_ParseTuple(args, "s", &uuid)) {
return nullptr;
}
auto materials =
getMaterialManagerPtr()->materialsWithModelComplete(QString::fromStdString(uuid));
PyObject* dict = PyDict_New();
for (auto it = materials->begin(); it != materials->end(); it++) {
QString key = it->first;
Material* material = it->second;
PyObject* materialPy = new MaterialPy(new Material(*material));
PyDict_SetItem(dict, PyUnicode_FromString(key.toStdString().c_str()), materialPy);
}
return dict;
}

View File

@@ -0,0 +1,161 @@
<?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="MaterialPy"
Twin="Material"
TwinPointer="Material"
Include="Mod/Material/App/Materials.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 descriptions.</UserDocu>
</Documentation>
<Attribute Name="LibraryName" ReadOnly="true">
<Documentation>
<UserDocu>Model library name.</UserDocu>
</Documentation>
<Parameter Name="LibraryName" Type="String"/>
</Attribute>
<Attribute Name="LibraryRoot" ReadOnly="true">
<Documentation>
<UserDocu>Model library path.</UserDocu>
</Documentation>
<Parameter Name="LibraryRoot" Type="String"/>
</Attribute>
<Attribute Name="LibraryIcon" ReadOnly="true">
<Documentation>
<UserDocu>Model icon path.</UserDocu>
</Documentation>
<Parameter Name="LibraryIcon" Type="String"/>
</Attribute>
<Attribute Name="Name" ReadOnly="true">
<Documentation>
<UserDocu>Model name.</UserDocu>
</Documentation>
<Parameter Name="Name" Type="String"/>
</Attribute>
<Attribute Name="Directory" ReadOnly="true">
<Documentation>
<UserDocu>Model directory relative to the library root.</UserDocu>
</Documentation>
<Parameter Name="Directory" Type="String"/>
</Attribute>
<Attribute Name="UUID" ReadOnly="true">
<Documentation>
<UserDocu>Unique model identifier.</UserDocu>
</Documentation>
<Parameter Name="UUID" Type="String"/>
</Attribute>
<Attribute Name="Description" ReadOnly="true">
<Documentation>
<UserDocu>Description of the material.</UserDocu>
</Documentation>
<Parameter Name="Description" Type="String"/>
</Attribute>
<Attribute Name="URL" ReadOnly="true">
<Documentation>
<UserDocu>URL to a material reference.</UserDocu>
</Documentation>
<Parameter Name="URL" Type="String"/>
</Attribute>
<Attribute Name="Reference" ReadOnly="true">
<Documentation>
<UserDocu>Reference for material data.</UserDocu>
</Documentation>
<Parameter Name="Reference" Type="String"/>
</Attribute>
<Attribute Name="Parent" ReadOnly="true">
<Documentation>
<UserDocu>Parent material UUID.</UserDocu>
</Documentation>
<Parameter Name="Parent" Type="String"/>
</Attribute>
<Attribute Name="AuthorAndLicense" ReadOnly="true">
<Documentation>
<UserDocu>Author and license information.</UserDocu>
</Documentation>
<Parameter Name="AuthorAndLicense" Type="String"/>
</Attribute>
<Attribute Name="PhysicalModels" ReadOnly="true">
<Documentation>
<UserDocu>List of implemented models.</UserDocu>
</Documentation>
<Parameter Name="PhysicalModels" Type="List"/>
</Attribute>
<Attribute Name="AppearanceModels" ReadOnly="true">
<Documentation>
<UserDocu>List of implemented models.</UserDocu>
</Documentation>
<Parameter Name="AppearanceModels" Type="List"/>
</Attribute>
<Attribute Name="Tags" ReadOnly="true">
<Documentation>
<UserDocu>List of searchable tags.</UserDocu>
</Documentation>
<Parameter Name="Tags" Type="List"/>
</Attribute>
<Methode Name="hasPhysicalModel" ReadOnly="true">
<Documentation>
<UserDocu>Check if the material implements the physical model with the given UUID</UserDocu>
</Documentation>
</Methode>
<Methode Name="hasAppearanceModel" ReadOnly="true">
<Documentation>
<UserDocu>Check if the material implements the appearance model with the given UUID</UserDocu>
</Documentation>
</Methode>
<Methode Name="isPhysicalModelComplete" ReadOnly="true">
<Documentation>
<UserDocu>Check if the material implements the physical model with the given UUID, and has values defined for each property</UserDocu>
</Documentation>
</Methode>
<Methode Name="isAppearanceModelComplete" ReadOnly="true">
<Documentation>
<UserDocu>Check if the material implements the appearance model with the given UUID, and has values defined for each property</UserDocu>
</Documentation>
</Methode>
<Methode Name="hasPhysicalProperty" ReadOnly="true">
<Documentation>
<UserDocu>Check if the material implements the physical property with the given name</UserDocu>
</Documentation>
</Methode>
<Methode Name="hasAppearanceProperty" ReadOnly="true">
<Documentation>
<UserDocu>Check if the material implements the appearance property with the given name</UserDocu>
</Documentation>
</Methode>
<Attribute Name="Properties" ReadOnly="true">
<Documentation>
<UserDocu>deprecated -- Dictionary of all material properties.</UserDocu>
</Documentation>
<Parameter Name="Properties" Type="Dict"/>
</Attribute>
<Attribute Name="PhysicalProperties" ReadOnly="true">
<Documentation>
<UserDocu>deprecated -- Dictionary of material physical properties.</UserDocu>
</Documentation>
<Parameter Name="PhysicalProperties" Type="Dict"/>
</Attribute>
<Attribute Name="AppearanceProperties" ReadOnly="true">
<Documentation>
<UserDocu>deprecated -- Dictionary of material appearance properties.</UserDocu>
</Documentation>
<Parameter Name="AppearanceProperties" Type="Dict"/>
</Attribute>
<Methode Name="getPhysicalValue" ReadOnly="true">
<Documentation>
<UserDocu>Get the value associated with the property</UserDocu>
</Documentation>
</Methode>
<Methode Name="getAppearanceValue" ReadOnly="true">
<Documentation>
<UserDocu>Get the value associated with the property</UserDocu>
</Documentation>
</Methode>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,394 @@
/***************************************************************************
* 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 <boost/uuid/uuid_io.hpp>
#endif
#include <QMetaType>
#include <Base/Quantity.h>
#include <Base/QuantityPy.h>
#include <Gui/MetaTypes.h>
#include "Exceptions.h"
#include "MaterialPy.h"
#include "Materials.h"
#include "MaterialPy.cpp"
using namespace Materials;
// returns a string which represents the object e.g. when printed in python
std::string MaterialPy::representation() const
{
MaterialPy::PointerType ptr = getMaterialPtr();
std::stringstream str;
str << "Property [Name=(";
str << ptr->getName().toStdString();
str << "), UUID=(";
str << ptr->getUUID().toStdString();
str << "), Library Name=(";
str << ptr->getLibrary().getName().toStdString();
str << "), Library Root=(";
str << ptr->getLibrary().getDirectoryPath().toStdString();
str << "), Library Icon=(";
str << ptr->getLibrary().getIconPath().toStdString();
str << "), Directory=(";
str << ptr->getDirectory().toStdString();
// str << "), URL=(";
// str << ptr->getURL();
// str << "), DOI=(";
// str << ptr->getDOI();
// str << "), Description=(";
// str << ptr->getDescription();
// str << "), Inherits=[";
// const std::vector<std::string> &inherited = getMaterialPtr()->getInheritance();
// for (auto it = inherited.begin(); it != inherited.end(); it++)
// {
// std::string uuid = *it;
// if (it != inherited.begin())
// str << "), UUID=(";
// else
// str << "UUID=(";
// str << uuid << ")";
// }
// str << "]]";
str << ")]";
return str.str();
}
PyObject* MaterialPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper
{
// never create such objects with the constructor
return new MaterialPy(new Material());
}
// constructor method
int MaterialPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
{
return 0;
}
Py::String MaterialPy::getLibraryName() const
{
return Py::String(getMaterialPtr()->getLibrary().getName().toStdString());
}
Py::String MaterialPy::getLibraryRoot() const
{
return Py::String(getMaterialPtr()->getLibrary().getDirectoryPath().toStdString());
}
Py::String MaterialPy::getLibraryIcon() const
{
return Py::String(getMaterialPtr()->getLibrary().getIconPath().toStdString());
}
Py::String MaterialPy::getName() const
{
return Py::String(getMaterialPtr()->getName().toStdString());
}
Py::String MaterialPy::getDirectory() const
{
return Py::String(getMaterialPtr()->getDirectory().toStdString());
}
Py::String MaterialPy::getUUID() const
{
return Py::String(getMaterialPtr()->getUUID().toStdString());
}
Py::String MaterialPy::getDescription() const
{
return Py::String(getMaterialPtr()->getDescription().toStdString());
}
Py::String MaterialPy::getURL() const
{
return Py::String(getMaterialPtr()->getURL().toStdString());
}
Py::String MaterialPy::getReference() const
{
return Py::String(getMaterialPtr()->getReference().toStdString());
}
Py::String MaterialPy::getParent() const
{
return Py::String(getMaterialPtr()->getParentUUID().toStdString());
}
Py::String MaterialPy::getAuthorAndLicense() const
{
return Py::String(getMaterialPtr()->getAuthorAndLicense().toStdString());
}
Py::List MaterialPy::getPhysicalModels() const
{
const std::vector<QString>* models = getMaterialPtr()->getPhysicalModels();
Py::List list;
for (auto it = models->begin(); it != models->end(); it++) {
QString uuid = *it;
list.append(Py::String(uuid.toStdString()));
}
return list;
}
Py::List MaterialPy::getAppearanceModels() const
{
const std::vector<QString>* models = getMaterialPtr()->getAppearanceModels();
Py::List list;
for (auto it = models->begin(); it != models->end(); it++) {
QString uuid = *it;
list.append(Py::String(uuid.toStdString()));
}
return list;
}
Py::List MaterialPy::getTags() const
{
const std::list<QString>& tags = getMaterialPtr()->getTags();
Py::List list;
for (auto it = tags.begin(); it != tags.end(); it++) {
QString uuid = *it;
list.append(Py::String(uuid.toStdString()));
}
return list;
}
PyObject* MaterialPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
int MaterialPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}
PyObject* MaterialPy::hasPhysicalModel(PyObject* args)
{
char* uuid;
if (!PyArg_ParseTuple(args, "s", &uuid)) {
return nullptr;
}
bool hasProperty = getMaterialPtr()->hasPhysicalModel(QString::fromStdString(uuid));
return hasProperty ? Py_True : Py_False;
}
PyObject* MaterialPy::hasAppearanceModel(PyObject* args)
{
char* uuid;
if (!PyArg_ParseTuple(args, "s", &uuid)) {
return nullptr;
}
bool hasProperty = getMaterialPtr()->hasAppearanceModel(QString::fromStdString(uuid));
return hasProperty ? Py_True : Py_False;
}
PyObject* MaterialPy::isPhysicalModelComplete(PyObject* args)
{
char* name;
if (!PyArg_ParseTuple(args, "s", &name)) {
return nullptr;
}
bool isComplete = getMaterialPtr()->isPhysicalModelComplete(QString::fromStdString(name));
return isComplete ? Py_True : Py_False;
}
PyObject* MaterialPy::isAppearanceModelComplete(PyObject* args)
{
char* name;
if (!PyArg_ParseTuple(args, "s", &name)) {
return nullptr;
}
bool isComplete = getMaterialPtr()->isAppearanceModelComplete(QString::fromStdString(name));
return isComplete ? Py_True : Py_False;
}
PyObject* MaterialPy::hasPhysicalProperty(PyObject* args)
{
char* name;
if (!PyArg_ParseTuple(args, "s", &name)) {
return nullptr;
}
bool hasProperty = getMaterialPtr()->hasPhysicalProperty(QString::fromStdString(name));
return hasProperty ? Py_True : Py_False;
}
PyObject* MaterialPy::hasAppearanceProperty(PyObject* args)
{
char* name;
if (!PyArg_ParseTuple(args, "s", &name)) {
return nullptr;
}
bool hasProperty = getMaterialPtr()->hasAppearanceProperty(QString::fromStdString(name));
return hasProperty ? Py_True : Py_False;
}
Py::Dict MaterialPy::getProperties() const
{
Py::Dict dict;
// Maintain backwards compatibility
dict.setItem(Py::String("CardName"), Py::String(getMaterialPtr()->getName().toStdString()));
dict.setItem(Py::String("AuthorAndLicense"),
Py::String(getMaterialPtr()->getAuthorAndLicense().toStdString()));
dict.setItem(Py::String("Name"), Py::String(getMaterialPtr()->getName().toStdString()));
dict.setItem(Py::String("Description"),
Py::String(getMaterialPtr()->getDescription().toStdString()));
dict.setItem(Py::String("ReferenceSource"),
Py::String(getMaterialPtr()->getReference().toStdString()));
dict.setItem(Py::String("SourceURL"), Py::String(getMaterialPtr()->getURL().toStdString()));
auto properties = getMaterialPtr()->getPhysicalProperties();
for (auto it = properties.begin(); it != properties.end(); it++) {
QString key = it->first;
MaterialProperty& materialProperty = it->second;
if (!materialProperty.isNull()) {
auto value = materialProperty.getString();
dict.setItem(Py::String(key.toStdString()), Py::String(value.toStdString()));
}
}
properties = getMaterialPtr()->getAppearanceProperties();
for (auto it = properties.begin(); it != properties.end(); it++) {
QString key = it->first;
MaterialProperty& materialProperty = it->second;
if (!materialProperty.isNull()) {
auto value = materialProperty.getString();
dict.setItem(Py::String(key.toStdString()), Py::String(value.toStdString()));
}
}
return dict;
}
Py::Dict MaterialPy::getPhysicalProperties() const
{
Py::Dict dict;
auto properties = getMaterialPtr()->getPhysicalProperties();
for (auto it = properties.begin(); it != properties.end(); it++) {
QString key = it->first;
MaterialProperty& materialProperty = it->second;
if (!materialProperty.isNull()) {
auto value = materialProperty.getString();
dict.setItem(Py::String(key.toStdString()), Py::String(value.toStdString()));
}
}
return dict;
}
Py::Dict MaterialPy::getAppearanceProperties() const
{
Py::Dict dict;
auto properties = getMaterialPtr()->getAppearanceProperties();
for (auto it = properties.begin(); it != properties.end(); it++) {
QString key = it->first;
MaterialProperty& materialProperty = it->second;
if (!materialProperty.isNull()) {
auto value = materialProperty.getString();
dict.setItem(Py::String(key.toStdString()), Py::String(value.toStdString()));
}
}
return dict;
}
static PyObject* _pyObjectFromVariant(const QVariant& value)
{
if (value.isNull()) {
return new PyObject();
}
if (value.userType() == QMetaType::type("Base::Quantity")) {
return new Base::QuantityPy(new Base::Quantity(value.value<Base::Quantity>()));
}
else if (value.userType() == QMetaType::Double) {
return PyFloat_FromDouble(value.toDouble());
}
else if (value.userType() == QMetaType::Float) {
return PyFloat_FromDouble(value.toFloat());
}
else if (value.userType() == QMetaType::Int) {
return PyLong_FromLong(value.toInt());
}
else if (value.userType() == QMetaType::Long) {
return PyLong_FromLong(value.toInt());
}
else if (value.userType() == QMetaType::Bool) {
return value.toBool() ? Py_True : Py_False;
}
else if (value.userType() == QMetaType::QString) {
return PyUnicode_FromString(value.toString().toStdString().c_str());
}
throw UnknownValueType();
}
PyObject* MaterialPy::getPhysicalValue(PyObject* args)
{
char* name;
if (!PyArg_ParseTuple(args, "s", &name)) {
return nullptr;
}
QVariant value = getMaterialPtr()->getPhysicalValue(QString::fromStdString(name));
return _pyObjectFromVariant(value);
}
PyObject* MaterialPy::getAppearanceValue(PyObject* args)
{
char* name;
if (!PyArg_ParseTuple(args, "s", &name)) {
return nullptr;
}
QVariant value = getMaterialPtr()->getAppearanceValue(QString::fromStdString(name));
return _pyObjectFromVariant(value);
}

View File

@@ -0,0 +1,242 @@
/***************************************************************************
* 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_
#endif
#include <App/Application.h>
#include "Exceptions.h"
#include "MaterialValue.h"
using namespace Materials;
/* TRANSLATOR Material::MaterialValue */
MaterialValue::MaterialValue()
: _valueType(None)
{}
MaterialValue::MaterialValue(ValueType type)
: _valueType(type)
{}
//===
Material2DArray::Material2DArray()
: MaterialValue(Array2D)
, _defaultSet(false)
{}
MaterialValue Material2DArray::getDefault() const
{
MaterialValue ret(_valueType);
ret.setValue(_value);
return ret;
}
const std::vector<QVariant>* Material2DArray::getRow(int row) const
{
try {
return _rows.at(row);
}
catch (std::out_of_range const&) {
throw InvalidRow();
}
}
std::vector<QVariant>* Material2DArray::getRow(int row)
{
try {
return _rows.at(row);
}
catch (std::out_of_range const&) {
throw InvalidRow();
}
}
void Material2DArray::addRow(std::vector<QVariant>* row)
{
_rows.push_back(row);
}
void Material2DArray::insertRow(int index, std::vector<QVariant>* row)
{
_rows.insert(_rows.begin() + index, row);
}
void Material2DArray::deleteRow(int row)
{
if (static_cast<std::size_t>(row) >= _rows.size() || row < 0) {
throw InvalidRow();
}
_rows.erase(_rows.begin() + row);
}
void Material2DArray::setValue(int row, int column, const QVariant& value)
{
if (row >= rows()) {
throw InvalidIndex();
}
std::vector<QVariant>* val = getRow(row);
try {
val->at(column) = value;
}
catch (const std::out_of_range&) {
throw InvalidIndex();
}
}
const QVariant Material2DArray::getValue(int row, int column) const
{
try {
auto val = getRow(row);
try {
return val->at(column);
}
catch (std::out_of_range const&) {
throw InvalidIndex();
}
}
catch (const InvalidRow&) {
throw InvalidIndex();
}
}
void Material2DArray::dumpRow(const std::vector<QVariant>& row) const
{
Base::Console().Log("row: ");
for (auto column : row) {
Base::Console().Log("'%s' ", column.toString().toStdString().c_str());
}
Base::Console().Log("\n");
}
void Material2DArray::dump() const
{
for (auto row : _rows) {
dumpRow(*row);
}
}
//===
Material3DArray::Material3DArray()
: MaterialValue(Array3D)
, _defaultSet(false)
{}
MaterialValue Material3DArray::getDefault() const
{
MaterialValue ret(_valueType);
ret.setValue(_value);
return ret;
}
const std::vector<std::vector<QVariant>*>& Material3DArray::getTable(const QVariant& depth) const
{
try {
return _rowMap.at(depth);
}
catch (std::out_of_range const&) {
throw InvalidRow();
}
}
const std::vector<QVariant>& Material3DArray::getRow(const QVariant& depth, int row) const
{
try {
return *(_rowMap.at(depth).at(row));
}
catch (std::out_of_range const&) {
throw InvalidRow();
}
}
const std::vector<QVariant>& Material3DArray::getRow(int row) const
{
return getRow(getDefault().getValue().toString(), row);
}
std::vector<QVariant>& Material3DArray::getRow(const QVariant& depth, int row)
{
try {
return *(_rowMap.at(depth).at(row));
}
catch (std::out_of_range const&) {
throw InvalidRow();
}
}
std::vector<QVariant>& Material3DArray::getRow(int row)
{
return getRow(getDefault().getValue().toString(), row);
}
void Material3DArray::addRow(const QVariant& depth, std::vector<QVariant>* row)
{
_rowMap[depth].push_back(row);
}
void Material3DArray::deleteRow(const QVariant& depth, int row)
{
Q_UNUSED(depth)
Q_UNUSED(row)
}
void Material3DArray::deleteRows(int depth)
{
Q_UNUSED(depth)
}
void Material3DArray::setValue(const QVariant& depth, int row, int column, const QVariant& value)
{
Q_UNUSED(depth)
Q_UNUSED(row)
Q_UNUSED(column)
Q_UNUSED(value)
}
void Material3DArray::setValue(int row, int column, const QVariant& value)
{
Q_UNUSED(row)
Q_UNUSED(column)
Q_UNUSED(value)
}
const QVariant Material3DArray::getValue(const QVariant& depth, int row, int column)
{
auto val = getRow(depth, row);
try {
return val.at(column);
}
catch (std::out_of_range const&) {
throw InvalidColumn();
}
}
const QVariant Material3DArray::getValue(int row, int column)
{
return getValue(getDefault().getValue().toString(), row, column);
}

View File

@@ -0,0 +1,188 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_MATERIALVALUE_H
#define MATERIAL_MATERIALVALUE_H
#include <Mod/Material/MaterialGlobal.h>
#include <QVariant>
namespace Materials
{
class MaterialsExport MaterialValue
{
public:
enum ValueType
{
None = 0,
String = 1,
Boolean = 2,
Integer = 3,
Float = 4,
Quantity = 5,
Distribution = 6,
List = 7,
Array2D = 8,
Array3D = 9,
Color = 10,
Image = 11,
File = 12,
URL = 13
};
MaterialValue();
explicit MaterialValue(ValueType type);
virtual ~MaterialValue() = default;
ValueType getType() const
{
return _valueType;
}
const QVariant getValue() const
{
return _value;
}
bool isNull() const
{
return _value.isNull();
}
virtual const QVariant getValueAt(const QVariant& value) const
{
Q_UNUSED(value);
return _value;
}
void setValue(const QVariant& value)
{
_value = value;
}
protected:
ValueType _valueType;
QVariant _value;
void setType(ValueType type)
{
_valueType = type;
}
};
class MaterialsExport Material2DArray: public MaterialValue
{
public:
Material2DArray();
~Material2DArray() override = default;
void setDefault(MaterialValue value)
{
_value = value.getValue();
_defaultSet = true;
}
void setDefault(const QVariant& value)
{
_value = value;
_defaultSet = true;
}
MaterialValue getDefault() const;
bool defaultSet() const
{
return _defaultSet;
}
const std::vector<QVariant>* getRow(int row) const;
std::vector<QVariant>* getRow(int row);
int rows() const
{
return _rows.size();
}
void addRow(std::vector<QVariant>* row);
void insertRow(int index, std::vector<QVariant>* row);
void deleteRow(int row);
void setValue(int row, int column, const QVariant& value);
const QVariant getValue(int row, int column) const;
protected:
std::vector<std::vector<QVariant>*> _rows;
bool _defaultSet;
private:
void dumpRow(const std::vector<QVariant>& row) const;
void dump() const;
};
class MaterialsExport Material3DArray: public MaterialValue
{
public:
Material3DArray();
~Material3DArray() override = default;
void setDefault(MaterialValue value)
{
_value = value.getValue();
_defaultSet = true;
}
void setDefault(const QVariant& value)
{
_value = value;
_defaultSet = true;
}
MaterialValue getDefault() const;
bool defaultSet() const
{
return _defaultSet;
}
const std::vector<std::vector<QVariant>*>& getTable(const QVariant& depth) const;
const std::vector<QVariant>& getRow(const QVariant& depth, int row) const;
const std::vector<QVariant>& getRow(int row) const;
std::vector<QVariant>& getRow(const QVariant& depth, int row);
std::vector<QVariant>& getRow(int row);
void addRow(const QVariant& depth, std::vector<QVariant>* row);
void deleteRow(const QVariant& depth, int row);
void deleteRows(int depth);
int depth() const
{
return _rowMap.size();
}
int rows(const QVariant& depth) const
{
return getTable(depth).size();
}
void setValue(const QVariant& depth, int row, int column, const QVariant& value);
void setValue(int row, int column, const QVariant& value);
const QVariant getValue(const QVariant& depth, int row, int column);
const QVariant getValue(int row, int column);
protected:
std::map<QVariant, std::vector<std::vector<QVariant>*>> _rowMap;
bool _defaultSet;
};
} // namespace Materials
Q_DECLARE_METATYPE(Materials::MaterialValue)
Q_DECLARE_METATYPE(Materials::Material2DArray)
Q_DECLARE_METATYPE(Materials::Material3DArray)
#endif // MATERIAL_MATERIALVALUE_H

View File

@@ -0,0 +1,924 @@
/***************************************************************************
* 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_
#endif
#include <QMetaType>
#include <QUuid>
#include <App/Application.h>
#include <Gui/MetaTypes.h>
#include "MaterialManager.h"
#include "Materials.h"
#include "ModelManager.h"
using namespace Materials;
/* TRANSLATOR Material::Materials */
TYPESYSTEM_SOURCE(Materials::MaterialProperty, Materials::ModelProperty)
MaterialProperty::MaterialProperty()
{
_valuePtr = std::make_shared<MaterialValue>(MaterialValue::None);
}
MaterialProperty::MaterialProperty(const ModelProperty& property)
: ModelProperty(property)
, _valuePtr(nullptr)
{
setType(getPropertyType());
auto columns = property.getColumns();
for (std::vector<ModelProperty>::const_iterator it = columns.begin(); it != columns.end();
it++) {
MaterialProperty prop(*it);
addColumn(prop);
}
if (_valuePtr->getType() == MaterialValue::Array2D) {
std::static_pointer_cast<Material2DArray>(_valuePtr)->setDefault(getColumnNull(0));
}
else if (_valuePtr->getType() == MaterialValue::Array3D) {
std::static_pointer_cast<Material3DArray>(_valuePtr)->setDefault(getColumnNull(0));
}
}
MaterialProperty::MaterialProperty(const MaterialProperty& other)
: ModelProperty(other)
{
_modelUUID = other._modelUUID;
if (other._valuePtr != nullptr) {
_valuePtr = std::make_shared<MaterialValue>(*(other._valuePtr));
}
else {
_valuePtr = nullptr;
}
for (auto it = other._columns.begin(); it != other._columns.end(); it++) {
_columns.push_back(*it);
}
}
// MaterialProperty::~MaterialProperty()
// {}
void MaterialProperty::setModelUUID(const QString& uuid)
{
_modelUUID = uuid;
}
const QVariant MaterialProperty::getValue() const
{
return _valuePtr->getValue();
}
std::shared_ptr<MaterialValue> MaterialProperty::getMaterialValue()
{
return _valuePtr;
}
const std::shared_ptr<MaterialValue> MaterialProperty::getMaterialValue() const
{
return _valuePtr;
}
const QString MaterialProperty::getString() const
{
if (getType() == MaterialValue::Quantity) {
Base::Quantity quantity = getValue().value<Base::Quantity>();
return quantity.getUserString();
}
return getValue().toString();
}
void MaterialProperty::setPropertyType(const QString& type)
{
ModelProperty::setPropertyType(type);
setType(type);
}
void MaterialProperty::setType(const QString& type)
{
if (type == QString::fromStdString("String")) {
_valuePtr = std::make_shared<MaterialValue>(MaterialValue::String);
}
else if (type == QString::fromStdString("Boolean")) {
_valuePtr = std::make_shared<MaterialValue>(MaterialValue::Boolean);
}
else if (type == QString::fromStdString("Integer")) {
_valuePtr = std::make_shared<MaterialValue>(MaterialValue::Integer);
}
else if (type == QString::fromStdString("Float")) {
_valuePtr = std::make_shared<MaterialValue>(MaterialValue::Float);
}
else if (type == QString::fromStdString("URL")) {
_valuePtr = std::make_shared<MaterialValue>(MaterialValue::URL);
}
else if (type == QString::fromStdString("Quantity")) {
_valuePtr = std::make_shared<MaterialValue>(MaterialValue::Quantity);
}
else if (type == QString::fromStdString("Color")) {
_valuePtr = std::make_shared<MaterialValue>(MaterialValue::Color);
}
else if (type == QString::fromStdString("File")) {
_valuePtr = std::make_shared<MaterialValue>(MaterialValue::File);
}
else if (type == QString::fromStdString("Image")) {
_valuePtr = std::make_shared<MaterialValue>(MaterialValue::Image);
}
else if (type == QString::fromStdString("List")) {
_valuePtr = std::make_shared<MaterialValue>(MaterialValue::List);
}
else if (type == QString::fromStdString("2DArray")) {
_valuePtr = std::make_shared<Material2DArray>();
}
else if (type == QString::fromStdString("3DArray")) {
_valuePtr = std::make_shared<Material3DArray>();
}
else {
// Error. Throw something
_valuePtr = std::make_shared<MaterialValue>(MaterialValue::None);
std::string stringType = type.toStdString();
std::string name = getName().toStdString();
throw UnknownValueType();
}
}
MaterialProperty& MaterialProperty::getColumn(int column)
{
try {
return _columns.at(column);
}
catch (std::out_of_range const&) {
throw InvalidColumn();
}
}
const MaterialProperty& MaterialProperty::getColumn(int column) const
{
try {
return _columns.at(column);
}
catch (std::out_of_range const&) {
throw InvalidColumn();
}
}
MaterialValue::ValueType MaterialProperty::getColumnType(int column) const
{
try {
return _columns.at(column).getType();
}
catch (std::out_of_range const&) {
throw InvalidColumn();
}
}
QString MaterialProperty::getColumnUnits(int column) const
{
try {
return _columns.at(column).getUnits();
}
catch (std::out_of_range const&) {
throw InvalidColumn();
}
}
QVariant MaterialProperty::getColumnNull(int column) const
{
MaterialValue::ValueType valueType = getColumnType(column);
switch (valueType) {
case MaterialValue::Quantity: {
Base::Quantity q = Base::Quantity(0, getColumnUnits(column));
return QVariant::fromValue(q);
}
case MaterialValue::Float:
case MaterialValue::Integer:
return QVariant(0);
default:
break;
}
return QVariant(QString());
}
void MaterialProperty::setValue(const QVariant& value)
{
// _valueType = MaterialValue::String;
_valuePtr->setValue(value);
}
void MaterialProperty::setValue(const QString& value)
{
if (_valuePtr->getType() == MaterialValue::Boolean) {
setBoolean(value);
}
else if (_valuePtr->getType() == MaterialValue::Integer) {
setInt(value);
}
else if (_valuePtr->getType() == MaterialValue::Float) {
setFloat(value);
}
else if (_valuePtr->getType() == MaterialValue::URL) {
setURL(value);
}
else if (_valuePtr->getType() == MaterialValue::Quantity) {
// Base::Console().Log("\tParse quantity '%s'\n", value.toStdString().c_str());
try {
setQuantity(Base::Quantity::parse(value));
}
catch (const Base::ParserError& e) {
Base::Console().Log("Error '%s'\n", e.what());
// Save as a string
setString(value);
}
}
else {
setString(value);
}
}
void MaterialProperty::setString(const QString& value)
{
// _valueType = MaterialValue::String;
_valuePtr->setValue(QVariant(value));
}
void MaterialProperty::setBoolean(bool value)
{
// _valueType = MaterialValue::Boolean;
_valuePtr->setValue(QVariant(value));
}
void MaterialProperty::setBoolean(int value)
{
// _valueType = MaterialValue::Boolean;
_valuePtr->setValue(QVariant(value != 0));
}
void MaterialProperty::setBoolean(const QString& value)
{
// _valueType = MaterialValue::Boolean;
bool boolean;
std::string val = value.toStdString();
if ((val == "true") || (val == "True")) {
boolean = true;
}
else if ((val == "false") || (val == "False")) {
boolean = false;
}
else {
boolean = (std::stoi(val) != 0);
}
setBoolean(boolean);
}
void MaterialProperty::setInt(int value)
{
_valuePtr->setValue(QVariant(value));
}
void MaterialProperty::setInt(const QString& value)
{
_valuePtr->setValue(value.toInt());
}
void MaterialProperty::setFloat(double value)
{
_valuePtr->setValue(QVariant(value));
}
void MaterialProperty::setFloat(const QString& value)
{
_valuePtr->setValue(QVariant(value.toFloat()));
}
void MaterialProperty::setQuantity(const Base::Quantity& value)
{
_valuePtr->setValue(QVariant(QVariant::fromValue(value)));
}
void MaterialProperty::setQuantity(double value, const QString& units)
{
setQuantity(Base::Quantity(value, units));
}
void MaterialProperty::setQuantity(const QString& value)
{
setQuantity(Base::Quantity::parse(value));
}
void MaterialProperty::setURL(const QString& value)
{
_valuePtr->setValue(QVariant(value));
}
MaterialProperty& MaterialProperty::operator=(const MaterialProperty& other)
{
if (this == &other) {
return *this;
}
ModelProperty::operator=(other);
_modelUUID = other._modelUUID;
if (other._valuePtr != nullptr) {
_valuePtr = std::make_shared<MaterialValue>(*(other._valuePtr));
}
else {
_valuePtr = nullptr;
}
_columns.clear();
for (auto it = other._columns.begin(); it != other._columns.end(); it++) {
_columns.push_back(*it);
}
return *this;
}
TYPESYSTEM_SOURCE(Materials::Material, Base::BaseClass)
Material::Material()
: _dereferenced(false)
{}
Material::Material(const MaterialLibrary& library,
const QString& directory,
const QString& uuid,
const QString& name)
: _library(library)
, _uuid(uuid)
, _name(name)
, _dereferenced(false)
, _editState(ModelEdit_None)
{
setDirectory(directory);
}
Material::Material(const Material& other)
: _library(other._library)
, _directory(other._directory)
, _uuid(other._uuid)
, _name(other._name)
, _authorAndLicense(other._authorAndLicense)
, _parentUuid(other._parentUuid)
, _description(other._description)
, _url(other._url)
, _reference(other._reference)
, _dereferenced(other._dereferenced)
, _editState(other._editState)
{
for (auto it = other._tags.begin(); it != other._tags.end(); it++) {
_tags.push_back(*it);
}
for (auto it = other._physicalUuids.begin(); it != other._physicalUuids.end(); it++) {
_physicalUuids.push_back(*it);
}
for (auto it = other._appearanceUuids.begin(); it != other._appearanceUuids.end(); it++) {
_appearanceUuids.push_back(*it);
}
for (auto it = other._allUuids.begin(); it != other._allUuids.end(); it++) {
_allUuids.push_back(*it);
}
for (auto it = other._physical.begin(); it != other._physical.end(); it++) {
_physical[it->first] = MaterialProperty(it->second);
}
for (auto it = other._appearance.begin(); it != other._appearance.end(); it++) {
_appearance[it->first] = MaterialProperty(it->second);
}
}
/*
* Destroys the object and frees any allocated resources
*/
Material::~Material()
{
// no need to delete child widgets, Qt does it all for us
}
void Material::addModel(const QString& uuid)
{
for (QString modelUUID : _allUuids) {
if (modelUUID == uuid) {
return;
}
}
_allUuids.push_back(uuid);
ModelManager manager;
try {
const Model& model = manager.getModel(uuid);
auto inheritance = model.getInheritance();
for (auto inherits = inheritance.begin(); inherits != inheritance.end(); inherits++) {
addModel(*inherits);
}
}
catch (ModelNotFound const&) {
}
}
void Material::setEditState(ModelEdit newState)
{
if (newState == ModelEdit_Extend) {
if (_editState != ModelEdit_Alter) {
_editState = newState;
}
}
else if (newState == ModelEdit_Alter) {
_editState = newState;
}
}
void Material::addPhysical(const QString& uuid)
{
if (hasPhysicalModel(uuid)) {
return;
}
ModelManager manager;
try {
const Model& model = manager.getModel(uuid);
_physicalUuids.push_back(uuid);
addModel(uuid);
setEditStateExtend();
for (auto it = model.begin(); it != model.end(); it++) {
QString propertyName = it->first;
ModelProperty property = static_cast<ModelProperty>(it->second);
try {
_physical[propertyName] = MaterialProperty(property);
}
catch (const UnknownValueType&) {
Base::Console().Error("Property '%s' has unknown type '%s'. Ignoring\n",
property.getName().toStdString().c_str(),
property.getPropertyType().toStdString().c_str());
}
}
}
catch (ModelNotFound const&) {
}
}
void Material::addAppearance(const QString& uuid)
{
if (hasAppearanceModel(uuid)) {
return;
}
ModelManager manager;
try {
const Model& model = manager.getModel(uuid);
_appearanceUuids.push_back(uuid);
addModel(uuid);
setEditStateExtend();
for (auto it = model.begin(); it != model.end(); it++) {
QString propertyName = it->first;
ModelProperty property = static_cast<ModelProperty>(it->second);
_appearance[propertyName] = MaterialProperty(property);
}
}
catch (ModelNotFound const&) {
}
}
void Material::setPhysicalEditState(const QString& name)
{
if (getPhysicalProperty(name).isNull()) {
setEditStateExtend();
}
else {
setEditStateAlter();
}
}
void Material::setAppearanceEditState(const QString& name)
{
if (getAppearanceProperty(name).isNull()) {
setEditStateExtend();
}
else {
setEditStateAlter();
}
}
void Material::setPhysicalValue(const QString& name, const QString& value)
{
setPhysicalEditState(name);
_physical[name].setValue(value); // may not be a string type
}
void Material::setPhysicalValue(const QString& name, int value)
{
setPhysicalEditState(name);
_physical[name].setInt(value);
}
void Material::setPhysicalValue(const QString& name, double value)
{
setPhysicalEditState(name);
_physical[name].setFloat(value);
}
void Material::setPhysicalValue(const QString& name, const Base::Quantity value)
{
setPhysicalEditState(name);
_physical[name].setQuantity(value);
}
void Material::setAppearanceValue(const QString& name, const QString& value)
{
setAppearanceEditState(name);
_appearance[name].setValue(value); // may not be a string type
}
MaterialProperty& Material::getPhysicalProperty(const QString& name)
{
try {
return _physical.at(name);
}
catch (std::out_of_range const&) {
throw PropertyNotFound();
}
}
const MaterialProperty& Material::getPhysicalProperty(const QString& name) const
{
try {
return _physical.at(name);
}
catch (std::out_of_range const&) {
throw PropertyNotFound();
}
}
MaterialProperty& Material::getAppearanceProperty(const QString& name)
{
try {
return _appearance.at(name);
}
catch (std::out_of_range const&) {
throw PropertyNotFound();
}
}
const MaterialProperty& Material::getAppearanceProperty(const QString& name) const
{
try {
return _appearance.at(name);
}
catch (std::out_of_range const&) {
throw PropertyNotFound();
}
}
const QVariant Material::getValue(const std::map<QString, MaterialProperty>& propertyList,
const QString& name) const
{
try {
return propertyList.at(name).getValue();
}
catch (std::out_of_range const&) {
throw PropertyNotFound();
}
}
const QString Material::getValueString(const std::map<QString, MaterialProperty>& propertyList,
const QString& name) const
{
try {
if (propertyList.at(name).getType() == MaterialValue::Quantity) {
auto value = propertyList.at(name).getValue();
if (value.isNull()) {
return QString();
}
return value.value<Base::Quantity>().getUserString();
}
return propertyList.at(name).getValue().toString();
}
catch (std::out_of_range const&) {
throw PropertyNotFound();
}
}
const QVariant Material::getPhysicalValue(const QString& name) const
{
return getValue(_physical, name);
}
const QString Material::getPhysicalValueString(const QString& name) const
{
return getValueString(_physical, name);
}
const QVariant Material::getAppearanceValue(const QString& name) const
{
return getValue(_appearance, name);
}
const QString Material::getAppearanceValueString(const QString& name) const
{
return getValueString(_appearance, name);
}
bool Material::hasPhysicalProperty(const QString& name) const
{
try {
static_cast<void>(_physical.at(name));
}
catch (std::out_of_range const&) {
return false;
}
return true;
}
bool Material::hasAppearanceProperty(const QString& name) const
{
try {
static_cast<void>(_appearance.at(name));
}
catch (std::out_of_range const&) {
return false;
}
return true;
}
bool Material::hasModel(const QString& uuid) const
{
for (QString modelUUID : _allUuids) {
if (modelUUID == uuid) {
return true;
}
}
return false;
}
bool Material::hasPhysicalModel(const QString& uuid) const
{
if (!hasModel(uuid)) {
return false;
}
ModelManager manager;
try {
const Model& model = manager.getModel(uuid);
if (model.getType() == Model::ModelType_Physical) {
return true;
}
}
catch (ModelNotFound const&) {
}
return false;
}
bool Material::hasAppearanceModel(const QString& uuid) const
{
if (!hasModel(uuid)) {
return false;
}
ModelManager manager;
try {
const Model& model = manager.getModel(uuid);
if (model.getType() == Model::ModelType_Appearance) {
return true;
}
}
catch (ModelNotFound const&) {
}
return false;
}
bool Material::isPhysicalModelComplete(const QString& uuid) const
{
if (!hasPhysicalModel(uuid)) {
return false;
}
ModelManager manager;
try {
const Model& model = manager.getModel(uuid);
for (auto it = model.begin(); it != model.end(); it++) {
QString propertyName = it->first;
const MaterialProperty& property = getPhysicalProperty(propertyName);
if (property.isNull()) {
return false;
}
}
}
catch (ModelNotFound const&) {
return false;
}
return true;
}
bool Material::isAppearanceModelComplete(const QString& uuid) const
{
if (!hasAppearanceModel(uuid)) {
return false;
}
ModelManager manager;
try {
const Model& model = manager.getModel(uuid);
for (auto it = model.begin(); it != model.end(); it++) {
QString propertyName = it->first;
const MaterialProperty& property = getAppearanceProperty(propertyName);
if (property.isNull()) {
return false;
}
}
}
catch (ModelNotFound const&) {
return false;
}
return true;
}
void Material::saveGeneral(QTextStream& stream) const
{
stream << "General:\n";
stream << " UUID: \"" << _uuid << "\"\n";
stream << " Name: \"" << _name << "\"\n";
if (!_authorAndLicense.isEmpty()) {
stream << " AuthorAndLicense: \"" << _authorAndLicense << "\"\n";
}
if (!_description.isEmpty()) {
stream << " Description: \"" << _description << "\"\n";
}
if (!_url.isEmpty()) {
stream << " SourceURL: \"" << _url << "\"\n";
}
if (!_reference.isEmpty()) {
stream << " ReferenceSource: \"" << _reference << "\"\n";
}
}
void Material::saveInherits(QTextStream& stream) const
{
if (!_parentUuid.isEmpty()) {
MaterialManager manager;
stream << "Inherits:\n";
stream << " " << manager.getMaterial(_parentUuid).getName() << ":\n";
stream << " UUID: \"" << _parentUuid << "\"\n";
}
}
void Material::saveModels(QTextStream& stream) const
{
if (!_physical.empty()) {
ModelManager modelManager;
stream << "Models:\n";
for (auto itm = _physicalUuids.begin(); itm != _physicalUuids.end(); itm++) {
auto model = modelManager.getModel(*itm);
stream << " " << model.getName() << ":\n";
stream << " UUID: \"" << model.getUUID() << "\"\n";
for (auto itp = model.begin(); itp != model.end(); itp++) {
QString propertyName = itp->first;
const MaterialProperty& property = getPhysicalProperty(propertyName);
if (!property.isNull()) {
stream << " " << propertyName << ": \""
<< getPhysicalValueString(propertyName) << "\"\n";
}
}
}
}
}
void Material::saveAppearanceModels(QTextStream& stream) const
{
if (!_appearance.empty()) {
ModelManager modelManager;
stream << "AppearanceModels:\n";
for (auto itm = _appearanceUuids.begin(); itm != _appearanceUuids.end(); itm++) {
auto model = modelManager.getModel(*itm);
stream << " " << model.getName() << ":\n";
stream << " UUID: \"" << model.getUUID() << "\"\n";
for (auto itp = model.begin(); itp != model.end(); itp++) {
QString propertyName = itp->first;
const MaterialProperty& property = getAppearanceProperty(propertyName);
if (!property.isNull()) {
stream << " " << propertyName << ": \""
<< getAppearanceValueString(propertyName) << "\"\n";
}
}
}
}
}
void Material::newUuid()
{
_uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
}
void Material::save(QTextStream& stream, bool saveAsCopy)
{
Q_UNUSED(saveAsCopy)
stream << "# File created by FreeCAD\n";
saveGeneral(stream);
saveInherits(stream);
saveModels(stream);
saveAppearanceModels(stream);
}
Material& Material::operator=(const Material& other)
{
if (this == &other) {
return *this;
}
_library = other._library;
_directory = other._directory;
_uuid = other._uuid;
_name = other._name;
_authorAndLicense = other._authorAndLicense;
_parentUuid = other._parentUuid;
_description = other._description;
_url = other._url;
_reference = other._reference;
_dereferenced = other._dereferenced;
_tags.clear();
for (auto it = other._tags.begin(); it != other._tags.end(); it++) {
_tags.push_back(*it);
}
_physicalUuids.clear();
for (auto it = other._physicalUuids.begin(); it != other._physicalUuids.end(); it++) {
_physicalUuids.push_back(*it);
}
_appearanceUuids.clear();
for (auto it = other._appearanceUuids.begin(); it != other._appearanceUuids.end(); it++) {
_appearanceUuids.push_back(*it);
}
_allUuids.clear();
for (auto it = other._allUuids.begin(); it != other._allUuids.end(); it++) {
_allUuids.push_back(*it);
}
_physical.clear();
for (auto it = other._physical.begin(); it != other._physical.end(); it++) {
_physical[it->first] = MaterialProperty(it->second);
}
_appearance.clear();
for (auto it = other._appearance.begin(); it != other._appearance.end(); it++) {
_appearance[it->first] = MaterialProperty(it->second);
}
return *this;
}

View File

@@ -0,0 +1,337 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_MATERIALS_H
#define MATERIAL_MATERIALS_H
#include <Mod/Material/MaterialGlobal.h>
#include <Base/BaseClass.h>
#include <QDir>
#include <QString>
#include <QTextStream>
#include <App/Application.h>
#include "MaterialLibrary.h"
#include "Model.h"
namespace fs = boost::filesystem;
namespace Materials
{
class MaterialsExport MaterialProperty: public ModelProperty
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
MaterialProperty();
explicit MaterialProperty(const ModelProperty& property);
explicit MaterialProperty(const MaterialProperty& property);
~MaterialProperty() override = default;
MaterialValue::ValueType getType() const
{
return _valuePtr->getType();
}
const QString getModelUUID() const;
const QVariant getValue() const;
bool isNull() const
{
return _valuePtr->isNull();
}
std::shared_ptr<MaterialValue> getMaterialValue();
const std::shared_ptr<MaterialValue> getMaterialValue() const;
const QString getString() const;
bool getBoolean() const;
int getInt() const;
double getFloat() const;
const Base::Quantity& getQuantity() const;
const QString getURL() const;
MaterialProperty& getColumn(int column);
const MaterialProperty& getColumn(int column) const;
MaterialValue::ValueType getColumnType(int column) const;
QString getColumnUnits(int column) const;
QVariant getColumnNull(int column) const;
void setModelUUID(const QString& uuid);
void setPropertyType(const QString& type) override;
void setValue(const QVariant& value);
void setValue(const QString& value);
void setString(const QString& value);
void setBoolean(bool value);
void setBoolean(int value);
void setBoolean(const QString& value);
void setInt(int value);
void setInt(const QString& value);
void setFloat(double value);
void setFloat(const QString& value);
void setQuantity(const Base::Quantity& value);
void setQuantity(double value, const QString& units);
void setQuantity(const QString& value);
void setURL(const QString& value);
MaterialProperty& operator=(const MaterialProperty& other);
protected:
void setType(const QString& type);
// void setType(MaterialValue::ValueType type) { _valueType = type; }
void addColumn(MaterialProperty& column)
{
_columns.push_back(column);
}
private:
QString _modelUUID;
std::shared_ptr<MaterialValue> _valuePtr;
std::vector<MaterialProperty> _columns;
};
class MaterialsExport Material: public Base::BaseClass
{
TYPESYSTEM_HEADER();
public:
enum ModelEdit
{
ModelEdit_None, // No change
ModelEdit_Alter, // Existing values are changed
ModelEdit_Extend // New values added
};
Material();
explicit Material(const MaterialLibrary& library,
const QString& directory,
const QString& uuid,
const QString& name);
explicit Material(const Material& other);
virtual ~Material();
const MaterialLibrary& getLibrary() const
{
return _library;
}
const QString getDirectory() const
{
return _directory;
}
const QString getUUID() const
{
return _uuid;
}
const QString getName() const
{
return _name;
}
const QString getAuthorAndLicense() const
{
return _authorAndLicense;
}
const QString getParentUUID() const
{
return _parentUuid;
}
const QString getDescription() const
{
return _description;
}
const QString getURL() const
{
return _url;
}
const QString getReference() const
{
return _reference;
}
ModelEdit getEditState() const
{
return _editState;
}
const std::list<QString>& getTags() const
{
return _tags;
}
const std::vector<QString>* getPhysicalModels() const
{
return &_physicalUuids;
}
const std::vector<QString>* getAppearanceModels() const
{
return &_appearanceUuids;
}
void setLibrary(const MaterialLibrary& library)
{
_library = library;
}
void setDirectory(const QString& directory)
{
Base::Console().Log("Materials::setDirectory(%s)\n", directory.toStdString().c_str());
_directory = directory;
}
void setUUID(const QString& uuid)
{
_uuid = uuid;
}
void setName(const QString& name)
{
_name = name;
}
void setAuthorAndLicense(const QString& authorAndLicense)
{
_authorAndLicense = authorAndLicense;
}
void setParentUUID(const QString& uuid)
{
_parentUuid = uuid;
}
void setDescription(const QString& description)
{
_description = description;
}
void setURL(const QString& url)
{
_url = url;
}
void setReference(const QString& reference)
{
_reference = reference;
}
void setEditState(ModelEdit newState);
void setEditStateAlter()
{
setEditState(ModelEdit_Alter);
}
void setEditStateExtend()
{
setEditState(ModelEdit_Extend);
}
void setPhysicalEditState(const QString& name);
void setAppearanceEditState(const QString& name);
void resetEditState()
{
_editState = ModelEdit_None;
}
void addTag(const QString& tag)
{
Q_UNUSED(tag);
}
void removeTag(const QString& tag)
{
Q_UNUSED(tag);
}
void addPhysical(const QString& uuid);
void addAppearance(const QString& uuid);
void newUuid();
void setPhysicalValue(const QString& name, const QString& value);
void setPhysicalValue(const QString& name, int value);
void setPhysicalValue(const QString& name, double value);
void setPhysicalValue(const QString& name, const Base::Quantity value);
void setAppearanceValue(const QString& name, const QString& value);
MaterialProperty& getPhysicalProperty(const QString& name);
const MaterialProperty& getPhysicalProperty(const QString& name) const;
MaterialProperty& getAppearanceProperty(const QString& name);
const MaterialProperty& getAppearanceProperty(const QString& name) const;
const QVariant getPhysicalValue(const QString& name) const;
const QString getPhysicalValueString(const QString& name) const;
const QVariant getAppearanceValue(const QString& name) const;
const QString getAppearanceValueString(const QString& name) const;
bool hasPhysicalProperty(const QString& name) const;
bool hasAppearanceProperty(const QString& name) const;
// Test if the model is defined, and if values are provided for all properties
bool hasModel(const QString& uuid) const;
bool hasPhysicalModel(const QString& uuid) const;
bool hasAppearanceModel(const QString& uuid) const;
bool isModelComplete(const QString& uuid) const
{
return isPhysicalModelComplete(uuid) || isAppearanceModelComplete(uuid);
}
bool isPhysicalModelComplete(const QString& uuid) const;
bool isAppearanceModelComplete(const QString& uuid) const;
const std::map<QString, MaterialProperty>& getPhysicalProperties() const
{
return _physical;
}
const std::map<QString, MaterialProperty>& getAppearanceProperties() const
{
return _appearance;
}
bool getDereferenced() const
{
return _dereferenced;
}
void markDereferenced()
{
_dereferenced = true;
}
void save(QTextStream& stream, bool saveAsCopy);
Material& operator=(const Material& other);
protected:
void addModel(const QString& uuid);
const QVariant getValue(const std::map<QString, MaterialProperty>& propertyList,
const QString& name) const;
const QString getValueString(const std::map<QString, MaterialProperty>& propertyList,
const QString& name) const;
void saveGeneral(QTextStream& stream) const;
void saveInherits(QTextStream& stream) const;
void saveModels(QTextStream& stream) const;
void saveAppearanceModels(QTextStream& stream) const;
private:
MaterialLibrary _library;
QString _directory;
QString _uuid;
QString _name;
QString _authorAndLicense;
QString _parentUuid;
QString _description;
QString _url;
QString _reference;
std::list<QString> _tags;
std::vector<QString> _physicalUuids;
std::vector<QString> _appearanceUuids;
std::vector<QString> _allUuids; // Includes inherited models
std::map<QString, MaterialProperty> _physical;
std::map<QString, MaterialProperty> _appearance;
bool _dereferenced;
ModelEdit _editState;
};
} // namespace Materials
Q_DECLARE_METATYPE(Materials::Material*)
#endif // MATERIAL_MATERIALS_H

View File

@@ -0,0 +1,115 @@
/***************************************************************************
* 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_
#endif
#include "Exceptions.h"
#include "Model.h"
#include "ModelLibrary.h"
#include <string>
using namespace Materials;
TYPESYSTEM_SOURCE(Materials::ModelProperty, Base::BaseClass)
ModelProperty::ModelProperty()
{}
ModelProperty::ModelProperty(const QString& name,
const QString& type,
const QString& units,
const QString& url,
const QString& description)
: _name(name)
, _propertyType(type)
, _units(units)
, _url(url)
, _description(description)
{}
ModelProperty::ModelProperty(const ModelProperty& other)
: _name(other._name)
, _propertyType(other._propertyType)
, _units(other._units)
, _url(other._url)
, _description(other._description)
, _inheritance(other._inheritance)
{
for (auto it = other._columns.begin(); it != other._columns.end(); it++) {
_columns.push_back(*it);
}
}
ModelProperty& ModelProperty::operator=(const ModelProperty& other)
{
if (this == &other) {
return *this;
}
_name = other._name;
_propertyType = other._propertyType;
_units = other._units;
_url = other._url;
_description = other._description;
_inheritance = other._inheritance;
_columns.clear();
for (auto it = other._columns.begin(); it != other._columns.end(); it++) {
_columns.push_back(*it);
}
return *this;
}
TYPESYSTEM_SOURCE(Materials::Model, Base::BaseClass)
Model::Model()
{}
Model::Model(const ModelLibrary& library,
ModelType type,
const QString& name,
const QString& directory,
const QString& uuid,
const QString& description,
const QString& url,
const QString& doi)
: _library(library)
, _type(type)
, _name(name)
, _directory(directory)
, _uuid(uuid)
, _description(description)
, _url(url)
, _doi(doi)
{}
ModelProperty& Model::operator[](const QString& key)
{
try {
return _properties.at(key);
}
catch (std::out_of_range const&) {
throw PropertyNotFound();
}
}

View File

@@ -0,0 +1,298 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_MODEL_H
#define MATERIAL_MODEL_H
#include <Mod/Material/MaterialGlobal.h>
#include <Base/BaseClass.h>
#include <Base/Quantity.h>
#include <QDir>
#include <QString>
#include "MaterialValue.h"
#include "ModelLibrary.h"
namespace Materials
{
class MaterialsExport ModelProperty: public Base::BaseClass
{
TYPESYSTEM_HEADER();
public:
ModelProperty();
explicit ModelProperty(const QString& name,
const QString& type,
const QString& units,
const QString& url,
const QString& description);
explicit ModelProperty(const ModelProperty& other);
~ModelProperty() override = default;
const QString getName() const
{
return _name;
}
const QString getPropertyType() const
{
return _propertyType;
}
const QString getUnits() const
{
return _units;
}
const QString getURL() const
{
return _url;
}
const QString getDescription() const
{
return _description;
}
const QString getInheritance() const
{
return _inheritance;
}
bool isInherited() const
{
return (_inheritance.length() > 0);
}
void setName(const QString& name)
{
_name = name;
}
virtual void setPropertyType(const QString& type)
{
_propertyType = type;
}
void setUnits(const QString& units)
{
_units = units;
}
void setURL(const QString& url)
{
_url = url;
}
void setDescription(const QString& description)
{
_description = description;
}
void setInheritance(const QString& uuid)
{
_inheritance = uuid;
}
void addColumn(ModelProperty& column)
{
_columns.push_back(column);
}
const std::vector<ModelProperty>& getColumns() const
{
return _columns;
}
int columns() const
{
return _columns.size();
}
ModelProperty& operator=(const ModelProperty& other);
private:
QString _name;
QString _propertyType;
QString _units;
QString _url;
QString _description;
QString _inheritance;
std::vector<ModelProperty> _columns;
};
class MaterialsExport Model: public Base::BaseClass
{
TYPESYSTEM_HEADER();
public:
enum ModelType
{
ModelType_Physical,
ModelType_Appearance
};
Model();
explicit Model(const ModelLibrary& library,
ModelType type,
const QString& name,
const QString& directory,
const QString& uuid,
const QString& description,
const QString& url,
const QString& doi);
~Model() override = default;
const ModelLibrary& getLibrary() const
{
return _library;
}
const QString getBase() const
{
return (_type == ModelType_Physical) ? QString::fromStdString("Model")
: QString::fromStdString("AppearanceModel");
}
const QString getName() const
{
return _name;
}
ModelType getType() const
{
return _type;
}
const QString getDirectory() const
{
return _directory;
}
const QString getDirectoryPath() const
{
return QDir(_directory).absolutePath();
}
const QString getRelativePath() const
{
return QDir(_directory).relativeFilePath(QDir(_directory).absolutePath());
}
const QString getUUID() const
{
return _uuid;
}
const QString getDescription() const
{
return _description;
}
const QString getURL() const
{
return _url;
}
const QString getDOI() const
{
return _doi;
}
void setLibrary(const ModelLibrary& library)
{
_library = library;
}
void setType(ModelType type)
{
_type = type;
}
void setName(const QString& name)
{
_name = name;
}
void setDirectory(const QString& directory)
{
_directory = directory;
}
void setUUID(const QString& uuid)
{
_uuid = uuid;
}
void setDescription(const QString& description)
{
_description = description;
}
void setURL(const QString& url)
{
_url = url;
}
void setDOI(const QString& doi)
{
_doi = doi;
}
void addInheritance(const QString& uuid)
{
_inheritedUuids.push_back(uuid);
}
const std::vector<QString>& getInheritance() const
{
return _inheritedUuids;
}
bool operator==(const Model& m) const
{
return _uuid == m._uuid;
}
bool operator!=(const Model& m) const
{
return !operator==(m);
}
ModelProperty& operator[](const QString& key);
void addProperty(ModelProperty& property)
{
_properties[property.getName()] = property;
}
using iterator = typename std::map<QString, ModelProperty>::iterator;
using const_iterator = typename std::map<QString, ModelProperty>::const_iterator;
iterator begin()
{
return _properties.begin();
}
const_iterator begin() const noexcept
{
return _properties.begin();
}
iterator end() noexcept
{
return _properties.end();
}
const_iterator end() const noexcept
{
return _properties.end();
}
const_iterator cbegin() const noexcept
{
return _properties.cbegin();
}
const_iterator cend() const noexcept
{
return _properties.cend();
}
private:
ModelLibrary _library;
ModelType _type;
QString _name;
QString _directory;
QString _uuid;
QString _description;
QString _url;
QString _doi;
std::vector<QString> _inheritedUuids;
std::map<QString, ModelProperty> _properties;
};
} // namespace Materials
#endif // MATERIAL_MODEL_H

View File

@@ -0,0 +1,113 @@
/***************************************************************************
* 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_
#endif
#include <string>
#include <App/Application.h>
#include "Exceptions.h"
#include "Model.h"
#include "ModelLibrary.h"
using namespace Materials;
TYPESYSTEM_SOURCE(Materials::LibraryBase, Base::BaseClass)
LibraryBase::LibraryBase(const QString& libraryName, const QString& dir, const QString& icon)
: _name(libraryName)
, _directory(QDir::cleanPath(dir))
, _iconPath(icon)
{}
LibraryBase::LibraryBase()
{}
bool LibraryBase::operator==(const LibraryBase& library) const
{
return (_name == library._name) && (_directory == library._directory);
}
QString LibraryBase::getLocalPath(const QString& path) const
{
QString filePath = getDirectoryPath();
QString cleanPath = QDir::cleanPath(path);
QString prefix = QString::fromStdString("/") + getName();
if (cleanPath.startsWith(prefix)) {
// Remove the library name from the path
filePath += cleanPath.right(cleanPath.length() - prefix.length());
}
else {
filePath += cleanPath;
}
return filePath;
}
QString LibraryBase::getRelativePath(const QString& path) const
{
QString filePath;
QString cleanPath = QDir::cleanPath(path);
QString prefix = QString::fromStdString("/") + getName();
if (cleanPath.startsWith(prefix)) {
// Remove the library name from the path
filePath = cleanPath.right(cleanPath.length() - prefix.length());
}
else {
filePath = cleanPath;
}
prefix = getDirectoryPath();
if (filePath.startsWith(prefix)) {
// Remove the library root from the path
filePath = filePath.right(filePath.length() - prefix.length());
}
// Remove any leading '/'
if (filePath.startsWith(QString::fromStdString("/"))) {
filePath.remove(0, 1);
}
return filePath;
}
TYPESYSTEM_SOURCE(Materials::ModelLibrary, LibraryBase)
ModelLibrary::ModelLibrary(const QString& libraryName, const QString& dir, const QString& icon)
: LibraryBase(libraryName, dir, icon)
{}
ModelLibrary::ModelLibrary()
{}
Model* ModelLibrary::addModel(const Model& model, const QString& path)
{
QString filePath = getRelativePath(path);
Model* newModel = new Model(model);
newModel->setLibrary(*this);
newModel->setDirectory(filePath);
return newModel;
}

View File

@@ -0,0 +1,101 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_MODELLIBRARY_H
#define MATERIAL_MODELLIBRARY_H
#include <Mod/Material/MaterialGlobal.h>
#include <Base/BaseClass.h>
#include <Base/Quantity.h>
#include <QDir>
#include <QString>
#include "MaterialValue.h"
namespace Materials
{
class Model;
class MaterialsExport LibraryBase: public Base::BaseClass
{
TYPESYSTEM_HEADER();
public:
LibraryBase();
explicit LibraryBase(const QString& libraryName, const QString& dir, const QString& icon);
~LibraryBase() override = default;
const QString getName() const
{
return _name;
}
const QString getDirectory() const
{
return _directory;
}
const QString getDirectoryPath() const
{
return QDir(_directory).absolutePath();
}
const QString getIconPath() const
{
return _iconPath;
}
bool operator==(const LibraryBase& library) const;
bool operator!=(const LibraryBase& library) const
{
return !operator==(library);
}
QString getLocalPath(const QString& path) const;
QString getRelativePath(const QString& path) const;
private:
QString _name;
QString _directory;
QString _iconPath;
};
class MaterialsExport ModelLibrary: public LibraryBase
{
TYPESYSTEM_HEADER();
public:
ModelLibrary();
explicit ModelLibrary(const QString& libraryName, const QString& dir, const QString& icon);
~ModelLibrary() override = default;
bool operator==(const ModelLibrary& library) const
{
return LibraryBase::operator==(library);
}
bool operator!=(const ModelLibrary& library) const
{
return !operator==(library);
}
Model* addModel(const Model& model, const QString& path);
};
} // namespace Materials
#endif // MATERIAL_MODELLIBRARY_H

View File

@@ -0,0 +1,417 @@
/***************************************************************************
* 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 <QString>
#endif
#include <App/Application.h>
#include <Base/Interpreter.h>
#include <QDirIterator>
#include <QFileInfo>
#include "Model.h"
#include "ModelLoader.h"
#include "ModelManager.h"
using namespace Materials;
ModelEntry::ModelEntry(const ModelLibrary& library,
const QString& baseName,
const QString& modelName,
const QString& dir,
const QString& modelUuid,
const YAML::Node& modelData)
: _library(library)
, _base(baseName)
, _name(modelName)
, _directory(dir)
, _uuid(modelUuid)
, _model(modelData)
, _dereferenced(false)
{}
std::unique_ptr<std::map<QString, ModelEntry*>> ModelLoader::_modelEntryMap = nullptr;
ModelLoader::ModelLoader(std::shared_ptr<std::map<QString, Model*>> modelMap,
std::shared_ptr<std::list<ModelLibrary*>> libraryList)
: _modelMap(modelMap)
, _libraryList(libraryList)
{
loadLibraries();
}
void ModelLoader::addLibrary(ModelLibrary* model)
{
_libraryList->push_back(model);
}
const QString ModelLoader::getUUIDFromPath(const QString& path)
{
QFile file(path);
if (!file.exists()) {
throw ModelNotFound();
}
try {
YAML::Node yamlroot = YAML::LoadFile(path.toStdString());
std::string base = "Model";
if (yamlroot["AppearanceModel"]) {
base = "AppearanceModel";
}
const QString uuid = QString::fromStdString(yamlroot[base]["UUID"].as<std::string>());
return uuid;
}
catch (YAML::Exception& ex) {
throw ModelNotFound();
}
}
ModelEntry* ModelLoader::getModelFromPath(const ModelLibrary& library, const QString& path) const
{
QFile file(path);
if (!file.exists()) {
throw ModelNotFound();
}
YAML::Node yamlroot;
std::string base = "Model";
std::string uuid;
std::string name;
try {
yamlroot = YAML::LoadFile(path.toStdString());
if (yamlroot["AppearanceModel"]) {
base = "AppearanceModel";
}
uuid = yamlroot[base]["UUID"].as<std::string>();
name = yamlroot[base]["Name"].as<std::string>();
}
catch (YAML::Exception const&) {
throw InvalidModel();
}
ModelEntry* model = new ModelEntry(library,
QString::fromStdString(base),
QString::fromStdString(name),
path,
QString::fromStdString(uuid),
yamlroot);
return model;
}
void ModelLoader::showYaml(const YAML::Node& yaml) const
{
std::stringstream out;
out << yaml;
std::string logData = out.str();
Base::Console().Log("%s\n", logData.c_str());
}
void ModelLoader::dereference(const QString& uuid,
ModelEntry* parent,
const ModelEntry* child,
std::map<std::pair<QString, QString>, QString>* inheritances)
{
auto parentPtr = parent->getModelPtr();
auto parentBase = parent->getBase().toStdString();
auto childYaml = child->getModel();
auto childBase = child->getBase().toStdString();
std::set<QString> exclude;
exclude.insert(QString::fromStdString("Name"));
exclude.insert(QString::fromStdString("UUID"));
exclude.insert(QString::fromStdString("URL"));
exclude.insert(QString::fromStdString("Description"));
exclude.insert(QString::fromStdString("DOI"));
exclude.insert(QString::fromStdString("Inherits"));
auto parentProperties = (*parentPtr)[parentBase];
auto childProperties = childYaml[childBase];
for (auto it = childProperties.begin(); it != childProperties.end(); it++) {
std::string name = it->first.as<std::string>();
if (exclude.count(QString::fromStdString(name)) == 0) {
// showYaml(it->second);
if (!parentProperties[name]) {
parentProperties[name] = it->second;
// parentProperties[name]["Inherits"] = childYaml[childBase]["UUID"];
(*inheritances)[std::pair<QString, QString>(uuid, QString::fromStdString(name))] =
yamlValue(childYaml[childBase], "UUID", "");
}
}
}
// showYaml(*parentPtr);
}
void ModelLoader::dereference(ModelEntry* model,
std::map<std::pair<QString, QString>, QString>* inheritances)
{
// Avoid recursion
if (model->getDereferenced()) {
return;
}
auto yamlModel = model->getModel();
auto base = model->getBase().toStdString();
if (yamlModel[base]["Inherits"]) {
auto inherits = yamlModel[base]["Inherits"];
for (auto it = inherits.begin(); it != inherits.end(); it++) {
QString nodeName = QString::fromStdString((*it)["UUID"].as<std::string>());
// This requires that all models have already been loaded undereferenced
try {
const ModelEntry* child = (*_modelEntryMap)[nodeName];
dereference(model->getUUID(), model, child, inheritances);
}
catch (const std::out_of_range& oor) {
Base::Console().Log("Unable to find '%s' in model map\n",
nodeName.toStdString().c_str());
}
}
}
model->markDereferenced();
}
QString ModelLoader::yamlValue(const YAML::Node& node,
const std::string& key,
const std::string& defaultValue)
{
if (node[key]) {
return QString::fromStdString(node[key].as<std::string>());
}
return QString::fromStdString(defaultValue);
}
void ModelLoader::addToTree(ModelEntry* model,
std::map<std::pair<QString, QString>, QString>* inheritances)
{
std::set<QString> exclude;
exclude.insert(QString::fromStdString("Name"));
exclude.insert(QString::fromStdString("UUID"));
exclude.insert(QString::fromStdString("URL"));
exclude.insert(QString::fromStdString("Description"));
exclude.insert(QString::fromStdString("DOI"));
exclude.insert(QString::fromStdString("Inherits"));
auto yamlModel = model->getModel();
auto library = model->getLibrary();
auto base = model->getBase().toStdString();
auto name = model->getName();
auto directory = model->getDirectory();
auto uuid = model->getUUID();
QString version = yamlValue(yamlModel["General"], "Version", "");
QString description = yamlValue(yamlModel[base], "Description", "");
QString url = yamlValue(yamlModel[base], "URL", "");
QString doi = yamlValue(yamlModel[base], "DOI", "");
Model::ModelType type =
(base == "Model") ? Model::ModelType_Physical : Model::ModelType_Appearance;
Model* finalModel = new Model(library, type, name, directory, uuid, description, url, doi);
// Add inheritance list
if (yamlModel[base]["Inherits"]) {
auto inherits = yamlModel[base]["Inherits"];
for (auto it = inherits.begin(); it != inherits.end(); it++) {
QString nodeName = QString::fromStdString((*it)["UUID"].as<std::string>());
finalModel->addInheritance(nodeName);
}
}
// Add property list
auto yamlProperties = yamlModel[base];
for (auto it = yamlProperties.begin(); it != yamlProperties.end(); it++) {
std::string propName = it->first.as<std::string>();
if (exclude.count(QString::fromStdString(propName)) == 0) {
// showYaml(it->second);
auto yamlProp = yamlProperties[propName];
auto propType = yamlValue(yamlProp, "Type", "");
auto propUnits = yamlValue(yamlProp, "Units", "");
auto propURL = yamlValue(yamlProp, "URL", "");
auto propDescription = yamlValue(yamlProp, "Description", "");
// auto inherits = yamlValue(yamlProp, "Inherits", "");
ModelProperty property(QString::fromStdString(propName),
propType,
propUnits,
propURL,
propDescription);
if (propType == QString::fromStdString("2DArray")
|| propType == QString::fromStdString("3DArray")) {
Base::Console().Log("Reading columns\n");
// Read the columns
auto cols = yamlProp["Columns"];
for (auto col : cols) {
std::string colName = col.first.as<std::string>();
Base::Console().Log("\tColumns '%s'\n", colName.c_str());
auto colProp = cols[colName];
auto colPropType = yamlValue(colProp, "Type", "");
auto colPropUnits = yamlValue(colProp, "Units", "");
auto colPropURL = yamlValue(colProp, "URL", "");
auto colPropDescription = yamlValue(colProp, "Description", "");
ModelProperty colProperty(QString::fromStdString(colName),
colPropType,
colPropUnits,
colPropURL,
colPropDescription);
property.addColumn(colProperty);
}
}
auto key = std::pair<QString, QString>(uuid, QString::fromStdString(propName));
if (inheritances->count(key) > 0) {
property.setInheritance((*inheritances)[key]);
}
finalModel->addProperty(property);
}
}
(*_modelMap)[uuid] = library.addModel(*finalModel, directory);
}
void ModelLoader::loadLibrary(const ModelLibrary& library)
{
if (_modelEntryMap == nullptr) {
_modelEntryMap = std::make_unique<std::map<QString, ModelEntry*>>();
}
QDirIterator it(library.getDirectory(), QDirIterator::Subdirectories);
while (it.hasNext()) {
auto pathname = it.next();
QFileInfo file(pathname);
if (file.isFile()) {
if (file.suffix().toStdString() == "yml") {
QString libraryName = file.baseName();
try {
auto model = getModelFromPath(library, file.canonicalFilePath());
(*_modelEntryMap)[model->getUUID()] = model;
// showYaml(model->getModel());
}
catch (InvalidModel const&) {
Base::Console().Log("Invalid model '%s'\n", pathname.toStdString().c_str());
}
}
}
}
std::map<std::pair<QString, QString>, QString>* inheritances =
new std::map<std::pair<QString, QString>, QString>();
for (auto it = _modelEntryMap->begin(); it != _modelEntryMap->end(); it++) {
dereference(it->second, inheritances);
}
for (auto it = _modelEntryMap->begin(); it != _modelEntryMap->end(); it++) {
addToTree(it->second, inheritances);
}
// delete inheritances;
}
void ModelLoader::loadLibraries()
{
getModelLibraries();
if (_libraryList) {
for (auto it = _libraryList->begin(); it != _libraryList->end(); it++) {
loadLibrary(**it);
}
}
}
void ModelLoader::getModelLibraries()
{
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/Resources");
bool useBuiltInMaterials = param->GetBool("UseBuiltInMaterials", true);
bool useMatFromModules = param->GetBool("UseMaterialsFromWorkbenches", true);
bool useMatFromConfigDir = param->GetBool("UseMaterialsFromConfigDir", true);
bool useMatFromCustomDir = param->GetBool("UseMaterialsFromCustomDir", true);
if (useBuiltInMaterials) {
QString resourceDir = QString::fromStdString(App::Application::getResourceDir()
+ "/Mod/Material/Resources/Models");
auto libData = new ModelLibrary(QString::fromStdString("System"),
resourceDir,
QString::fromStdString(":/icons/freecad.svg"));
_libraryList->push_back(libData);
}
if (useMatFromModules) {
auto moduleParam = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/Resources/Modules");
for (auto& group : moduleParam->GetGroups()) {
// auto module = moduleParam->GetGroup(group->GetGroupName());
auto moduleName = QString::fromStdString(group->GetGroupName());
auto modelDir = QString::fromStdString(group->GetASCII("ModuleModelDir", ""));
auto modelIcon = QString::fromStdString(group->GetASCII("ModuleIcon", ""));
if (modelDir.length() > 0) {
QDir dir(modelDir);
if (dir.exists()) {
auto libData = new ModelLibrary(moduleName, modelDir, modelIcon);
_libraryList->push_back(libData);
}
}
}
}
if (useMatFromConfigDir) {
QString resourceDir =
QString::fromStdString(App::Application::getUserAppDataDir() + "/Models");
if (!resourceDir.isEmpty()) {
QDir materialDir(resourceDir);
if (materialDir.exists()) {
auto libData =
new ModelLibrary(QString::fromStdString("User"),
resourceDir,
QString::fromStdString(":/icons/preferences-general.svg"));
_libraryList->push_back(libData);
}
}
}
if (useMatFromCustomDir) {
QString resourceDir = QString::fromStdString(param->GetASCII("CustomMaterialsDir", ""));
if (!resourceDir.isEmpty()) {
QDir materialDir(resourceDir);
if (materialDir.exists()) {
auto libData = new ModelLibrary(QString::fromStdString("Custom"),
resourceDir,
QString::fromStdString(":/icons/user.svg"));
_libraryList->push_back(libData);
}
}
}
}

View File

@@ -0,0 +1,129 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_MODELLOADER_H
#define MATERIAL_MODELLOADER_H
#include <QDir>
#include <QString>
#include <yaml-cpp/yaml.h>
#include "Model.h"
namespace Materials
{
class ModelEntry
{
public:
explicit ModelEntry(const ModelLibrary& library,
const QString& baseName,
const QString& modelName,
const QString& dir,
const QString& modelUuid,
const YAML::Node& modelData);
virtual ~ModelEntry() = default;
const ModelLibrary& getLibrary() const
{
return _library;
}
const QString getBase() const
{
return _base;
}
const QString getName() const
{
return _name;
}
const QString getDirectory() const
{
return _directory;
}
const QString getUUID() const
{
return _uuid;
}
const YAML::Node& getModel() const
{
return _model;
}
YAML::Node* getModelPtr()
{
return &_model;
}
bool getDereferenced() const
{
return _dereferenced;
}
void markDereferenced()
{
_dereferenced = true;
}
private:
ModelEntry();
ModelLibrary _library;
QString _base;
QString _name;
QString _directory;
QString _uuid;
YAML::Node _model;
bool _dereferenced;
};
class ModelLoader
{
public:
explicit ModelLoader(std::shared_ptr<std::map<QString, Model*>> modelMap,
std::shared_ptr<std::list<ModelLibrary*>> libraryList);
virtual ~ModelLoader() = default;
static const QString getUUIDFromPath(const QString& path);
private:
ModelLoader();
void getModelLibraries();
QString
yamlValue(const YAML::Node& node, const std::string& key, const std::string& defaultValue);
void addToTree(ModelEntry* model, std::map<std::pair<QString, QString>, QString>* inheritances);
void showYaml(const YAML::Node& yaml) const;
void dereference(const QString& uuid,
ModelEntry* parent,
const ModelEntry* child,
std::map<std::pair<QString, QString>, QString>* inheritances);
void dereference(ModelEntry* model,
std::map<std::pair<QString, QString>, QString>* inheritances);
ModelEntry* getModelFromPath(const ModelLibrary& library, const QString& path) const;
void addLibrary(ModelLibrary* model);
void loadLibrary(const ModelLibrary& library);
void loadLibraries();
static std::unique_ptr<std::map<QString, ModelEntry*>> _modelEntryMap;
std::shared_ptr<std::map<QString, Model*>> _modelMap;
std::shared_ptr<std::list<ModelLibrary*>> _libraryList;
};
} // namespace Materials
#endif // MATERIAL_MODELLOADER_H

View File

@@ -0,0 +1,172 @@
/***************************************************************************
* 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_
#endif
#include <QMutexLocker>
#include <Base/Console.h>
#include "Model.h"
#include "ModelLoader.h"
#include "ModelManager.h"
using namespace Materials;
std::shared_ptr<std::list<ModelLibrary*>> ModelManager::_libraryList = nullptr;
std::shared_ptr<std::map<QString, Model*>> ModelManager::_modelMap = nullptr;
QMutex ModelManager::_mutex;
TYPESYSTEM_SOURCE(Materials::ModelManager, Base::BaseClass)
ModelManager::ModelManager()
{
initLibraries();
}
void ModelManager::initLibraries()
{
QMutexLocker locker(&_mutex);
if (_modelMap == nullptr) {
_modelMap = std::make_shared<std::map<QString, Model*>>();
if (_libraryList == nullptr) {
_libraryList = std::make_shared<std::list<ModelLibrary*>>();
}
// Load the libraries
ModelLoader loader(_modelMap, _libraryList);
}
}
bool ModelManager::isModel(const fs::path& p)
{
// if (!fs::is_regular_file(p))
// return false;
// check file extension
if (p.extension() == ".yml") {
return true;
}
return false;
}
void ModelManager::refresh()
{
_modelMap->clear();
_libraryList->clear();
// Load the libraries
ModelLoader loader(_modelMap, _libraryList);
}
const Model& ModelManager::getModel(const QString& uuid) const
{
try {
if (_modelMap == nullptr) {
throw Uninitialized();
}
return *(_modelMap->at(uuid));
}
catch (std::out_of_range const&) {
throw ModelNotFound();
}
}
const Model& ModelManager::getModelByPath(const QString& path) const
{
const QString& uuid = ModelLoader::getUUIDFromPath(path);
const Model& model = getModel(uuid);
return model;
}
const Model& ModelManager::getModelByPath(const QString& path, const QString& libraryPath) const
{
QDir modelDir(QDir::cleanPath(libraryPath + QString::fromStdString("/") + path));
QString absPath = modelDir.absolutePath();
return getModelByPath(absPath);
}
bool ModelManager::passFilter(ModelFilter filter, Model::ModelType modelType) const
{
switch (filter) {
case ModelFilter_None:
return true;
case ModelFilter_Physical:
return (modelType == Model::ModelType_Physical);
case ModelFilter_Appearance:
return (modelType == Model::ModelType_Appearance);
}
return false;
}
std::shared_ptr<std::map<QString, ModelTreeNode*>>
ModelManager::getModelTree(const ModelLibrary& library, ModelFilter filter) const
{
std::shared_ptr<std::map<QString, ModelTreeNode*>> modelTree =
std::make_shared<std::map<QString, ModelTreeNode*>>();
for (auto it = _modelMap->begin(); it != _modelMap->end(); it++) {
auto filename = it->first;
auto model = it->second;
if (model->getLibrary() == library && passFilter(filter, model->getType())) {
fs::path path = model->getDirectory().toStdString();
Base::Console().Log("Relative path '%s'\n\t", path.string().c_str());
// Start at the root
std::shared_ptr<std::map<QString, ModelTreeNode*>> node = modelTree;
for (auto itp = path.begin(); itp != path.end(); itp++) {
if (isModel(itp->string())) {
ModelTreeNode* child = new ModelTreeNode();
child->setData(model);
(*node)[QString::fromStdString(itp->string())] = child;
}
else {
// Add the folder only if it's not already there
QString folderName = QString::fromStdString(itp->string());
std::shared_ptr<std::map<QString, ModelTreeNode*>> mapPtr;
if (node->count(QString::fromStdString(itp->string())) == 0) {
mapPtr = std::make_shared<std::map<QString, ModelTreeNode*>>();
ModelTreeNode* child = new ModelTreeNode();
child->setFolder(mapPtr);
(*node)[QString::fromStdString(itp->string())] = child;
node = mapPtr;
}
else {
node = (*node)[QString::fromStdString(itp->string())]->getFolder();
}
}
Base::Console().Log("'%s' ", itp->string().c_str());
}
Base::Console().Log("\n");
}
}
return modelTree;
}

View File

@@ -0,0 +1,86 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_MODELMANAGER_H
#define MATERIAL_MODELMANAGER_H
#include <Mod/Material/MaterialGlobal.h>
#include <QMutex>
#include <boost/filesystem.hpp>
#include "Exceptions.h"
#include "FolderTree.h"
#include "Model.h"
namespace fs = boost::filesystem;
namespace Materials
{
typedef FolderTreeNode<Model> ModelTreeNode;
class MaterialsExport ModelManager: public Base::BaseClass
{
TYPESYSTEM_HEADER();
public:
enum ModelFilter
{
ModelFilter_None,
ModelFilter_Physical,
ModelFilter_Appearance
};
ModelManager();
~ModelManager() override = default;
void refresh();
std::shared_ptr<std::list<ModelLibrary*>> getModelLibraries()
{
return _libraryList;
}
std::shared_ptr<std::map<QString, Model*>> getModels()
{
return _modelMap;
}
std::shared_ptr<std::map<QString, ModelTreeNode*>>
getModelTree(const ModelLibrary& library, ModelFilter filter = ModelFilter_None) const;
const Model& getModel(const QString& uuid) const;
const Model& getModelByPath(const QString& path) const;
const Model& getModelByPath(const QString& path, const QString& libraryPath) const;
static bool isModel(const fs::path& p);
bool passFilter(ModelFilter filter, Model::ModelType modelType) const;
private:
static void initLibraries();
static std::shared_ptr<std::list<ModelLibrary*>> _libraryList;
static std::shared_ptr<std::map<QString, Model*>> _modelMap;
static QMutex _mutex;
};
} // namespace Materials
#endif // MATERIAL_MODELMANAGER_H

View File

@@ -0,0 +1,41 @@
<?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="ModelManagerPy"
Twin="ModelManager"
TwinPointer="ModelManager"
Include="Mod/Material/App/ModelManager.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 model descriptions.</UserDocu>
</Documentation>
<Methode Name="getModel">
<Documentation>
<UserDocu>Get a model object by specifying its UUID</UserDocu>
</Documentation>
</Methode>
<Methode Name="getModelByPath">
<Documentation>
<UserDocu>Get a model object by specifying its path</UserDocu>
</Documentation>
</Methode>
<Attribute Name="ModelLibraries" ReadOnly="true">
<Documentation>
<UserDocu>List of model libraries.</UserDocu>
</Documentation>
<Parameter Name="ModelLibraries" Type="List"/>
</Attribute>
<Attribute Name="Models" ReadOnly="true">
<Documentation>
<UserDocu>List of model libraries.</UserDocu>
</Documentation>
<Parameter Name="Models" Type="Dict"/>
</Attribute>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,162 @@
/***************************************************************************
* 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 <boost/uuid/uuid_io.hpp>
#endif
#include "ModelManager.h"
#include "ModelManagerPy.h"
#include "ModelPy.h"
#include "ModelManagerPy.cpp"
using namespace Materials;
// returns a string which represents the object e.g. when printed in python
std::string ModelManagerPy::representation() const
{
std::stringstream str;
str << "<ModelManager object at " << getModelManagerPtr() << ">";
return str.str();
}
PyObject* ModelManagerPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper
{
// never create such objects with the constructor
return new ModelManagerPy(new ModelManager());
}
// constructor method
int ModelManagerPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
{
return 0;
}
PyObject* ModelManagerPy::getModel(PyObject* args)
{
char* uuid;
if (!PyArg_ParseTuple(args, "s", &uuid)) {
return nullptr;
}
try {
const Model model = getModelManagerPtr()->getModel(QString::fromStdString(uuid));
return new ModelPy(new Model(model));
}
catch (ModelNotFound const&) {
QString error = QString::fromStdString("Model not found:\n");
auto _modelMap = getModelManagerPtr()->getModels();
error += QString::fromStdString("ModelMap:\n");
for (auto itp = _modelMap->begin(); itp != _modelMap->end(); itp++) {
error += QString::fromStdString("\t_modelMap[") + itp->first
+ QString::fromStdString("] = '") + itp->second->getName()
+ QString::fromStdString("'\n");
}
error += QString::fromStdString("\tuuid = '") + QString::fromStdString(uuid)
+ QString::fromStdString("'\n");
PyErr_SetString(PyExc_LookupError, error.toStdString().c_str());
return nullptr;
}
catch (Uninitialized const&) {
PyErr_SetString(PyExc_LookupError, "Uninitialized model list");
return nullptr;
}
}
PyObject* ModelManagerPy::getModelByPath(PyObject* args)
{
char* path;
char* lib = "";
if (!PyArg_ParseTuple(args, "s|s", &path, &lib)) {
return nullptr;
}
std::string libPath(lib);
if (libPath.length() > 0) {
try {
const Model& model =
getModelManagerPtr()->getModelByPath(QString::fromStdString(path),
QString::fromStdString(libPath));
return new ModelPy(new Model(model));
}
catch (ModelNotFound const&) {
PyErr_SetString(PyExc_LookupError, "Model not found");
return nullptr;
}
}
try {
const Model& model = getModelManagerPtr()->getModelByPath(QString::fromStdString(path));
return new ModelPy(new Model(model));
}
catch (ModelNotFound const&) {
PyErr_SetString(PyExc_LookupError, "Model not found");
return nullptr;
}
}
Py::List ModelManagerPy::getModelLibraries() const
{
std::shared_ptr<std::list<ModelLibrary*>> libraries = getModelManagerPtr()->getModelLibraries();
Py::List list;
for (auto it = libraries->begin(); it != libraries->end(); it++) {
ModelLibrary* lib = *it;
Py::Tuple libTuple(3);
libTuple.setItem(0, Py::String(lib->getName().toStdString()));
libTuple.setItem(1, Py::String(lib->getDirectoryPath().toStdString()));
libTuple.setItem(2, Py::String(lib->getIconPath().toStdString()));
list.append(libTuple);
}
return list;
}
Py::Dict ModelManagerPy::getModels() const
{
auto models = getModelManagerPtr()->getModels();
Py::Dict dict;
for (auto it = models->begin(); it != models->end(); it++) {
QString key = it->first;
Model* model = it->second;
PyObject* modelPy = new ModelPy(new Model(*model));
dict.setItem(Py::String(key.toStdString()), Py::Object(modelPy, true));
}
return dict;
}
PyObject* ModelManagerPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
int ModelManagerPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}

View File

@@ -0,0 +1,54 @@
<?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="ModelPropertyPy"
Twin="ModelProperty"
TwinPointer="ModelProperty"
Include="Mod/Material/App/Model.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 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>
</Documentation>
<Parameter Name="Name" Type="String"/>
</Attribute>
<Attribute Name="Type" ReadOnly="true">
<Documentation>
<UserDocu>Property type.</UserDocu>
</Documentation>
<Parameter Name="Type" Type="String"/>
</Attribute>
<Attribute Name="Units" ReadOnly="true">
<Documentation>
<UserDocu>Property units category.</UserDocu>
</Documentation>
<Parameter Name="Units" Type="String"/>
</Attribute>
<Attribute Name="URL" ReadOnly="true">
<Documentation>
<UserDocu>URL to a detailed description of the property.</UserDocu>
</Documentation>
<Parameter Name="URL" Type="String"/>
</Attribute>
<Attribute Name="Description" ReadOnly="true">
<Documentation>
<UserDocu>Property description.</UserDocu>
</Documentation>
<Parameter Name="Description" Type="String"/>
</Attribute>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,100 @@
/***************************************************************************
* 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 <boost/uuid/uuid_io.hpp>
#endif
#include "Model.h"
#include "ModelPropertyPy.h"
#include "ModelPropertyPy.cpp"
using namespace Materials;
// returns a string which represents the object e.g. when printed in python
std::string ModelPropertyPy::representation() const
{
ModelPropertyPy::PointerType ptr = getModelPropertyPtr();
std::stringstream str;
str << "Property [Name=(";
str << ptr->getName().toStdString();
str << "), Type=(";
str << ptr->getPropertyType().toStdString();
str << "), Units=(";
str << ptr->getUnits().toStdString();
str << "), URL=(";
str << ptr->getURL().toStdString();
str << "), Description=(";
str << ptr->getDescription().toStdString();
str << ")]";
return str.str();
}
PyObject* ModelPropertyPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper
{
// never create such objects with the constructor
return new ModelPropertyPy(new ModelProperty());
}
// constructor method
int ModelPropertyPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
{
return 0;
}
Py::String ModelPropertyPy::getName() const
{
return Py::String(getModelPropertyPtr()->getName().toStdString());
}
Py::String ModelPropertyPy::getType() const
{
return Py::String(getModelPropertyPtr()->getPropertyType().toStdString());
}
Py::String ModelPropertyPy::getUnits() const
{
return Py::String(getModelPropertyPtr()->getUnits().toStdString());
}
Py::String ModelPropertyPy::getURL() const
{
return Py::String(getModelPropertyPtr()->getURL().toStdString());
}
Py::String ModelPropertyPy::getDescription() const
{
return Py::String(getModelPropertyPtr()->getDescription().toStdString());
}
PyObject* ModelPropertyPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
int ModelPropertyPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}

View File

@@ -0,0 +1,85 @@
<?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="ModelPy"
Twin="Model"
TwinPointer="Model"
Include="Mod/Material/App/Model.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 model descriptions.</UserDocu>
</Documentation>
<Attribute Name="LibraryName" ReadOnly="true">
<Documentation>
<UserDocu>Model library name.</UserDocu>
</Documentation>
<Parameter Name="LibraryName" Type="String"/>
</Attribute>
<Attribute Name="LibraryRoot" ReadOnly="true">
<Documentation>
<UserDocu>Model library path.</UserDocu>
</Documentation>
<Parameter Name="LibraryRoot" Type="String"/>
</Attribute>
<Attribute Name="LibraryIcon" ReadOnly="true">
<Documentation>
<UserDocu>Model icon path.</UserDocu>
</Documentation>
<Parameter Name="LibraryIcon" Type="String"/>
</Attribute>
<Attribute Name="Name" ReadOnly="true">
<Documentation>
<UserDocu>Model name.</UserDocu>
</Documentation>
<Parameter Name="Name" Type="String"/>
</Attribute>
<Attribute Name="Directory" ReadOnly="true">
<Documentation>
<UserDocu>Model directory.</UserDocu>
</Documentation>
<Parameter Name="Directory" Type="String"/>
</Attribute>
<Attribute Name="UUID" ReadOnly="true">
<Documentation>
<UserDocu>Unique model identifier.</UserDocu>
</Documentation>
<Parameter Name="UUID" Type="String"/>
</Attribute>
<Attribute Name="Description" ReadOnly="true">
<Documentation>
<UserDocu>Description of the model.</UserDocu>
</Documentation>
<Parameter Name="Description" Type="String"/>
</Attribute>
<Attribute Name="URL" ReadOnly="true">
<Documentation>
<UserDocu>URL to a detailed description of the model.</UserDocu>
</Documentation>
<Parameter Name="URL" Type="String"/>
</Attribute>
<Attribute Name="DOI" ReadOnly="true">
<Documentation>
<UserDocu>Digital Object Identifier (see https://doi.org/)</UserDocu>
</Documentation>
<Parameter Name="DOI" Type="String"/>
</Attribute>
<Attribute Name="Inherited" ReadOnly="true">
<Documentation>
<UserDocu>List of inherited models identified by UUID.</UserDocu>
</Documentation>
<Parameter Name="Inherited" Type="List"/>
</Attribute>
<Attribute Name="Properties" ReadOnly="true">
<Documentation>
<UserDocu>Dictionary of model properties.</UserDocu>
</Documentation>
<Parameter Name="Properties" Type="Dict"/>
</Attribute>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,171 @@
/***************************************************************************
* 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 <boost/uuid/uuid_io.hpp>
#endif
#include "Model.h"
#include "ModelPropertyPy.h"
#include "ModelPy.h"
#include "ModelPy.cpp"
using namespace Materials;
// returns a string which represents the object e.g. when printed in python
std::string ModelPy::representation() const
{
ModelPy::PointerType ptr = getModelPtr();
std::stringstream str;
str << "Property [Name=(";
str << ptr->getName().toStdString();
str << "), UUID=(";
str << ptr->getUUID().toStdString();
str << "), Library Name=(";
str << ptr->getLibrary().getName().toStdString();
str << "), Library Root=(";
str << ptr->getLibrary().getDirectoryPath().toStdString();
str << "), Library Icon=(";
str << ptr->getLibrary().getIconPath().toStdString();
str << "), Directory=(";
str << ptr->getDirectory().toStdString();
str << "), URL=(";
str << ptr->getURL().toStdString();
str << "), DOI=(";
str << ptr->getDOI().toStdString();
str << "), Description=(";
str << ptr->getDescription().toStdString();
str << "), Inherits=[";
const std::vector<QString>& inherited = getModelPtr()->getInheritance();
for (auto it = inherited.begin(); it != inherited.end(); it++) {
QString uuid = *it;
if (it != inherited.begin()) {
str << "), UUID=(";
}
else {
str << "UUID=(";
}
str << uuid.toStdString() << ")";
}
str << "]]";
return str.str();
}
PyObject* ModelPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper
{
// never create such objects with the constructor
return new ModelPy(new Model());
}
// constructor method
int ModelPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
{
return 0;
}
Py::String ModelPy::getLibraryName() const
{
return Py::String(getModelPtr()->getLibrary().getName().toStdString());
}
Py::String ModelPy::getLibraryRoot() const
{
return Py::String(getModelPtr()->getLibrary().getDirectoryPath().toStdString());
}
Py::String ModelPy::getLibraryIcon() const
{
return Py::String(getModelPtr()->getLibrary().getIconPath().toStdString());
}
Py::String ModelPy::getName() const
{
return Py::String(getModelPtr()->getName().toStdString());
}
Py::String ModelPy::getDirectory() const
{
return Py::String(getModelPtr()->getDirectoryPath().toStdString());
}
Py::String ModelPy::getUUID() const
{
return Py::String(getModelPtr()->getUUID().toStdString());
}
Py::String ModelPy::getDescription() const
{
return Py::String(getModelPtr()->getDescription().toStdString());
}
Py::String ModelPy::getURL() const
{
return Py::String(getModelPtr()->getURL().toStdString());
}
Py::String ModelPy::getDOI() const
{
return Py::String(getModelPtr()->getDOI().toStdString());
}
Py::List ModelPy::getInherited() const
{
const std::vector<QString>& inherited = getModelPtr()->getInheritance();
Py::List list;
for (auto it = inherited.begin(); it != inherited.end(); it++) {
QString uuid = *it;
list.append(Py::String(uuid.toStdString()));
}
return list;
}
Py::Dict ModelPy::getProperties() const
{
// std::map<std::string, Model*> *models = getModelPtr()->getModels();
Py::Dict dict;
for (auto it = getModelPtr()->begin(); it != getModelPtr()->end(); it++) {
QString key = it->first;
ModelProperty& modelProperty = it->second;
PyObject* modelPropertyPy = new ModelPropertyPy(new ModelProperty(modelProperty));
dict.setItem(Py::String(key.toStdString()), Py::Object(modelPropertyPy, true));
}
return dict;
}
PyObject* ModelPy::getCustomAttributes(const char* /*attr*/) const
{
return nullptr;
}
int ModelPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}

View File

@@ -0,0 +1,72 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_MODELUUIDS_H
#define MATERIAL_MODELUUIDS_H
namespace Materials
{
// UUIDs for predefined material models
static const QString ModelUUID_Legacy_Father =
QString::fromStdString("9cdda8b6-b606-4778-8f13-3934d8668e67");
static const QString ModelUUID_Legacy_MaterialStandard =
QString::fromStdString("1e2c0088-904a-4537-925f-64064c07d700");
static const QString ModelUUID_Mechanical_Density =
QString::fromStdString("454661e5-265b-4320-8e6f-fcf6223ac3af");
static const QString ModelUUID_Mechanical_IsotropicLinearElastic =
QString::fromStdString("f6f9e48c-b116-4e82-ad7f-3659a9219c50");
static const QString ModelUUID_Mechanical_LinearElastic =
QString::fromStdString("7b561d1d-fb9b-44f6-9da9-56a4f74d7536");
static const QString ModelUUID_Mechanical_OgdenYld2004p18 =
QString::fromStdString("3ef9e427-cc25-43f7-817f-79ff0d49625f");
static const QString ModelUUID_Mechanical_OrthotropicLinearElastic =
QString::fromStdString("b19ccc6b-a431-418e-91c2-0ac8c649d146");
static const QString ModelUUID_Fluid_Default =
QString::fromStdString("1ae66d8c-1ba1-4211-ad12-b9917573b202");
static const QString ModelUUID_Thermal_Default =
QString::fromStdString("9959d007-a970-4ea7-bae4-3eb1b8b883c7");
static const QString ModelUUID_Electromagnetic_Default =
QString::fromStdString("b2eb5f48-74b3-4193-9fbb-948674f427f3");
static const QString ModelUUID_Architectural_Default =
QString::fromStdString("32439c3b-262f-4b7b-99a8-f7f44e5894c8");
static const QString ModelUUID_Costs_Default =
QString::fromStdString("881df808-8726-4c2e-be38-688bb6cce466");
static const QString ModelUUID_Rendering_Basic =
QString::fromStdString("f006c7e4-35b7-43d5-bbf9-c5d572309e6e");
static const QString ModelUUID_Rendering_Texture =
QString::fromStdString("bbdcc65b-67ca-489c-bd5c-a36e33d1c160");
static const QString ModelUUID_Rendering_Advanced =
QString::fromStdString("c880f092-cdae-43d6-a24b-55e884aacbbf");
static const QString ModelUUID_Rendering_Vector =
QString::fromStdString("fdf5a80e-de50-4157-b2e5-b6e5f88b680e");
} // namespace Materials
#endif // MATERIAL_MODELUUIDS_H

View File

@@ -0,0 +1,22 @@
/***************************************************************************
* 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"

View File

@@ -0,0 +1,64 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_PRECOMPILED_H
#define MATERIAL_PRECOMPILED_H
#include <FCConfig.h>
// point at which warnings of overly long specifiers disabled (needed for VC6)
#ifdef _MSC_VER
#pragma warning(disable : 4251)
#pragma warning(disable : 4503)
#pragma warning(disable : 4786) // specifier longer then 255 chars
#pragma warning(disable : 4273)
#endif
#ifdef FC_OS_WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#endif
#ifdef _PreComp_
// standard
#include <cfloat>
#include <cmath>
// STL
#include <algorithm>
#include <map>
#include <sstream>
#include <string>
#include <vector>
// Qt
#include <QtGlobal>
// Boost
#include <boost/algorithm/string/predicate.hpp>
#include <boost/regex.hpp>
#endif //_PreComp_
#endif // MATERIAL_PRECOMPILED_H

View File

@@ -0,0 +1,80 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATERIAL_TRIM_H
#define MATERIAL_TRIM_H
#include <string>
namespace Materials
{
// trim from start (in place)
static inline void ltrim(std::string& s)
{
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
return !std::isspace(ch);
}));
}
// trim from end (in place)
static inline void rtrim(std::string& s)
{
s.erase(std::find_if(s.rbegin(),
s.rend(),
[](unsigned char ch) {
return !std::isspace(ch);
})
.base(),
s.end());
}
// trim from both ends (in place)
static inline void trim(std::string& s)
{
rtrim(s);
ltrim(s);
}
// trim from start (copying)
static inline std::string ltrim_copy(std::string s)
{
ltrim(s);
return s;
}
// trim from end (copying)
static inline std::string rtrim_copy(std::string s)
{
rtrim(s);
return s;
}
// trim from both ends (copying)
static inline std::string trim_copy(std::string s)
{
trim(s);
return s;
}
} // namespace Materials
#endif // MATERIAL_TRIM_H

View File

@@ -1,174 +1,221 @@
IF (BUILD_GUI)
PYSIDE_WRAP_RC(Material_QRC_SRCS Resources/Material.qrc)
ENDIF (BUILD_GUI)
add_subdirectory(App)
SET(Material_SRCS
if(BUILD_GUI)
add_subdirectory(Gui)
endif(BUILD_GUI)
IF(BUILD_GUI)
PYSIDE_WRAP_RC(Material_QRC_SRCS Resources/Material.qrc)
ENDIF(BUILD_GUI)
SET(MaterialScripts_Files
Init.py
InitGui.py
Material.py
importFCMat.py
MaterialEditor.py
materials-editor.ui
TestMaterialsApp.py
Resources/ui/materials-editor.ui
Templatematerial.yml
)
SOURCE_GROUP("Module" FILES ${Material_SRCS})
SET (MaterialTools_Files
# SOURCE_GROUP("MaterialScripts" FILES ${MaterialScripts_Files})
SET(MaterialTools_Files
materialtools/__init__.py
materialtools/cardutils.py
materialtools/MaterialModels.py
)
SET (Material_Icon_Files
SET(Material_Icon_Files
Resources/icons/preview-rendered.svg
Resources/icons/preview-vector.svg
)
# collect all the material cards:
#FILE( GLOB MaterialLib_Files ./StandardMaterial/*.FCMat ./StandardMaterial/*.txt )
SET (MaterialLib_Files
StandardMaterial/None.FCMat
StandardMaterial/ABS-Generic.FCMat
StandardMaterial/Acrylic-Glass-Generic.FCMat
StandardMaterial/AlMg3F24.FCMat
StandardMaterial/AlMgSi1F31.FCMat
StandardMaterial/Aluminum-6061-T6.FCMat
StandardMaterial/Aluminum-Generic.FCMat
StandardMaterial/AlZn4-5Mg1F35.FCMat
StandardMaterial/CalculiX-Steel.FCMat
StandardMaterial/Concrete-Generic.FCMat
StandardMaterial/Copper-Generic.FCMat
StandardMaterial/Glass-E-GlassFibre.FCMat
StandardMaterial/Glass-Generic.FCMat
StandardMaterial/Glass-S2-GlassFibre.FCMat
StandardMaterial/Graphite.FCMat
StandardMaterial/Iron-Generic.FCMat
StandardMaterial/Invar-Generic.FCMat
StandardMaterial/PA6-Generic.FCMat
StandardMaterial/PET-Generic.FCMat
StandardMaterial/PLA-Generic.FCMat
StandardMaterial/PP-Generic.FCMat
StandardMaterial/PTFE-Generic.FCMat
StandardMaterial/PVC-Generic.FCMat
StandardMaterial/Reinforcement-FIB-B500.FCMat
StandardMaterial/Steel-15CrNi6.FCMat
StandardMaterial/Steel-17CrNiMo6.FCMat
StandardMaterial/Steel-1C22.FCMat
StandardMaterial/Steel-1C35.FCMat
StandardMaterial/Steel-1C45.FCMat
StandardMaterial/Steel-1C60.FCMat
StandardMaterial/Steel-20NiCrMo2.FCMat
StandardMaterial/Steel-28Mn6.FCMat
StandardMaterial/Steel-2C10.FCMat
StandardMaterial/Steel-30CrNiMo8.FCMat
StandardMaterial/Steel-34CrNiMo6.FCMat
StandardMaterial/Steel-36CrNiMo4.FCMat
StandardMaterial/Steel-36NiCrMo16.FCMat
StandardMaterial/Steel-3C15.FCMat
StandardMaterial/Steel-3C22.FCMat
StandardMaterial/Steel-3C35.FCMat
StandardMaterial/Steel-3V45.FCMat
StandardMaterial/Steel-C10.FCMat
StandardMaterial/Steel-C15.FCMat
StandardMaterial/Steel-C22E.FCMat
StandardMaterial/Steel-C25E.FCMat
StandardMaterial/Steel-C30E.FCMat
StandardMaterial/Steel-C40E.FCMat
StandardMaterial/Steel-C50E.FCMat
StandardMaterial/Steel-C55E.FCMat
StandardMaterial/Steel-C60E.FCMat
StandardMaterial/Steel-E295-GC.FCMat
StandardMaterial/Steel-E295.FCMat
StandardMaterial/Steel-E335-GC.FCMat
StandardMaterial/Steel-E335.FCMat
StandardMaterial/Steel-E360-GC.FCMat
StandardMaterial/Steel-E360.FCMat
StandardMaterial/Steel-EN-GJL-100.FCMat
StandardMaterial/Steel-EN-GJL-150.FCMat
StandardMaterial/Steel-EN-GJL-200.FCMat
StandardMaterial/Steel-EN-GJL-250.FCMat
StandardMaterial/Steel-EN-GJL-300.FCMat
StandardMaterial/Steel-EN-GJL-350.FCMat
StandardMaterial/Steel-EN-GJMB-350-10.FCMat
StandardMaterial/Steel-EN-GJMB-550-4.FCMat
StandardMaterial/Steel-EN-GJMB-650-2.FCMat
StandardMaterial/Steel-EN-GJMW-350-4.FCMat
StandardMaterial/Steel-EN-GJMW-360-12.FCMat
StandardMaterial/Steel-EN-GJMW-400-5.FCMat
StandardMaterial/Steel-EN-GJMW-450-7.FCMat
StandardMaterial/Steel-EN-GJS-400-15.FCMat
StandardMaterial/Steel-EN-GJS-500-7.FCMat
StandardMaterial/Steel-EN-GJS-600-3.FCMat
StandardMaterial/Steel-EN-GJS-700-2.FCMat
StandardMaterial/Steel-EN-GJS-800-1.FCMat
StandardMaterial/Steel-G16Mn5.FCMat
StandardMaterial/Steel-G200.FCMat
StandardMaterial/Steel-G20Mn5.FCMat
StandardMaterial/Steel-G230.FCMat
StandardMaterial/Steel-G260.FCMat
StandardMaterial/Steel-G300.FCMat
StandardMaterial/Steel-G30Mn5.FCMat
StandardMaterial/Steel-Generic.FCMat
StandardMaterial/Steel-S185.FCMat
StandardMaterial/Steel-S235JO.FCMat
StandardMaterial/Steel-S235JR.FCMat
StandardMaterial/Steel-S235JRG1.FCMat
StandardMaterial/Steel-S260NC.FCMat
StandardMaterial/Steel-S275JO.FCMat
StandardMaterial/Steel-S275JR.FCMat
StandardMaterial/Steel-S275N.FCMat
StandardMaterial/Steel-S335JO.FCMat
StandardMaterial/Steel-S335JR.FCMat
StandardMaterial/Steel-S335N.FCMat
StandardMaterial/Steel-S340MC.FCMat
StandardMaterial/Steel-S355J2G3.FCMat
StandardMaterial/Steel-S380MC.FCMat
StandardMaterial/Steel-S420MC.FCMat
StandardMaterial/Steel-S420N.FCMat
StandardMaterial/Steel-S460MC.FCMat
StandardMaterial/Steel-S460N.FCMat
StandardMaterial/Steel-S500MC.FCMat
StandardMaterial/Steel-S550MC.FCMat
StandardMaterial/Steel-S690MC.FCMat
StandardMaterial/Steel-St-37-2K.FCMat
StandardMaterial/Steel-St-E-255.FCMat
StandardMaterial/Steel-St-E-315.FCMat
StandardMaterial/Steel-St-E-380.FCMat
StandardMaterial/Steel-St-E-460.FCMat
StandardMaterial/Steel-St-E-500.FCMat
StandardMaterial/Steel-X2CrNiMoN17-13-3.FCMat
StandardMaterial/Steel-X2CrNiN24-4.FCMat
StandardMaterial/Steel-X39CrMo17-1.FCMat
StandardMaterial/Steel-X3CrNiMo13-14.FCMat
StandardMaterial/Steel-X5CrNi18-10.FCMat
StandardMaterial/Steel-X5CrNiMo17-12-2.FCMat
StandardMaterial/Steel-X6CrNiTi18-10.FCMat
StandardMaterial/TEMPLATE.FCMat
StandardMaterial/Ti-6Al-4V.FCMat
StandardMaterial/Wood-Generic.FCMat
StandardMaterial/Readme.txt
)
SOURCE_GROUP("MatLib" FILES ${MaterialLib_Files})
SET (FluidMaterial_Files
FluidMaterial/None.FCMat
FluidMaterial/Air.FCMat
FluidMaterial/Argon.FCMat
FluidMaterial/Carbon_dioxide.FCMat
FluidMaterial/Nitrogen.FCMat
FluidMaterial/Water.FCMat
FluidMaterial/Readme.md
)
SOURCE_GROUP("MatLib" FILES ${FluidMaterial_Files})
ADD_CUSTOM_TARGET(Material ALL
SOURCES ${Material_SRCS} ${Material_QRC_SRCS}
# FILE( GLOB MaterialLib_Files ./StandardMaterial/*.FCMat ./StandardMaterial/*.txt )
SET(MaterialLib_Files
Resources/Materials/StandardMaterial/Aggregate/Concrete-EN-C35_45.FCMat
Resources/Materials/StandardMaterial/Aggregate/Concrete-Generic.FCMat
Resources/Materials/StandardMaterial/Aggregate/Reinforcement-FIB-B500.FCMat
Resources/Materials/StandardMaterial/Carbon/Graphite.FCMat
Resources/Materials/StandardMaterial/Glass/Glass-E-GlassFibre.FCMat
Resources/Materials/StandardMaterial/Glass/Glass-Generic.FCMat
Resources/Materials/StandardMaterial/Glass/Glass-S2-GlassFibre.FCMat
Resources/Materials/StandardMaterial/Metal/Alloys/Invar-Generic.FCMat
Resources/Materials/StandardMaterial/Metal/Aluminum/AlMg3F24.FCMat
Resources/Materials/StandardMaterial/Metal/Aluminum/AlMgSi1F31.FCMat
Resources/Materials/StandardMaterial/Metal/Aluminum/Aluminum-6061-T6.FCMat
Resources/Materials/StandardMaterial/Metal/Aluminum/Aluminum-Generic.FCMat
Resources/Materials/StandardMaterial/Metal/Aluminum/AlZn4-5Mg1F35.FCMat
Resources/Materials/StandardMaterial/Metal/Copper/Copper-Generic.FCMat
Resources/Materials/StandardMaterial/Metal/Iron/Iron-Generic.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/CalculiX-Steel.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-15CrNi6.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-17CrNiMo6.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-1C22.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-1C35.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-1C45.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-1C60.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-20NiCrMo2.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-28Mn6.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-2C10.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-30CrNiMo8.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-34CrNiMo6.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-36CrNiMo4.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-36NiCrMo16.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-3C15.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-3C22.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-3C35.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-3V45.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-C10.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-C15.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-C22E.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-C25E.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-C30E.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-C40E.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-C50E.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-C55E.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-C60E.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-E295-GC.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-E295.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-E335-GC.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-E335.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-E360-GC.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-E360.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJL-100.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJL-150.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJL-200.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJL-250.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJL-300.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJL-350.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJMB-350-10.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJMB-550-4.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJMB-650-2.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJMW-350-4.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJMW-360-12.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJMW-400-5.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJMW-450-7.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJS-400-15.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJS-500-7.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJS-600-3.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJS-700-2.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-EN-GJS-800-1.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-G16Mn5.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-G200.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-G20Mn5.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-G230.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-G260.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-G300.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-G30Mn5.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-Generic.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S185.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S235JO.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S235JR.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S235JRG1.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S260NC.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S275JO.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S275JR.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S275N.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S335JO.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S335JR.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S335N.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S340MC.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S355J2G3.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S380MC.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S420MC.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S420N.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S460MC.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S460N.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S500MC.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S550MC.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-S690MC.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-St-37-2K.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-St-E-255.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-St-E-315.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-St-E-380.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-St-E-460.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-St-E-500.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-X2CrNiMoN17-13-3.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-X2CrNiN24-4.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-X39CrMo17-1.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-X3CrNiMo13-14.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-X5CrNi18-10.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-X5CrNiMo17-12-2.FCMat
Resources/Materials/StandardMaterial/Metal/Steel/Steel-X6CrNiTi18-10.FCMat
Resources/Materials/StandardMaterial/Metal/Titanium/Ti-6Al-4V.FCMat
Resources/Materials/StandardMaterial/Thermoplast/ABS-Generic.FCMat
Resources/Materials/StandardMaterial/Thermoplast/Acrylic-Glass-Generic.FCMat
Resources/Materials/StandardMaterial/Thermoplast/PA6-Generic.FCMat
Resources/Materials/StandardMaterial/Thermoplast/PET-Generic.FCMat
Resources/Materials/StandardMaterial/Thermoplast/PLA-Generic.FCMat
Resources/Materials/StandardMaterial/Thermoplast/PP-Generic.FCMat
Resources/Materials/StandardMaterial/Thermoplast/PTFE-Generic.FCMat
Resources/Materials/StandardMaterial/Thermoplast/PVC-Generic.FCMat
Resources/Materials/StandardMaterial/Wood/Wood-Generic.FCMat
)
fc_target_copy_resource(Material
SET(FluidMaterial_Files
Resources/Materials/FluidMaterial/None.FCMat
Resources/Materials/FluidMaterial/Air.FCMat
Resources/Materials/FluidMaterial/Argon.FCMat
Resources/Materials/FluidMaterial/Carbon_dioxide.FCMat
Resources/Materials/FluidMaterial/Nitrogen.FCMat
Resources/Materials/FluidMaterial/Water.FCMat
)
SET(AppearanceLib_Files
Resources/Materials/Appearance/Aluminum.FCMat
Resources/Materials/Appearance/Brass.FCMat
Resources/Materials/Appearance/Bronze.FCMat
Resources/Materials/Appearance/Chrome.FCMat
Resources/Materials/Appearance/Copper.FCMat
Resources/Materials/Appearance/DefaultAppearance.FCMat
Resources/Materials/Appearance/Emerald.FCMat
Resources/Materials/Appearance/Gold.FCMat
Resources/Materials/Appearance/Jade.FCMat
Resources/Materials/Appearance/Metalized.FCMat
Resources/Materials/Appearance/NeonGNC.FCMat
Resources/Materials/Appearance/NeonPHC.FCMat
Resources/Materials/Appearance/Obsidian.FCMat
Resources/Materials/Appearance/Pewter.FCMat
Resources/Materials/Appearance/Plaster.FCMat
Resources/Materials/Appearance/Plastic.FCMat
Resources/Materials/Appearance/Ruby.FCMat
Resources/Materials/Appearance/Satin.FCMat
Resources/Materials/Appearance/ShinyPlastic.FCMat
Resources/Materials/Appearance/Silver.FCMat
Resources/Materials/Appearance/Steel.FCMat
Resources/Materials/Appearance/Stone.FCMat
)
SET(MaterialModel_Files
Resources/Models/Architectural/Architectural.yml
Resources/Models/Costs/Costs.yml
Resources/Models/Electromagnetic/Electromagnetic.yml
Resources/Models/Fluid/Fluid.yml
Resources/Models/Legacy/Father.yml
Resources/Models/Legacy/MaterialStandard.yml
Resources/Models/Mechanical/Density.yml
Resources/Models/Mechanical/HypotheticalExample.yml
Resources/Models/Mechanical/IsotropicLinearElastic.yml
Resources/Models/Mechanical/LinearElastic.yml
Resources/Models/Mechanical/OgdenYld2004p18.yml
Resources/Models/Mechanical/OrthotropicLinearElastic.yml
Resources/Models/Rendering/AdvancedRendering.yml
Resources/Models/Rendering/BasicRendering.yml
Resources/Models/Rendering/TextureRendering.yml
Resources/Models/Rendering/VectorRendering.yml
Resources/Models/Thermal/Thermal.yml
)
ADD_CUSTOM_TARGET(MaterialScripts ALL
SOURCES ${MaterialScripts_Files} ${Material_QRC_SRCS}
)
fc_target_copy_resource(MaterialScripts
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_BINARY_DIR}/Mod/Material
${Material_SRCS})
${MaterialScripts_Files})
INSTALL(FILES ${MaterialScripts_Files} DESTINATION Mod/Material)
ADD_CUSTOM_TARGET(MaterialToolsLib ALL
SOURCES ${MaterialTools_Files}
@@ -191,42 +238,47 @@ fc_target_copy_resource(MaterialIconsLib
INSTALL(FILES ${MaterialTools_Files} DESTINATION Mod/Material/materialtools)
INSTALL(FILES ${Material_Icon_Files} DESTINATION Mod/Material/Resources/icons)
IF (BUILD_GUI)
fc_target_copy_resource(Material
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_BINARY_DIR}/Mod/Material
Material_rc.py)
ENDIF (BUILD_GUI)
ADD_CUSTOM_TARGET(MaterialLib ALL
SOURCES ${MaterialLib_Files}
)
ADD_CUSTOM_TARGET(FluidMaterialLib ALL
SOURCES ${FluidMaterial_Files}
)
ADD_CUSTOM_TARGET(AppearanceLib ALL
SOURCES ${AppearanceLib_Files}
)
ADD_CUSTOM_TARGET(MaterialModelLib ALL
SOURCES ${MaterialModel_Files}
)
# When a target copies files to different output directories then apparently it always builds the project
# which is very annoying. So, the trick is to split this into two targets to avoid this behaviour.
fc_target_copy_resource(MaterialLib
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Mod/Material
${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Mod/Material/
${MaterialLib_Files})
fc_target_copy_resource(FluidMaterialLib
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Mod/Material
${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Mod/Material/
${FluidMaterial_Files})
fc_target_copy_resource(AppearanceLib
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Mod/Material/
${AppearanceLib_Files})
fc_target_copy_resource(MaterialModelLib
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Mod/Material/
${MaterialModel_Files})
INSTALL(
FILES ${Material_SRCS} ${Material_QRC_SRCS}
DESTINATION Mod/Material
)
INSTALL(
DIRECTORY
StandardMaterial
DIRECTORY
FluidMaterial
DESTINATION
${CMAKE_INSTALL_DATADIR}/Mod/Material
FILES_MATCHING PATTERN "*.FCMat*"
)
foreach(file ${MaterialLib_Files} ${FluidMaterial_Files} ${AppearanceLib_Files} ${MaterialModel_Files})
cmake_path(REMOVE_FILENAME file OUTPUT_VARIABLE filepath)
INSTALL(
FILES ${file}
DESTINATION ${CMAKE_INSTALL_DATADIR}/Mod/Material/${filepath}
)
endforeach()

View File

@@ -1,24 +0,0 @@
[General]
Name = Air
Description = Dry air properties at 20 Degrees Celsius and 1 atm
MolarMass = 28.965
Father = Gas
[Fluidic]
Density = 1.204 kg/m^3
DynamicViscosity = 1.80e-5 kg/m/s
KinematicViscosity = 1.511e-5 m^2/s
; PrandtlNumber is a nondimension number for CFD simulation
PrandtlNumber = 0.7
[Thermal]
SpecificHeat = 1.01 kJ/kg/K
ThermalConductivity = 0.02587 W/m/K
; thermal expansion coefficient of ideal gas is 1/temperature
ThermalExpansionCoefficient = 3.43e-3 1/K
[Electromagnetic]
RelativePermittivity = 1.00059
; at 18°C and 50Hz
ElectricalConductivity = 1e-12 S/m
RelativePermeability = 1.0

View File

@@ -1,24 +0,0 @@
[General]
Name = Argon
Description = Argon properties at 20 Degrees Celsius and 1 atm
MolarMass = 39.95
Father = Gas
[Fluidic]
Density = 1.641 kg/m^3
DynamicViscosity = 22.3e-6 kg/m/s
; Kinematic Viscosity = Dynamic Viscosity / Density
KinematicViscosity = 13.59-6 m^2/s
; PrandtlNumber is a nondimension number for CFD simulation
PrandtlNumber = 0.7
[Thermal]
SpecificHeat = 0.520 kJ/kg/K
ThermalConductivity = 0.018 W/m/K
; thermal expansion coefficient of ideal gas is 1/temperature
ThermalExpansionCoefficient = 3.43e-3 1/K
[Electromagnetic]
RelativePermittivity = 1.000513
ElectricalConductivity = 1e-15 S/m
RelativePermeability = 1.0

View File

@@ -1,24 +0,0 @@
[General]
Name = Carbon dioxide
Description = Carbon dioxide properties at 20 Degrees Celsius and 1 atm
MolarMass = 44.009
Father = Gas
[Fluidic]
Density = 1.8393 kg/m^3
DynamicViscosity = 14.7e-6 kg/m/s
; Kinematic Viscosity = Dynamic Viscosity / Density
KinematicViscosity = 8.09e-6 m^2/s
; PrandtlNumber is a nondimension number for CFD simulation
PrandtlNumber = 0.7651
[Thermal]
SpecificHeat = 0.8460 kJ/kg/K
ThermalConductivity = 0.016242 W/m/K
; thermal expansion coefficient of ideal gas is 1/temperature
ThermalExpansionCoefficient = 3.43e-3 1/K
[Electromagnetic]
RelativePermittivity = 1.0009217
ElectricalConductivity = 1e-12 S/m
RelativePermeability = 1.0

View File

@@ -1,24 +0,0 @@
[General]
Name = Nitrogen
Description = Nitrogen properties at 20 Degrees Celsius and 1 atm
MolarMass = 14.007
Father = Gas
[Fluidic]
Density = 1.2506 kg/m^3
DynamicViscosity = 17.58e-6 kg/m/s
; Kinematic Viscosity = Dynamic Viscosity / Density
KinematicViscosity = 14.06e-6 m^2/s
; PrandtlNumber is a nondimension number for CFD simulation
PrandtlNumber = 0.7
[Thermal]
SpecificHeat = 1.04 kJ/kg/K
ThermalConductivity = 25.83e-3 W/m/K
; thermal expansion coefficient of ideal gas is 1/temperature
ThermalExpansionCoefficient = 3.43e-3 1/K
[Electromagnetic]
RelativePermittivity = 1.00058
ElectricalConductivity = 1e-12 S/m
RelativePermeability = 1.0

View File

@@ -1,15 +0,0 @@
; None means nothing, as the starting point of making a new fluid material
[General]
Name = None
Description = "None"
[Fluidic]
Density = 0 kg/m^3
DynamicViscosity = 0 kg/m/s
KinematicViscosity = 0 m^2/s
[Thermal]
SpecificHeat = 0 J/kg/K
ThermalConductivity = 0 W/m/K
ThermalExpansionCoefficient = 0 1/K

View File

@@ -1,31 +0,0 @@
# FreeCAD fluid material library
It's intended to gather the most common fluid properties, water, air, which are useful for other modules and workbenches.
## User defined material
To prevent the database from becoming inefficiently large it is only limited to commonly used variables at 20 degrees Celsius at 1 atm.
Users can defined new material, either in Fem material card editor, or directly generate textual material file, * .FCMat, see example in this folder.
To enable new material, go to FreeCAD menu "Edit->Preference..." Cfd preference page (select on the left panel) and switch to materiai tab on the right.
Browse to your material folder, and save/apply this preference, new material will be Material with same name as FreeCAD material has higher priority, so user defined `Water` material will not appear in Fem material task panel's dropbox list, just give it a different name!
### Edit material value
Please verify the fluid material properties before use. It aims to serve as a quick reference and does not aim to be an extended look up table.
## Add new material to Material module
1. follow examples in material folders to create new material file
2. stick to the meta data definition in `src/Mod/Material/Templatematerial.yml` for property name
3. add the file name into the `src/Mod/Material/CMakeLists.txt` , so the new files can be installed to the properly place during compiling and installation.
## Changelog
CfdOF module authored 5 material types, values are taken from FM White (2011) Fluid Mechanics.
Currently, 3 (Water, Air, None) are merged into Fem module and maintained by Cfd module author, Qingfeng Xia

View File

@@ -1,26 +0,0 @@
; see meta data definition in the file: src/Mod/Material/Templatematerial.yml
[General]
Name = Water
Description = Standard distilled water properties at 20 Degrees Celsius and 1 atm
MolarMass = 18
Father = Gas
ReferenceSource = ''
[Fluidic]
Density = 998 kg/m^3
DynamicViscosity = 1.003e-3 kg/m/s
KinematicViscosity = 1.005e-6 m^2/s
; PrandtlNumber is a nondimension number for CFD simulation
PrandtlNumber = 7.56
[Thermal]
SpecificHeat = 4182 J/kg/K
ThermalConductivity = 0.591 W/m/K
; https://en.wikipedia.org/wiki/Water
ThermalExpansionCoefficient = 2.07e-4 m/m/K
[Electromagnetic]
RelativePermittivity = 80.0
; at 20°C and 50Hz
ElectricalConductivity = 5.5e-6 S/m
RelativePermeability = 0.999992

View File

@@ -0,0 +1,106 @@
/***************************************************************************
* 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_
#endif
#include <Base/Console.h>
#include <Base/Interpreter.h>
#include <Base/PyObjectBase.h>
#include <Gui/Application.h>
#include <Gui/Language/Translator.h>
#include <Gui/WidgetFactory.h>
#include "DlgSettingsMaterial.h"
#include "Workbench.h"
// use a different name to CreateCommand()
void CreateMaterialCommands();
void loadMaterialResource()
{
// add resources and reloads the translators
Q_INIT_RESOURCE(Material);
Q_INIT_RESOURCE(Material_translation);
Gui::Translator::instance()->refresh();
}
namespace MatGui
{
class Module: public Py::ExtensionModule<Module>
{
public:
Module()
: Py::ExtensionModule<Module>("MatGui")
{
initialize("This module is the MatGui module."); // register with Python
}
~Module() override
{}
private:
};
PyObject* initModule()
{
return Base::Interpreter().addModule(new Module);
}
} // namespace MatGui
PyMOD_INIT_FUNC(MatGui)
{
if (!Gui::Application::Instance) {
PyErr_SetString(PyExc_ImportError, "Cannot load Gui module in console application.");
PyMOD_Return(nullptr);
}
// load needed modules
try {
Base::Interpreter().runString("import Material");
}
catch (const Base::Exception& e) {
PyErr_SetString(PyExc_ImportError, e.what());
PyMOD_Return(nullptr);
}
PyObject* matGuiModule = MatGui::initModule();
Base::Console().Log("Loading GUI of Material module... done\n");
MatGui::Workbench ::init();
// instantiating the commands
CreateMaterialCommands();
// register preferences pages on Material, the order here will be the order of the tabs in pref
// widget
new Gui::PrefPageProducer<MatGui::DlgSettingsMaterial>(
QT_TRANSLATE_NOOP("QObject", "Material"));
// add resources and reloads the translators
loadMaterialResource();
PyMOD_Return(matGuiModule);
}

View File

@@ -0,0 +1,158 @@
/***************************************************************************
* 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 <QMessageBox>
#endif
#include <Gui/MainWindow.h>
#include <Mod/Material/App/Exceptions.h>
#include <Mod/Material/App/Materials.h>
#include "Array2D.h"
#include "ArrayDelegate.h"
#include "ArrayModel.h"
#include "ui_Array2D.h"
using namespace MatGui;
/* TRANSLATOR MatGui::Array2D */
Array2D::Array2D(const QString& propertyName, Materials::Material* material, QWidget* parent)
: QDialog(parent)
, ui(new Ui_Array2D)
{
ui->setupUi(this);
if (material->hasPhysicalProperty(propertyName)) {
_property = &(material->getPhysicalProperty(propertyName));
}
else if (material->hasAppearanceProperty(propertyName)) {
_property = &(material->getAppearanceProperty(propertyName));
}
else {
_property = nullptr;
}
if (_property) {
_value =
std::static_pointer_cast<Materials::Material2DArray>(_property->getMaterialValue());
}
else {
_value = nullptr;
}
setupDefault();
setupArray();
connect(ui->standardButtons, &QDialogButtonBox::accepted, this, &Array2D::accept);
connect(ui->standardButtons, &QDialogButtonBox::rejected, this, &Array2D::reject);
}
void Array2D::setupDefault()
{
if (_property == nullptr) {
return;
}
try {
const Materials::MaterialProperty& column1 = _property->getColumn(0);
QString label = QString::fromStdString("Default ") + column1.getName();
ui->labelDefault->setText(label);
if (column1.getPropertyType() == QString::fromStdString("Quantity")) {
ui->editDefault->setMinimum(std::numeric_limits<double>::min());
ui->editDefault->setMaximum(std::numeric_limits<double>::max());
ui->editDefault->setUnitText(_property->getColumnUnits(0));
ui->editDefault->setValue(_value->getDefault().getValue().value<Base::Quantity>());
connect(ui->editDefault,
qOverload<const Base::Quantity&>(&Gui::QuantitySpinBox::valueChanged),
this,
&Array2D::defaultValueChanged);
}
}
catch (const Materials::PropertyNotFound&) {
return;
}
}
void Array2D::setHeaders(QStandardItemModel* model)
{
QStringList headers;
auto columns = _property->getColumns();
for (auto column = columns.begin(); column != columns.end(); column++) {
headers.append(column->getName());
}
model->setHorizontalHeaderLabels(headers);
}
void Array2D::setColumnWidths(QTableView* table)
{
int length = _property->columns();
for (int i = 0; i < length; i++) {
table->setColumnWidth(i, 100);
}
}
void Array2D::setColumnDelegates(QTableView* table)
{
int length = _property->columns();
for (int i = 0; i < length; i++) {
const Materials::MaterialProperty& column = _property->getColumn(i);
table->setItemDelegateForColumn(
i,
new ArrayDelegate(column.getType(), column.getUnits(), this));
}
}
void Array2D::setupArray()
{
if (_property == nullptr) {
return;
}
auto table = ui->tableView;
auto model = new Array2DModel(_property, _value, this);
table->setModel(model);
table->setEditTriggers(QAbstractItemView::AllEditTriggers);
setColumnWidths(table);
setColumnDelegates(table);
}
void Array2D::defaultValueChanged(const Base::Quantity& value)
{
_value->setDefault(QVariant::fromValue(value));
}
void Array2D::accept()
{
QDialog::accept();
}
void Array2D::reject()
{
QDialog::reject();
}
#include "moc_Array2D.cpp"

View File

@@ -0,0 +1,67 @@
/***************************************************************************
* 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_ARRAY2D_H
#define MATGUI_ARRAY2D_H
#include <QAbstractTableModel>
#include <QDialog>
#include <QStandardItem>
#include <QTableView>
#include "ArrayModel.h"
#include <Mod/Material/App/Model.h>
namespace MatGui
{
class Ui_Array2D;
class Array2D: public QDialog
{
Q_OBJECT
public:
explicit Array2D(const QString& propertyName,
Materials::Material* material,
QWidget* parent = nullptr);
~Array2D() override = default;
void defaultValueChanged(const Base::Quantity& value);
void accept() override;
void reject() override;
private:
std::unique_ptr<Ui_Array2D> ui;
const Materials::MaterialProperty* _property;
std::shared_ptr<Materials::Material2DArray> _value;
void setupDefault();
void setHeaders(QStandardItemModel* model);
void setColumnWidths(QTableView* table);
void setColumnDelegates(QTableView* table);
void setupArray();
};
} // namespace MatGui
#endif // MATGUI_ARRAY2D_H

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MatGui::Array2D</class>
<widget class="QDialog" name="MatGui::Array2D">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>578</width>
<height>557</height>
</rect>
</property>
<property name="windowTitle">
<string>2D Array</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="labelDefault">
<property name="text">
<string>Default Value</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::QuantitySpinBox" name="editDefault" native="true">
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableView" name="tableView"/>
</item>
<item>
<widget class="QDialogButtonBox" name="standardButtons">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::QuantitySpinBox</class>
<extends>QWidget</extends>
<header>Gui/QuantitySpinBox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>standardButtons</sender>
<signal>accepted()</signal>
<receiver>MatGui::Array2D</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>standardButtons</sender>
<signal>rejected()</signal>
<receiver>MatGui::Array2D</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,198 @@
/***************************************************************************
* 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 <QMessageBox>
#include <QPushButton>
#endif
#include <Gui/MainWindow.h>
#include <Mod/Material/App/Exceptions.h>
#include <Mod/Material/App/Materials.h>
#include "Array3D.h"
#include "ArrayDelegate.h"
#include "ArrayModel.h"
#include "ui_Array3D.h"
using namespace MatGui;
Array3D::Array3D(const QString& propertyName, Materials::Material* material, QWidget* parent)
: QDialog(parent)
, ui(new Ui_Array3D)
{
ui->setupUi(this);
if (material->hasPhysicalProperty(propertyName)) {
_property = &(material->getPhysicalProperty(propertyName));
}
else if (material->hasAppearanceProperty(propertyName)) {
_property = &(material->getAppearanceProperty(propertyName));
}
else {
_property = nullptr;
}
if (_property) {
_value =
std::static_pointer_cast<Materials::Material3DArray>(_property->getMaterialValue());
}
else {
_value = nullptr;
}
setupDefault();
setupDepthArray();
setupArray();
Base::Console().Log("Material '%s'\n", material->getName().toStdString().c_str());
Base::Console().Log("\tproperty '%s'\n", propertyName.toStdString().c_str());
// connect(ui->splitter, &QSplitter::event,
// this, &Array3D::onSplitter);
connect(ui->standardButtons->button(QDialogButtonBox::Ok),
&QPushButton::clicked,
this,
&Array3D::onOk);
connect(ui->standardButtons->button(QDialogButtonBox::Cancel),
&QPushButton::clicked,
this,
&Array3D::onCancel);
}
bool Array3D::onSplitter(QEvent* e)
{
Q_UNUSED(e)
return false;
}
void Array3D::setupDefault()
{
if (_property == nullptr) {
return;
}
try {
auto& column1 = _property->getColumn(0);
QString label = QString::fromStdString("Default ") + column1.getName();
ui->labelDefault->setText(label);
if (column1.getPropertyType() == QString::fromStdString("Quantity")) {
ui->editDefault->setMinimum(std::numeric_limits<double>::min());
ui->editDefault->setMaximum(std::numeric_limits<double>::max());
ui->editDefault->setUnitText(_property->getColumnUnits(0));
ui->editDefault->setValue(_value->getDefault().getValue().value<Base::Quantity>());
connect(ui->editDefault,
qOverload<const Base::Quantity&>(&Gui::QuantitySpinBox::valueChanged),
this,
&Array3D::defaultValueChanged);
}
}
catch (const Materials::PropertyNotFound&) {
return;
}
}
void Array3D::defaultValueChanged(const Base::Quantity& value)
{
_value->setDefault(QVariant::fromValue(value));
}
void Array3D::setDepthColumnDelegate(QTableView* table)
{
auto& column = _property->getColumn(0);
table->setItemDelegateForColumn(0,
new ArrayDelegate(column.getType(), column.getUnits(), this));
}
void Array3D::setDepthColumnWidth(QTableView* table)
{
table->setColumnWidth(0, 100);
}
void Array3D::setupDepthArray()
{
if (_property == nullptr) {
return;
}
auto table = ui->table3D;
auto model = new Array3DDepthModel(_property, _value, this);
table->setModel(model);
table->setEditTriggers(QAbstractItemView::AllEditTriggers);
setDepthColumnWidth(table);
setDepthColumnDelegate(table);
}
void Array3D::setColumnWidths(QTableView* table)
{
int length = _property->columns();
for (int i = 0; i < length; i++) {
table->setColumnWidth(i, 100);
}
}
void Array3D::setColumnDelegates(QTableView* table)
{
int length = _property->columns();
for (int i = 0; i < length; i++) {
auto& column = _property->getColumn(i);
table->setItemDelegateForColumn(
i,
new ArrayDelegate(column.getType(), column.getUnits(), this));
}
}
void Array3D::setupArray()
{
if (_property == nullptr) {
return;
}
auto table = ui->table2D;
auto model = new Array3DModel(_property, _value, this);
table->setModel(model);
table->setEditTriggers(QAbstractItemView::AllEditTriggers);
setColumnWidths(table);
setColumnDelegates(table);
}
void Array3D::onOk(bool checked)
{
Q_UNUSED(checked)
QDialog::accept();
}
void Array3D::onCancel(bool checked)
{
Q_UNUSED(checked)
QDialog::reject();
}
#include "moc_Array3D.cpp"

View File

@@ -0,0 +1,66 @@
/***************************************************************************
* 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_ARRAY3D_H
#define MATGUI_ARRAY3D_H
#include <QDialog>
#include <QStandardItem>
#include <QTableView>
namespace MatGui
{
class Ui_Array3D;
class Array3D: public QDialog
{
Q_OBJECT
public:
explicit Array3D(const QString& propertyName,
Materials::Material* material,
QWidget* parent = nullptr);
~Array3D() override = default;
void defaultValueChanged(const Base::Quantity& value);
bool onSplitter(QEvent* e);
void onOk(bool checked);
void onCancel(bool checked);
private:
std::unique_ptr<Ui_Array3D> ui;
const Materials::MaterialProperty* _property;
std::shared_ptr<Materials::Material3DArray> _value;
void setupDefault();
void setDepthColumnWidth(QTableView* table);
void setDepthColumnDelegate(QTableView* table);
void setupDepthArray();
void setColumnWidths(QTableView* table);
void setColumnDelegates(QTableView* table);
void setupArray();
};
} // namespace MatGui
#endif // MATGUI_ARRAY3D_H

View File

@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MatGui::Array3D</class>
<widget class="QDialog" name="MatGui::Array3D">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>915</width>
<height>709</height>
</rect>
</property>
<property name="windowTitle">
<string>3D Array</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="labelDefault">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Default Value</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::QuantitySpinBox" name="editDefault" native="true">
<property name="unit" stdset="0">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTableView" name="table3D"/>
<widget class="QTableView" name="table2D"/>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="standardButtons">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::QuantitySpinBox</class>
<extends>QWidget</extends>
<header>Gui/QuantitySpinBox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>standardButtons</sender>
<signal>accepted()</signal>
<receiver>MatGui::Array3D</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>standardButtons</sender>
<signal>rejected()</signal>
<receiver>MatGui::Array3D</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,183 @@
/***************************************************************************
* 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 <QColorDialog>
#include <QDesktopServices>
#include <QIODevice>
#include <QItemSelectionModel>
#include <QPainter>
#include <QString>
#include <QStringList>
#include <QTextStream>
#include <QVariant>
#endif
#include <limits>
#include <App/Application.h>
#include <Base/Interpreter.h>
#include <Base/Quantity.h>
#include <Gui/Application.h>
#include <Gui/Command.h>
#include <Gui/InputField.h>
#include <Gui/PrefWidgets.h>
#include <Gui/SpinBox.h>
#include <Gui/WaitCursor.h>
// #include <Gui/FileDialog.h>
#include <Mod/Material/App/Exceptions.h>
#include <Mod/Material/App/ModelManager.h>
#include "Array2D.h"
#include "Array3D.h"
#include "ArrayDelegate.h"
#include "MaterialSave.h"
using namespace MatGui;
ArrayDelegate::ArrayDelegate(Materials::MaterialValue::ValueType type,
QString units,
QObject* parent)
: QStyledItemDelegate(parent)
, _type(type)
, _units(units)
{}
void ArrayDelegate::paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
if (_type == Materials::MaterialValue::Quantity) {
const AbstractArrayModel* tableModel =
reinterpret_cast<const AbstractArrayModel*>(index.model());
painter->save();
if (tableModel->newRow(index)) {
painter->drawText(option.rect, 0, QString());
}
else {
QVariant item = tableModel->data(index);
Base::Quantity quantity = item.value<Base::Quantity>();
QString text = quantity.getUserString();
painter->drawText(option.rect, 0, text);
}
painter->restore();
}
else {
QStyledItemDelegate::paint(painter, option, index);
}
}
void ArrayDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
Base::Console().Log("ArrayDelegate::setEditorData()\n");
if (_type == Materials::MaterialValue::Quantity) {
QAbstractItemModel* tableModel = const_cast<QAbstractItemModel*>(index.model());
auto item = tableModel->data(index);
// Gui::InputField* input = static_cast<Gui::InputField*>(editor);
// input->setText(item.toString());
Gui::QuantitySpinBox* input = static_cast<Gui::QuantitySpinBox*>(editor);
input->setValue(item.value<Base::Quantity>());
}
else {
QStyledItemDelegate::setEditorData(editor, index);
}
}
QWidget* ArrayDelegate::createEditor(QWidget* parent,
const QStyleOptionViewItem&,
const QModelIndex& index) const
{
Base::Console().Log("ArrayDelegate::createEditor()\n");
const QAbstractTableModel* tableModel = static_cast<const QAbstractTableModel*>(index.model());
auto item = tableModel->data(index);
QWidget* editor = createWidget(parent, item);
return editor;
}
QWidget* ArrayDelegate::createWidget(QWidget* parent, const QVariant& item) const
{
QWidget* widget = nullptr;
if (_type == Materials::MaterialValue::String || _type == Materials::MaterialValue::URL
|| _type == Materials::MaterialValue::List) {
widget = new Gui::PrefLineEdit(parent);
}
else if (_type == Materials::MaterialValue::Integer) {
Gui::UIntSpinBox* spinner = new Gui::UIntSpinBox(parent);
spinner->setMinimum(0);
spinner->setMaximum(UINT_MAX);
spinner->setValue(item.toUInt());
widget = spinner;
}
else if (_type == Materials::MaterialValue::Float) {
Gui::DoubleSpinBox* spinner = new Gui::DoubleSpinBox(parent);
// the magnetic permeability is the parameter for which many decimals matter
// the most however, even for this, 6 digits are sufficient
spinner->setDecimals(6);
// for almost all Float parameters of materials a step of 1 would be too large
spinner->setSingleStep(0.1);
spinner->setMinimum(std::numeric_limits<double>::min());
spinner->setMaximum(std::numeric_limits<double>::max());
spinner->setValue(item.toDouble());
widget = spinner;
}
else if (_type == Materials::MaterialValue::Boolean) {
Gui::PrefComboBox* combo = new Gui::PrefComboBox(parent);
combo->insertItem(0, QString::fromStdString(""));
combo->insertItem(1, QString::fromStdString("False"));
combo->insertItem(2, QString::fromStdString("True"));
combo->setCurrentText(item.toString());
widget = combo;
}
else if (_type == Materials::MaterialValue::Quantity) {
Gui::QuantitySpinBox* input = new Gui::QuantitySpinBox();
input->setMinimum(std::numeric_limits<double>::min());
input->setMaximum(std::numeric_limits<double>::max());
input->setUnitText(_units);
input->setValue(item.value<Base::Quantity>());
widget = input;
}
else {
// Default editor
widget = new QLineEdit(parent);
}
widget->setParent(parent);
return widget;
}
#include "moc_ArrayDelegate.cpp"

View File

@@ -0,0 +1,74 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATGUI_ArrayDelegate_H
#define MATGUI_ArrayDelegate_H
#include <boost/filesystem.hpp>
#include <QDialog>
#include <QDir>
#include <QStandardItem>
#include <QStyledItemDelegate>
#include <QSvgWidget>
#include <QTreeView>
#include <Mod/Material/App/MaterialManager.h>
#include <Mod/Material/App/Materials.h>
#include <Mod/Material/App/ModelManager.h>
namespace fs = boost::filesystem;
namespace MatGui
{
class ArrayDelegate: public QStyledItemDelegate
{
Q_OBJECT
public:
explicit ArrayDelegate(
Materials::MaterialValue::ValueType type = Materials::MaterialValue::None,
QString units = QString(),
QObject* parent = nullptr);
virtual ~ArrayDelegate() = default;
void paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
QWidget* createEditor(QWidget* parent,
const QStyleOptionViewItem&,
const QModelIndex& index) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
// Q_SIGNALS:
/** Emits this signal when a property has changed */
// void propertyChange(const QString &property, const QString value);
private:
Materials::MaterialValue::ValueType _type;
QString _units;
QWidget* createWidget(QWidget* parent, const QVariant& item) const;
};
} // namespace MatGui
#endif // MATGUI_ArrayDelegate_H

View File

@@ -0,0 +1,465 @@
/***************************************************************************
* 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 <QMessageBox>
#endif
#include <QMetaType>
#include <Gui/MainWindow.h>
#include <Gui/MetaTypes.h>
#include <Mod/Material/App/Exceptions.h>
#include <Mod/Material/App/Materials.h>
#include "ArrayModel.h"
using namespace MatGui;
/* TRANSLATOR MatGui::ArrayModel */
AbstractArrayModel::AbstractArrayModel(QObject* parent)
: QAbstractTableModel(parent)
{}
//===
Array2DModel::Array2DModel(const Materials::MaterialProperty* property,
std::shared_ptr<Materials::Material2DArray> value,
QObject* parent)
: AbstractArrayModel(parent)
, _property(property)
, _value(value)
{}
int Array2DModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid()) {
return 0; // No children
}
return _value->rows() + 1; // Will always have 1 empty row
}
bool Array2DModel::newRow(const QModelIndex& index) const
{
return (index.row() == _value->rows());
}
int Array2DModel::columnCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
return _property->columns();
}
QVariant Array2DModel::data(const QModelIndex& index, int role) const
{
if (role == Qt::DisplayRole) {
try {
return _value->getValue(index.row(), index.column());
}
catch (const Materials::InvalidIndex&) {
}
try {
auto column = _property->getColumnType(index.column());
if (column == Materials::MaterialValue::Quantity) {
Base::Quantity q = Base::Quantity(0, _property->getColumnUnits(index.column()));
return QVariant::fromValue(q);
}
}
catch (const Materials::InvalidColumn&) {
}
return QString();
}
return QVariant();
}
QVariant Array2DModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole) {
if (orientation == Qt::Horizontal) {
const Materials::MaterialProperty& column = _property->getColumn(section);
return QVariant(column.getName());
}
else if (orientation == Qt::Vertical) {
// Vertical header
if (section == (rowCount() - 1)) {
return QVariant(QString::fromStdString("*"));
}
return QVariant(section + 1);
}
}
return QAbstractTableModel::headerData(section, orientation, role);
}
bool Array2DModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
Q_UNUSED(role);
if (index.row() == _value->rows()) {
insertRows(index.row(), 1);
}
_value->setValue(index.row(), index.column(), value);
Q_EMIT dataChanged(index, index);
return true;
}
Qt::ItemFlags Array2DModel::flags(const QModelIndex& index) const
{
return (QAbstractTableModel::flags(index) | Qt::ItemIsEditable);
}
// Resizing functions
bool Array2DModel::insertRows(int row, int count, const QModelIndex& parent)
{
beginInsertRows(parent, row, row + count - 1);
int columns = columnCount();
for (int i = 0; i < count; i++) {
std::vector<QVariant>* rowPtr = new std::vector<QVariant>();
for (int j = 0; j < columns; j++) {
rowPtr->push_back(_property->getColumnNull(j));
}
_value->insertRow(row, rowPtr);
}
endInsertRows();
return false;
}
bool Array2DModel::removeRows(int row, int count, const QModelIndex& parent)
{
beginRemoveRows(parent, row, row + count - 1);
endRemoveRows();
return false;
}
bool Array2DModel::insertColumns(int column, int count, const QModelIndex& parent)
{
Q_UNUSED(column);
Q_UNUSED(count);
Q_UNUSED(parent);
return false;
}
bool Array2DModel::removeColumns(int column, int count, const QModelIndex& parent)
{
Q_UNUSED(column);
Q_UNUSED(count);
Q_UNUSED(parent);
return false;
}
//===
Array3DDepthModel::Array3DDepthModel(const Materials::MaterialProperty* property,
std::shared_ptr<Materials::Material3DArray> value,
QObject* parent)
: AbstractArrayModel(parent)
, _property(property)
, _value(value)
{}
int Array3DDepthModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid()) {
return 0; // No children
}
return _value->depth() + 1; // Will always have 1 empty row
}
bool Array3DDepthModel::newRow(const QModelIndex& index) const
{
return (index.row() == _value->depth());
}
QVariant Array3DDepthModel::data(const QModelIndex& index, int role) const
{
if (role == Qt::DisplayRole) {
try {
return _value->getValue(index.row(), index.column());
}
catch (const Materials::InvalidIndex&) {
}
try {
auto column = _property->getColumnType(index.column());
if (column == Materials::MaterialValue::Quantity) {
Base::Quantity q = Base::Quantity(0, _property->getColumnUnits(index.column()));
return QVariant::fromValue(q);
}
}
catch (const Materials::InvalidColumn&) {
}
return QString();
}
return QVariant();
}
QVariant Array3DDepthModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole) {
if (orientation == Qt::Horizontal) {
const Materials::MaterialProperty& column = _property->getColumn(section);
return QVariant(column.getName());
}
else if (orientation == Qt::Vertical) {
// Vertical header
if (section == (rowCount() - 1)) {
return QVariant(QString::fromStdString("*"));
}
return QVariant(section + 1);
}
}
return QAbstractTableModel::headerData(section, orientation, role);
}
bool Array3DDepthModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
Q_UNUSED(role);
if (index.row() == _value->depth()) {
insertRows(index.row(), 1);
}
_value->setValue(index.row(), index.column(), value);
Q_EMIT dataChanged(index, index);
return true;
}
Qt::ItemFlags Array3DDepthModel::flags(const QModelIndex& index) const
{
return (QAbstractTableModel::flags(index) | Qt::ItemIsEditable);
}
// Resizing functions
bool Array3DDepthModel::insertRows(int row, int count, const QModelIndex& parent)
{
beginInsertRows(parent, row, row + count - 1);
int columns = columnCount();
for (int i = 0; i < count; i++) {
std::vector<QVariant>* rowPtr = new std::vector<QVariant>();
for (int j = 0; j < columns; j++) {
rowPtr->push_back(_property->getColumnNull(j));
}
// _value->insertRow(row, rowPtr);
}
endInsertRows();
return false;
}
bool Array3DDepthModel::removeRows(int row, int count, const QModelIndex& parent)
{
beginRemoveRows(parent, row, row + count - 1);
endRemoveRows();
return false;
}
bool Array3DDepthModel::insertColumns(int column, int count, const QModelIndex& parent)
{
Q_UNUSED(column);
Q_UNUSED(count);
Q_UNUSED(parent);
return false;
}
bool Array3DDepthModel::removeColumns(int column, int count, const QModelIndex& parent)
{
Q_UNUSED(column);
Q_UNUSED(count);
Q_UNUSED(parent);
return false;
}
//===
Array3DModel::Array3DModel(const Materials::MaterialProperty* property,
std::shared_ptr<Materials::Material3DArray> value,
QObject* parent)
: AbstractArrayModel(parent)
, _property(property)
, _value(value)
{}
int Array3DModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid()) {
return 0; // No children
}
return _value->depth() + 1; // Will always have 1 empty row
}
int Array3DModel::columnCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
return _property->columns() - 1;
}
bool Array3DModel::newRow(const QModelIndex& index) const
{
return (index.row() == _value->depth());
}
QVariant Array3DModel::data(const QModelIndex& index, int role) const
{
if (role == Qt::DisplayRole) {
Base::Console().Error("Row %d, column %d\n", index.row(), index.column());
try {
return _value->getValue(index.row(), index.column() + 1);
}
catch (const Materials::InvalidIndex&) {
}
catch (const std::exception& e) {
Base::Console().Error("The error message is: %s\n", e.what());
}
try {
auto column = _property->getColumnType(index.column() + 1);
if (column == Materials::MaterialValue::Quantity) {
Base::Quantity q = Base::Quantity(0, _property->getColumnUnits(index.column() - 1));
return QVariant::fromValue(q);
}
}
catch (const Materials::InvalidColumn&) {
}
return QString();
}
return QVariant();
}
QVariant Array3DModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole) {
if (orientation == Qt::Horizontal) {
const Materials::MaterialProperty& column = _property->getColumn(section + 1);
return QVariant(column.getName());
}
else if (orientation == Qt::Vertical) {
// Vertical header
if (section == (rowCount() - 1)) {
return QVariant(QString::fromStdString("*"));
}
return QVariant(section + 1);
}
}
return QAbstractTableModel::headerData(section, orientation, role);
}
bool Array3DModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
Q_UNUSED(role);
if (index.row() == _value->depth()) {
insertRows(index.row(), 1);
}
_value->setValue(index.row(), index.column(), value);
Q_EMIT dataChanged(index, index);
return true;
}
Qt::ItemFlags Array3DModel::flags(const QModelIndex& index) const
{
return (QAbstractTableModel::flags(index) | Qt::ItemIsEditable);
}
// Resizing functions
bool Array3DModel::insertRows(int row, int count, const QModelIndex& parent)
{
beginInsertRows(parent, row, row + count - 1);
int columns = columnCount();
for (int i = 0; i < count; i++) {
std::vector<QVariant>* rowPtr = new std::vector<QVariant>();
for (int j = 0; j < columns; j++) {
rowPtr->push_back(_property->getColumnNull(j));
}
// _value->insertRow(row, rowPtr);
}
endInsertRows();
return false;
}
bool Array3DModel::removeRows(int row, int count, const QModelIndex& parent)
{
beginRemoveRows(parent, row, row + count - 1);
endRemoveRows();
return false;
}
bool Array3DModel::insertColumns(int column, int count, const QModelIndex& parent)
{
Q_UNUSED(column);
Q_UNUSED(count);
Q_UNUSED(parent);
return false;
}
bool Array3DModel::removeColumns(int column, int count, const QModelIndex& parent)
{
Q_UNUSED(column);
Q_UNUSED(count);
Q_UNUSED(parent);
return false;
}

View File

@@ -0,0 +1,138 @@
/***************************************************************************
* 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_ARRAYMODEL_H
#define MATGUI_ARRAYMODEL_H
#include <QAbstractTableModel>
#include <QDialog>
#include <QStandardItem>
#include <QTableView>
#include <Mod/Material/App/Materials.h>
#include <Mod/Material/App/Model.h>
namespace MatGui
{
class AbstractArrayModel: public QAbstractTableModel
{
public:
explicit AbstractArrayModel(QObject* parent = nullptr);
~AbstractArrayModel() override = default;
virtual bool newRow(const QModelIndex& index) const = 0;
};
class Array2DModel: public AbstractArrayModel
{
public:
explicit Array2DModel(const Materials::MaterialProperty* property = nullptr,
std::shared_ptr<Materials::Material2DArray> value = nullptr,
QObject* parent = nullptr);
~Array2DModel() override = default;
// Overridden virtual functions
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
bool newRow(const QModelIndex& index) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant
headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
// Resizing functions
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
bool insertColumns(int column, int count, const QModelIndex& parent = QModelIndex()) override;
bool removeColumns(int column, int count, const QModelIndex& parent = QModelIndex()) override;
private:
const Materials::MaterialProperty* _property;
std::shared_ptr<Materials::Material2DArray> _value;
};
class Array3DDepthModel: public AbstractArrayModel
{
public:
explicit Array3DDepthModel(const Materials::MaterialProperty* property = nullptr,
std::shared_ptr<Materials::Material3DArray> value = nullptr,
QObject* parent = nullptr);
~Array3DDepthModel() override = default;
// Overridden virtual functions
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
bool newRow(const QModelIndex& index) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override
{
Q_UNUSED(parent)
return 1;
}
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant
headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
// Resizing functions
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
bool insertColumns(int column, int count, const QModelIndex& parent = QModelIndex()) override;
bool removeColumns(int column, int count, const QModelIndex& parent = QModelIndex()) override;
private:
const Materials::MaterialProperty* _property;
std::shared_ptr<Materials::Material3DArray> _value;
};
class Array3DModel: public AbstractArrayModel
{
public:
explicit Array3DModel(const Materials::MaterialProperty* property = nullptr,
std::shared_ptr<Materials::Material3DArray> value = nullptr,
QObject* parent = nullptr);
~Array3DModel() override = default;
// Overridden virtual functions
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
bool newRow(const QModelIndex& index) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant
headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
// Resizing functions
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
bool insertColumns(int column, int count, const QModelIndex& parent = QModelIndex()) override;
bool removeColumns(int column, int count, const QModelIndex& parent = QModelIndex()) override;
private:
const Materials::MaterialProperty* _property;
std::shared_ptr<Materials::Material3DArray> _value;
};
} // namespace MatGui
#endif // MATGUI_ARRAYMODEL_H

View File

@@ -0,0 +1,107 @@
if(MSVC)
add_definitions(-DHAVE_ACOSH -DHAVE_ATANH -DHAVE_ASINH)
else(MSVC)
add_definitions(-DHAVE_LIMITS_H -DHAVE_CONFIG_H)
endif(MSVC)
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/src
${CMAKE_BINARY_DIR}/src
${CMAKE_CURRENT_BINARY_DIR}
${Boost_INCLUDE_DIRS}
${COIN3D_INCLUDE_DIRS}
${OCC_INCLUDE_DIR}
${PYTHON_INCLUDE_DIRS}
${XercesC_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIR}
)
link_directories(${OCC_LIBRARY_DIR})
set(MatGui_LIBS
Material
FreeCADGui
)
include_directories(
${QtConcurrent_INCLUDE_DIRS}
)
list(APPEND MatGui_LIBS
${QtConcurrent_LIBRARIES}
)
set(Material_TR_QRC ${CMAKE_CURRENT_BINARY_DIR}/Resources/Material_translation.qrc)
qt_find_and_add_translation(QM_SRCS "Resources/translations/*_*.ts"
${CMAKE_CURRENT_BINARY_DIR}/Resources/translations)
qt_create_resource_file(${Material_TR_QRC} ${QM_SRCS})
qt_add_resources(MatGui_QRC_SRCS Resources/Material.qrc ${Material_TR_QRC})
set(MatGui_UIC_SRCS
Array2D.ui
Array3D.ui
DlgSettingsMaterial.ui
MaterialSave.ui
MaterialsEditor.ui
ModelSelect.ui
)
SET(MatGui_SRCS
${MatGui_QRC_SRCS}
${MatGui_UIC_HDRS}
AppMatGui.cpp
Array2D.cpp
Array2D.h
Array2D.ui
Array3D.cpp
Array3D.h
Array3D.ui
ArrayDelegate.cpp
ArrayDelegate.h
ArrayModel.cpp
ArrayModel.h
Command.cpp
DlgSettingsMaterial.cpp
DlgSettingsMaterial.h
DlgSettingsMaterial.ui
MaterialDelegate.cpp
MaterialDelegate.h
MaterialSave.cpp
MaterialSave.h
MaterialSave.ui
MaterialsEditor.cpp
MaterialsEditor.h
MaterialsEditor.ui
ModelSelect.cpp
ModelSelect.h
ModelSelect.ui
PreCompiled.cpp
PreCompiled.h
Workbench.cpp
Workbench.h
)
if(FREECAD_USE_PCH)
add_definitions(-D_PreComp_)
GET_MSVC_PRECOMPILED_SOURCE("PreCompiled.cpp" PCH_SRCS ${MatGui_SRCS})
ADD_MSVC_PRECOMPILED_HEADER(MatGui PreCompiled.h PreCompiled.cpp PCH_SRCS)
endif(FREECAD_USE_PCH)
SET(MatGuiIcon_SVG
Resources/icons/Materials_Edit.svg
Resources/icons/MaterialWorkbench.svg
Resources/icons/preferences-material.svg
Resources/icons/preview-rendered.svg
Resources/icons/preview-vector.svg
Resources/icons/table.svg
)
add_library(MatGui SHARED ${MatGui_SRCS} ${MatGuiIcon_SVG})
target_link_libraries(MatGui ${MatGui_LIBS})
SET_BIN_DIR(MatGui MatGui /Mod/Material)
SET_PYTHON_PREFIX_SUFFIX(MatGui)
fc_copy_sources(MatGui "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Mod/Material" ${MatGuiIcon_SVG})
INSTALL(TARGETS MatGui DESTINATION ${CMAKE_INSTALL_LIBDIR})
INSTALL(FILES ${MatGuiIcon_SVG} DESTINATION "${CMAKE_INSTALL_DATADIR}/Mod/Material/Resources/icons")

View File

@@ -0,0 +1,81 @@
/***************************************************************************
* 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 <QPointer>
#endif
#include <Gui/Command.h>
#include <Gui/MainWindow.h>
#include "MaterialSave.h"
#include "MaterialsEditor.h"
#include "ModelSelect.h"
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//===========================================================================
// Material_Edit
//===========================================================================
DEF_STD_CMD_A(CmdMaterialsEdit)
CmdMaterialsEdit::CmdMaterialsEdit()
: Command("Materials_Edit")
{
sAppModule = "Material";
sGroup = QT_TR_NOOP("Material");
sMenuText = QT_TR_NOOP("Edit...");
sToolTipText = QT_TR_NOOP("Edit material properties");
sWhatsThis = "Materials_Edit";
sStatusTip = sToolTipText;
sPixmap = "Materials_Edit";
}
void CmdMaterialsEdit::activated(int iMsg)
{
Q_UNUSED(iMsg);
Base::Console().Log("Materials_Edit\n");
static QPointer<QDialog> dlg = nullptr;
if (!dlg) {
dlg = new MatGui::MaterialsEditor(Gui::getMainWindow());
}
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->show();
}
bool CmdMaterialsEdit::isActive()
{
// return (hasActiveDocument() && !Gui::Control().activeDialog());
return true;
}
//---------------------------------------------------------------
void CreateMaterialCommands()
{
Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager();
rcCmdMgr.addCommand(new CmdMaterialsEdit());
}

View File

@@ -0,0 +1,78 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include "PreCompiled.h"
#include "DlgSettingsMaterial.h"
#include "ui_DlgSettingsMaterial.h"
using namespace MatGui;
DlgSettingsMaterial::DlgSettingsMaterial(QWidget* parent)
: PreferencePage(parent)
, ui(new Ui_DlgSettingsMaterial)
{
ui->setupUi(this);
}
void DlgSettingsMaterial::saveSettings()
{
ui->cb_use_built_in_materials->onSave();
ui->cb_use_mat_from_workbenches->onSave();
ui->cb_use_mat_from_config_dir->onSave();
ui->cb_use_mat_from_custom_dir->onSave();
ui->fc_custom_mat_dir->onSave();
ui->cb_delete_duplicates->onSave();
ui->cb_sort_by_resources->onSave();
// Temporary for testing
ui->cb_legacy_editor->onSave();
}
void DlgSettingsMaterial::loadSettings()
{
ui->cb_use_built_in_materials->onRestore();
ui->cb_use_mat_from_workbenches->onRestore();
ui->cb_use_mat_from_config_dir->onRestore();
ui->cb_use_mat_from_custom_dir->onRestore();
ui->fc_custom_mat_dir->onRestore();
ui->cb_delete_duplicates->onRestore();
ui->cb_sort_by_resources->onRestore();
// Temporary for testing
ui->cb_legacy_editor->onRestore();
}
/**
* Sets the strings of the subwidgets using the current language.
*/
void DlgSettingsMaterial::changeEvent(QEvent* e)
{
if (e->type() == QEvent::LanguageChange) {
ui->retranslateUi(this);
}
else {
QWidget::changeEvent(e);
}
}
#include "moc_DlgSettingsMaterial.cpp"

View File

@@ -0,0 +1,52 @@
/***************************************************************************
* 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_DLGSETTINGSMATERIAL_H
#define MATGUI_DLGSETTINGSMATERIAL_H
#include <Gui/PropertyPage.h>
#include <memory>
namespace MatGui
{
class Ui_DlgSettingsMaterial;
class DlgSettingsMaterial: public Gui::Dialog::PreferencePage
{
Q_OBJECT
public:
explicit DlgSettingsMaterial(QWidget* parent = nullptr);
~DlgSettingsMaterial() override = default;
protected:
void saveSettings() override;
void loadSettings() override;
void changeEvent(QEvent* e) override;
private:
std::unique_ptr<Ui_DlgSettingsMaterial> ui;
};
} // namespace MatGui
#endif // MATGUI_DLGSETTINGSMATERIAL_H

View File

@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FemGui::DlgSettingsFemMaterialImp</class>
<widget class="QWidget" name="FemGui::DlgSettingsFemMaterialImp">
<class>MatGui::DlgSettingsMaterial</class>
<widget class="QWidget" name="MatGui::DlgSettingsMaterial">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>434</width>
<height>229</height>
<height>341</height>
</rect>
</property>
<property name="windowTitle">
<string>Material</string>
<string>General</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
@@ -44,6 +44,25 @@
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="cb_use_mat_from_workbenches">
<property name="toolTip">
<string>Use materials added by external workbenches.</string>
</property>
<property name="text">
<string>Use materials from external workbenches</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>UseMaterialsFromWorkbenches</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Material/Resources</cstring>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefCheckBox" name="cb_use_mat_from_config_dir">
<property name="toolTip">
@@ -124,9 +143,6 @@ will be listed as available.</string>
<height>0</height>
</size>
</property>
<property name="mode">
<enum>Gui::FileChooser::Directory</enum>
</property>
<property name="prefEntry" stdset="0">
<cstring>CustomMaterialsDir</cstring>
</property>
@@ -190,6 +206,31 @@ If unchecked, they will be sorted by their name.</string>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_materials_testing">
<property name="title">
<string>Testing</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="Gui::PrefCheckBox" name="cb_legacy_editor">
<property name="text">
<string>Use legacy editor</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="prefEntry" stdset="0">
<cstring>LegacyEditor</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Material/Cards</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
@@ -226,9 +267,7 @@ If unchecked, they will be sorted by their name.</string>
<header>Gui/PrefWidgets.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="Resources/Fem.qrc"/>
</resources>
<resources/>
<connections>
<connection>
<sender>cb_use_mat_from_custom_dir</sender>

View File

@@ -0,0 +1,465 @@
/***************************************************************************
* 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 <QColorDialog>
#include <QDesktopServices>
#include <QIODevice>
#include <QItemSelectionModel>
#include <QPainter>
#include <QString>
#include <QStringList>
#include <QTextStream>
#include <QVariant>
#endif
#include <limits>
#include <App/Application.h>
#include <Base/Interpreter.h>
#include <Base/Quantity.h>
#include <Gui/Application.h>
#include <Gui/Command.h>
#include <Gui/InputField.h>
#include <Gui/PrefWidgets.h>
#include <Gui/SpinBox.h>
#include <Gui/WaitCursor.h>
#include <Mod/Material/App/Exceptions.h>
#include <Mod/Material/App/ModelManager.h>
#include "Array2D.h"
#include "Array3D.h"
#include "MaterialDelegate.h"
#include "MaterialSave.h"
using namespace MatGui;
MaterialDelegate::MaterialDelegate(QObject* parent)
: QStyledItemDelegate(parent)
{}
bool MaterialDelegate::editorEvent(QEvent* event,
QAbstractItemModel* model,
const QStyleOptionViewItem& option,
const QModelIndex& index)
{
if (index.column() == 1) {
if (event->type() == QEvent::MouseButtonDblClick) {
const QStandardItemModel* treeModel =
static_cast<const QStandardItemModel*>(index.model());
// Check we're not the material model root. This is also used to access the entry
// columns
auto item = treeModel->itemFromIndex(index);
auto group = item->parent();
if (!group) {
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
int row = index.row();
QString propertyName = group->child(row, 0)->text();
QString propertyType = QString::fromStdString("String");
if (group->child(row, 2)) {
propertyType = group->child(row, 2)->text();
}
std::string type = propertyType.toStdString();
if (type == "Color") {
Base::Console().Log("Edit color\n");
showColorModal(item);
// Mark as handled
return true;
}
else if (type == "2DArray") {
Base::Console().Log("Edit 2DArray\n");
showArray2DModal(propertyName, item);
// Mark as handled
return true;
}
else if (type == "3DArray") {
Base::Console().Log("Edit 3DArray\n");
showArray3DModal(propertyName, item);
// Mark as handled
return true;
}
}
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
void MaterialDelegate::showColorModal(QStandardItem* item)
{
QColor currentColor; // = d->col;
currentColor.setRgba(parseColor(item->text()));
QColorDialog* dlg = new QColorDialog(currentColor);
dlg->setAttribute(Qt::WA_DeleteOnClose);
if (Gui::DialogOptions::dontUseNativeColorDialog()) {
dlg->setOptions(QColorDialog::DontUseNativeDialog);
}
dlg->setOption(QColorDialog::ColorDialogOption::ShowAlphaChannel, false);
dlg->setCurrentColor(currentColor);
dlg->adjustSize();
connect(dlg, &QColorDialog::finished, this, [&](int result) {
if (result == QDialog::Accepted) {
QColor color = dlg->selectedColor();
if (color.isValid()) {
QString colorText = QString(QString::fromStdString("(%1,%2,%3,%4)"))
.arg(color.red() / 255.0)
.arg(color.green() / 255.0)
.arg(color.blue() / 255.0)
.arg(color.alpha() / 255.0);
item->setText(colorText);
}
}
});
dlg->exec();
}
void MaterialDelegate::showArray2DModal(const QString& propertyName, QStandardItem* item)
{
Materials::Material* material = item->data().value<Materials::Material*>();
Array2D* dlg = new Array2D(propertyName, material);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->adjustSize();
connect(dlg, &QDialog::finished, this, [&](int result) {
if (result == QDialog::Accepted) {
Base::Console().Log("Accepted\n");
}
});
dlg->exec();
}
void MaterialDelegate::showArray3DModal(const QString& propertyName, QStandardItem* item)
{
Materials::Material* material = item->data().value<Materials::Material*>();
Array3D* dlg = new Array3D(propertyName, material);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->adjustSize();
connect(dlg, &QDialog::finished, this, [&](int result) {
if (result == QDialog::Accepted) {
Base::Console().Log("Accepted\n");
}
});
dlg->exec();
}
void MaterialDelegate::paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
// Base::Console().Log("MaterialsEditor::paint()\n");
if (index.column() != 1) {
QStyledItemDelegate::paint(painter, option, index);
return;
}
const QStandardItemModel* treeModel = static_cast<const QStandardItemModel*>(index.model());
// Check we're not the material model root. This is also used to access the entry columns
auto item = treeModel->itemFromIndex(index);
auto group = item->parent();
if (!group) {
QStyledItemDelegate::paint(painter, option, index);
return;
}
int row = index.row();
QString propertyName = group->child(row, 0)->text();
QString propertyType = QString::fromStdString("String");
if (group->child(row, 2)) {
propertyType = group->child(row, 2)->text();
}
QString propertyValue = QString::fromStdString("");
if (group->child(row, 1)) {
propertyValue = group->child(row, 1)->text();
}
std::string type = propertyType.toStdString();
if (type == "Color") {
painter->save();
QColor color;
color.setRgba(parseColor(propertyValue));
int left = option.rect.left() + 5;
int width = option.rect.width() - 10;
if (option.rect.width() > 75) {
left += (option.rect.width() - 75) / 2;
width = 65;
}
painter->fillRect(left,
option.rect.top() + 5,
width,
option.rect.height() - 10,
QBrush(color));
painter->restore();
return;
}
else if (type == "2DArray" || type == "3DArray") {
// painter->save();
QImage table(QString::fromStdString(":/icons/table.svg"));
QRect target(option.rect);
if (target.width() > target.height()) {
target.setWidth(target.height());
}
else {
target.setHeight(target.width());
}
painter->drawImage(target, table, table.rect());
// painter->restore();
return;
}
QStyledItemDelegate::paint(painter, option, index);
}
QSize MaterialDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
if (index.column() != 1) {
return QStyledItemDelegate::sizeHint(option, index);
}
const QStandardItemModel* treeModel = static_cast<const QStandardItemModel*>(index.model());
// Check we're not the material model root. This is also used to access the entry columns
auto item = treeModel->itemFromIndex(index);
auto group = item->parent();
if (!group) {
return QStyledItemDelegate::sizeHint(option, index);
}
int row = index.row();
QString propertyType = QString::fromStdString("String");
if (group->child(row, 2)) {
propertyType = group->child(row, 2)->text();
}
std::string type = propertyType.toStdString();
if (type == "Color") {
return QSize(75, 23); // Standard QPushButton size
}
else if (type == "2DArray" || type == "3DArray") {
return QSize(23, 23);
}
return QStyledItemDelegate::sizeHint(option, index);
}
void MaterialDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
Base::Console().Log("MaterialsEditor::setEditorData()\n");
QVariant propertyType = editor->property("Type");
const QStandardItemModel* model = static_cast<const QStandardItemModel*>(index.model());
QStandardItem* item = model->itemFromIndex(index);
auto group = item->parent();
if (!group) {
return;
}
int row = index.row();
QString propertyName = group->child(row, 0)->text();
std::string type = propertyType.toString().toStdString();
if (type == "File") {
Gui::FileChooser* chooser = static_cast<Gui::FileChooser*>(editor);
item->setText(chooser->fileName());
}
else if (type == "Quantity") {
Gui::InputField* input = static_cast<Gui::InputField*>(editor);
item->setText(input->getQuantityString());
}
else {
QStyledItemDelegate::setEditorData(editor, index);
}
// Q_EMIT const_cast<MaterialDelegate *>(this)->propertyChange(propertyName, item->text());
}
void MaterialDelegate::setModelData(QWidget* editor,
QAbstractItemModel* model,
const QModelIndex& index) const
{
QStyledItemDelegate::setModelData(editor, model, index);
QStandardItem* item = static_cast<const QStandardItemModel*>(model)->itemFromIndex(index);
auto group = item->parent();
if (!group) {
return;
}
int row = index.row();
QString propertyName = group->child(row, 0)->text();
Q_EMIT const_cast<MaterialDelegate*>(this)->propertyChange(propertyName, item->text());
}
QWidget* MaterialDelegate::createEditor(QWidget* parent,
const QStyleOptionViewItem&,
const QModelIndex& index) const
{
Base::Console().Log("MaterialsEditor::createEditor()\n");
if (index.column() != 1) {
return nullptr;
}
const QStandardItemModel* treeModel = static_cast<const QStandardItemModel*>(index.model());
// Check we're not the material model root. This is also used to access the entry columns
auto item = treeModel->itemFromIndex(index);
auto group = item->parent();
if (!group) {
return nullptr;
}
int row = index.row();
QString propertyName = group->child(row, 0)->text();
QString propertyType = QString::fromStdString("String");
if (group->child(row, 2)) {
propertyType = group->child(row, 2)->text();
}
QString propertyValue = QString::fromStdString("");
if (group->child(row, 1)) {
propertyValue = group->child(row, 1)->text();
}
QString propertyUnits = QString::fromStdString("");
if (group->child(row, 1)) {
propertyUnits = group->child(row, 3)->text();
}
QWidget* editor =
createWidget(parent, propertyName, propertyType, propertyValue, propertyUnits);
return editor;
}
QWidget* MaterialDelegate::createWidget(QWidget* parent,
const QString& propertyName,
const QString& propertyType,
const QString& propertyValue,
const QString& propertyUnits) const
{
Q_UNUSED(propertyName);
QWidget* widget = nullptr;
std::string type = propertyType.toStdString();
if (type == "String" || type == "URL" || type == "Vector") {
widget = new Gui::PrefLineEdit(parent);
}
else if ((type == "Integer") || (type == "Int")) {
Gui::UIntSpinBox* spinner = new Gui::UIntSpinBox(parent);
spinner->setMinimum(0);
spinner->setMaximum(UINT_MAX);
spinner->setValue(propertyValue.toUInt());
widget = spinner;
}
else if (type == "Float") {
Gui::DoubleSpinBox* spinner = new Gui::DoubleSpinBox(parent);
// the magnetic permeability is the parameter for which many decimals matter
// the most however, even for this, 6 digits are sufficient
spinner->setDecimals(6);
// for almost all Float parameters of materials a step of 1 would be too large
spinner->setSingleStep(0.1);
spinner->setMinimum(std::numeric_limits<double>::min());
spinner->setMaximum(std::numeric_limits<double>::max());
spinner->setValue(propertyValue.toDouble());
widget = spinner;
}
else if (type == "Boolean") {
Gui::PrefComboBox* combo = new Gui::PrefComboBox(parent);
combo->insertItem(0, QString::fromStdString(""));
combo->insertItem(1, QString::fromStdString("False"));
combo->insertItem(2, QString::fromStdString("True"));
combo->setCurrentText(propertyValue);
widget = combo;
}
else if (type == "Quantity") {
Gui::InputField* input = new Gui::InputField();
input->setMinimum(std::numeric_limits<double>::min());
input->setMaximum(std::numeric_limits<double>::max());
input->setUnitText(propertyUnits); // TODO: Ensure this exists
input->setPrecision(6);
input->setQuantityString(propertyValue);
widget = input;
}
else if (type == "File") {
Gui::FileChooser* chooser = new Gui::FileChooser();
if (propertyValue.length() > 0) {
chooser->setFileName(propertyValue);
}
widget = chooser;
}
else {
// Default editor
widget = new QLineEdit(parent);
}
widget->setProperty("Type", propertyType);
widget->setParent(parent);
return widget;
}
QRgb MaterialDelegate::parseColor(const QString& color) const
{
QString trimmed = color;
trimmed.replace(QRegularExpression(QString::fromStdString("\\(([^<]*)\\)")),
QString::fromStdString("\\1"));
QStringList parts = trimmed.split(QString::fromStdString(","));
if (parts.length() < 4) {
return qRgba(0, 0, 0, 1);
}
int red = parts.at(0).toDouble() * 255;
int green = parts.at(1).toDouble() * 255;
int blue = parts.at(2).toDouble() * 255;
int alpha = parts.at(3).toDouble() * 255;
return qRgba(red, green, blue, alpha);
}
#include "moc_MaterialDelegate.cpp"

View File

@@ -0,0 +1,86 @@
/***************************************************************************
* 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_MATERIALDELEGATE_H
#define MATGUI_MATERIALDELEGATE_H
#include <boost/filesystem.hpp>
#include <QDialog>
#include <QDir>
#include <QStandardItem>
#include <QStyledItemDelegate>
#include <QSvgWidget>
#include <QTreeView>
#include <Mod/Material/App/MaterialManager.h>
#include <Mod/Material/App/Materials.h>
#include <Mod/Material/App/ModelManager.h>
namespace fs = boost::filesystem;
namespace MatGui
{
class MaterialDelegate: public QStyledItemDelegate
{
Q_OBJECT
public:
explicit MaterialDelegate(QObject* parent = nullptr);
~MaterialDelegate() override = default;
QWidget* createEditor(QWidget* parent,
const QStyleOptionViewItem&,
const QModelIndex& index) const override;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor,
QAbstractItemModel* model,
const QModelIndex& index) const override;
protected:
bool editorEvent(QEvent* event,
QAbstractItemModel* model,
const QStyleOptionViewItem& option,
const QModelIndex& index) override;
Q_SIGNALS:
/** Emits this signal when a property has changed */
void propertyChange(const QString& property, const QString value);
private:
QWidget* createWidget(QWidget* parent,
const QString& propertyName,
const QString& propertyType,
const QString& propertyValue,
const QString& propertyUnits) const;
QRgb parseColor(const QString& color) const;
void showColorModal(QStandardItem* item);
void showArray2DModal(const QString& propertyName, QStandardItem* item);
void showArray3DModal(const QString& propertyName, QStandardItem* item);
};
} // namespace MatGui
#endif // MATGUI_MATERIALDELEGATE_H

View File

@@ -0,0 +1,313 @@
/***************************************************************************
* 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 <QMessageBox>
#include <QTreeView>
#endif
#include <Gui/MainWindow.h>
#include <Mod/Material/App/MaterialLibrary.h>
#include "MaterialSave.h"
#include "ui_MaterialSave.h"
using namespace MatGui;
/* TRANSLATOR MatGui::MaterialsEditor */
MaterialSave::MaterialSave(Materials::Material* material, QWidget* parent)
: QDialog(parent)
, ui(new Ui_MaterialSave)
, _material(material)
, _selectedPath(QString::fromStdString("/"))
, _selectedUUID(QString())
{
ui->setupUi(this);
setLibraries();
createModelTree();
showSelectedTree();
if (_material->getName().length() > 0) {
ui->editFilename->setText(_material->getName() + QString::fromStdString(".FCMat"));
}
else {
ui->editFilename->setText(QString::fromStdString("NewMaterial.FCMat"));
}
_filename = QString(ui->editFilename->text()); // No filename by default
connect(ui->standardButtons->button(QDialogButtonBox::Ok),
&QPushButton::clicked,
this,
&MaterialSave::onOk);
connect(ui->standardButtons->button(QDialogButtonBox::Cancel),
&QPushButton::clicked,
this,
&MaterialSave::onCancel);
connect(ui->comboLibrary,
&QComboBox::currentTextChanged,
this,
&MaterialSave::currentTextChanged);
connect(ui->buttonNewFolder, &QPushButton::clicked, this, &MaterialSave::onNewFolder);
connect(ui->editFilename, &QLineEdit::textEdited, this, &MaterialSave::onFilename);
QItemSelectionModel* selectionModel = ui->treeMaterials->selectionModel();
connect(selectionModel,
&QItemSelectionModel::selectionChanged,
this,
&MaterialSave::onSelectModel);
}
/*
* Destroys the object and frees any allocated resources
*/
MaterialSave::~MaterialSave()
{
// no need to delete child widgets, Qt does it all for us
}
void MaterialSave::onOk(bool checked)
{
Q_UNUSED(checked)
QString name = _filename.remove(QString::fromStdString(".FCMat"), Qt::CaseInsensitive);
Base::Console().Log("name '%s'\n", _filename.toStdString().c_str());
if (name != _material->getName()) {
_material->setName(name);
_material->setEditStateAlter(); // ? Does a name change count?
}
auto variant = ui->comboLibrary->currentData();
auto library = variant.value<Materials::MaterialLibrary>();
QFileInfo filepath(_selectedPath + QString::fromStdString("/") + name
+ QString::fromStdString(".FCMat"));
Base::Console().Log("saveMaterial(library(%s), material(%s), path(%s))\n",
library.getName().toStdString().c_str(),
_material->getName().toStdString().c_str(),
filepath.filePath().toStdString().c_str());
_manager.saveMaterial(&library, *_material, filepath.filePath());
accept();
}
void MaterialSave::onCancel(bool checked)
{
Q_UNUSED(checked)
reject();
}
void MaterialSave::accept()
{
QDialog::accept();
}
void MaterialSave::reject()
{
QDialog::reject();
}
void MaterialSave::setLibraries()
{
auto libraries = _manager.getMaterialLibraries();
for (auto library : *libraries) {
if (!library->isReadOnly()) {
QVariant libraryVariant;
libraryVariant.setValue(*library);
ui->comboLibrary->addItem(library->getName(), libraryVariant);
}
}
}
void MaterialSave::createModelTree()
{
auto tree = ui->treeMaterials;
auto model = new QStandardItemModel();
tree->setModel(model);
tree->setHeaderHidden(true);
}
void MaterialSave::addExpanded(QTreeView* tree, QStandardItem* parent, QStandardItem* child)
{
parent->appendRow(child);
tree->setExpanded(child->index(), true);
}
void MaterialSave::addExpanded(QTreeView* tree, QStandardItemModel* parent, QStandardItem* child)
{
parent->appendRow(child);
tree->setExpanded(child->index(), true);
}
void MaterialSave::addMaterials(
QStandardItem& parent,
const std::shared_ptr<std::map<QString, Materials::MaterialTreeNode*>> modelTree,
const QIcon& folderIcon,
const QIcon& icon)
{
auto tree = ui->treeMaterials;
for (auto& mat : *modelTree) {
Materials::MaterialTreeNode* nodePtr = mat.second;
if (nodePtr->getType() == Materials::MaterialTreeNode::DataNode) {
const Materials::Material* 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 | Qt::ItemIsDragEnabled
// | Qt::ItemIsDropEnabled);
card->setData(QVariant(uuid), Qt::UserRole);
addExpanded(tree, &parent, card);
}
else {
auto node = new QStandardItem(folderIcon, mat.first);
addExpanded(tree, &parent, node);
// node->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
auto treeMap = nodePtr->getFolder();
addMaterials(*node, treeMap, folderIcon, icon);
}
}
}
void MaterialSave::showSelectedTree()
{
auto tree = ui->treeMaterials;
auto model = static_cast<QStandardItemModel*>(tree->model());
model->clear();
if (ui->comboLibrary->count() > 0) {
auto variant = ui->comboLibrary->currentData();
auto library = variant.value<Materials::MaterialLibrary>();
QIcon icon(library.getIconPath());
QIcon folderIcon(QString::fromStdString(":/icons/folder.svg"));
_libraryName = library.getName();
_selectedPath = QString::fromStdString("/") + _libraryName;
auto lib = new QStandardItem(library.getName());
lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
addExpanded(tree, model, lib);
auto modelTree = _manager.getMaterialTree(library);
addMaterials(*lib, modelTree, folderIcon, icon);
}
else {
QMessageBox::warning(Gui::getMainWindow(),
QObject::tr("No writeable library"),
QObject::tr("No writeable library"));
}
}
QString MaterialSave::getPath(const QStandardItem* item) const
{
QString path = QString::fromStdString("/");
if (item) {
path = path + item->text();
if (item->parent()) {
return getPath(item->parent()) + path;
}
}
return path;
}
void MaterialSave::onSelectModel(const QItemSelection& selected, const QItemSelection& deselected)
{
// Q_UNUSED(selected);
Q_UNUSED(deselected);
_filename = QString(ui->editFilename->text()); // No filename by default
QStandardItemModel* model = static_cast<QStandardItemModel*>(ui->treeMaterials->model());
QModelIndexList indexes = selected.indexes();
if (indexes.count() == 0) {
Base::Console().Log("Nothing selected\n");
_selectedPath = QString::fromStdString("/") + _libraryName;
_selectedUUID = QString();
Base::Console().Log("\tSelected path '%s'\n", _selectedPath.toStdString().c_str());
return;
}
for (auto it = indexes.begin(); it != indexes.end(); it++) {
QStandardItem* item = model->itemFromIndex(*it);
Base::Console().Log("%s\n", item->text().toStdString().c_str());
if (item) {
auto _selected = item->data(Qt::UserRole);
if (_selected.isValid()) {
Base::Console().Log("\tuuid %s\n", _selected.toString().toStdString().c_str());
_selectedPath = getPath(item->parent());
_selectedUUID = _selected.toString();
_filename = item->text();
}
else {
_selectedPath = getPath(item);
_selectedUUID = QString();
}
}
}
if (_filename.length() > 0) {
ui->editFilename->setText(_filename);
}
Base::Console().Log("\tSelected path '%s', filename = '%s'\n",
_selectedPath.toStdString().c_str(),
_filename.toStdString().c_str());
}
void MaterialSave::currentTextChanged(const QString& value)
{
Q_UNUSED(value)
showSelectedTree();
}
void MaterialSave::onNewFolder(bool checked)
{
Q_UNUSED(checked)
auto tree = ui->treeMaterials;
auto model = static_cast<QStandardItemModel*>(tree->model());
auto current = tree->currentIndex();
if (!current.isValid()) {
current = model->index(0, 0);
}
auto item = model->itemFromIndex(current);
if (item->hasChildren()) {
Base::Console().Log("Add new folder to '%s'\n", item->text().toStdString().c_str());
QIcon folderIcon(QString::fromStdString(":/icons/folder.svg"));
auto node = new QStandardItem(folderIcon, QString::fromStdString("New Folder"));
addExpanded(tree, item, node);
// node->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
}
}
void MaterialSave::onFilename(const QString& text)
{
Base::Console().Log("MaterialSave::onFilename('%s')\n", text.toStdString().c_str());
_filename = text;
}
#include "moc_MaterialSave.cpp"

View File

@@ -0,0 +1,81 @@
/***************************************************************************
* 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_MATERIALSAVE_H
#define MATGUI_MATERIALSAVE_H
// #include <boost/filesystem.hpp>
#include <QDialog>
#include <QItemSelection>
#include <QStandardItem>
#include <QTreeView>
#include <Mod/Material/App/MaterialManager.h>
namespace MatGui
{
class Ui_MaterialSave;
class MaterialSave: public QDialog
{
Q_OBJECT
public:
explicit MaterialSave(Materials::Material* material, QWidget* parent = nullptr);
~MaterialSave() override;
void setLibraries();
void createModelTree();
void addExpanded(QTreeView* tree, QStandardItem* parent, QStandardItem* child);
void addExpanded(QTreeView* tree, QStandardItemModel* parent, QStandardItem* child);
void
addMaterials(QStandardItem& parent,
const std::shared_ptr<std::map<QString, Materials::MaterialTreeNode*>> modelTree,
const QIcon& folderIcon,
const QIcon& icon);
void showSelectedTree();
void onSelectModel(const QItemSelection& selected, const QItemSelection& deselected);
void currentTextChanged(const QString& value);
void onNewFolder(bool checked);
void onFilename(const QString& text);
void onOk(bool checked);
void onCancel(bool checked);
void accept() override;
void reject() override;
private:
std::unique_ptr<Ui_MaterialSave> ui;
Materials::MaterialManager _manager;
Materials::Material* _material;
QString _selectedPath;
QString _selectedUUID;
QString _libraryName;
QString _filename;
QString getPath(const QStandardItem* item) const;
};
} // namespace MatGui
#endif // MATGUI_MATERIALSAVE_H

View File

@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MatGui::MaterialSave</class>
<widget class="QDialog" name="MatGui::MaterialSave">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>654</width>
<height>708</height>
</rect>
</property>
<property name="windowTitle">
<string>Save Material</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Library:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboLibrary">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTreeView" name="treeMaterials"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonNewFolder">
<property name="text">
<string>New Folder</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Filename:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="editFilename">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="standardButtons">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>standardButtons</sender>
<signal>accepted()</signal>
<receiver>MatGui::MaterialSave</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>standardButtons</sender>
<signal>rejected()</signal>
<receiver>MatGui::MaterialSave</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

View File

@@ -0,0 +1,904 @@
/***************************************************************************
* 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 <QColorDialog>
#include <QDesktopServices>
#include <QIODevice>
#include <QItemSelectionModel>
#include <QMessageBox>
#include <QString>
#include <QStringList>
#include <QTextStream>
#include <QVariant>
#endif
#include <limits>
#include <App/Application.h>
#include <Base/Interpreter.h>
#include <Base/Quantity.h>
#include <Gui/Application.h>
#include <Gui/Command.h>
#include <Gui/InputField.h>
#include <Gui/PrefWidgets.h>
#include <Gui/SpinBox.h>
#include <Gui/WaitCursor.h>
#include <Mod/Material/App/Exceptions.h>
#include <Mod/Material/App/ModelManager.h>
#include "MaterialDelegate.h"
#include "MaterialSave.h"
#include "MaterialsEditor.h"
#include "ModelSelect.h"
#include "ui_MaterialsEditor.h"
using namespace MatGui;
/* TRANSLATOR MatGui::MaterialsEditor */
MaterialsEditor::MaterialsEditor(QWidget* parent)
: QDialog(parent)
, ui(new Ui_MaterialsEditor)
, _edited(false)
{
ui->setupUi(this);
getFavorites();
getRecents();
createMaterialTree();
createPhysicalTree();
createAppearanceTree();
createPreviews();
ui->buttonURL->setIcon(QIcon(QString::fromStdString(":/icons/internet-web-browser.svg")));
connect(ui->standardButtons->button(QDialogButtonBox::Ok),
&QPushButton::clicked,
this,
&MaterialsEditor::onOk);
connect(ui->standardButtons->button(QDialogButtonBox::Cancel),
&QPushButton::clicked,
this,
&MaterialsEditor::onCancel);
connect(ui->standardButtons->button(QDialogButtonBox::Save),
&QPushButton::clicked,
this,
&MaterialsEditor::onSave);
connect(ui->buttonURL, &QPushButton::clicked, this, &MaterialsEditor::onURL);
connect(ui->buttonPhysicalAdd, &QPushButton::clicked, this, &MaterialsEditor::onPhysicalAdd);
connect(ui->buttonAppearanceAdd,
&QPushButton::clicked,
this,
&MaterialsEditor::onAppearanceAdd);
connect(ui->buttonFavorite, &QPushButton::clicked, this, &MaterialsEditor::onFavourite);
QItemSelectionModel* selectionModel = ui->treeMaterials->selectionModel();
connect(selectionModel,
&QItemSelectionModel::selectionChanged,
this,
&MaterialsEditor::onSelectMaterial);
}
void MaterialsEditor::getFavorites()
{
_favorites.clear();
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/Favorites");
int count = param->GetInt("Favorites", 0);
for (int i = 0; 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);
}
}
void MaterialsEditor::saveFavorites()
{
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/Favorites");
// Clear out the existing favorites
int count = param->GetInt("Favorites", 0);
for (int i = 0; i < count; i++) {
QString key = QString::fromLatin1("FAV%1").arg(i);
param->RemoveASCII(key.toStdString().c_str());
}
// Add the current values
param->SetInt("Favorites", _favorites.size());
int j = 0;
for (auto favorite : _favorites) {
QString key = QString::fromLatin1("FAV%1").arg(j);
param->SetASCII(key.toStdString().c_str(), favorite.toStdString());
j++;
}
}
void MaterialsEditor::addFavorite(const QString& uuid)
{
// Ensure it is a material. New, unsaved materials will not be
try {
const Materials::Material& material = _materialManager.getMaterial(uuid);
Q_UNUSED(material)
}
catch (const Materials::MaterialNotFound&) {
return;
}
if (!isFavorite(uuid)) {
_favorites.push_back(uuid);
saveFavorites();
refreshMaterialTree();
}
}
void MaterialsEditor::removeFavorite(const QString& uuid)
{
if (isFavorite(uuid)) {
_favorites.remove(uuid);
saveFavorites();
refreshMaterialTree();
}
}
bool MaterialsEditor::isFavorite(const QString& uuid) const
{
for (auto it : _favorites) {
if (it == uuid) {
return true;
}
}
return false;
}
void MaterialsEditor::getRecents()
{
_recents.clear();
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/Recent");
_recentMax = param->GetInt("RecentMax", 5);
int count = param->GetInt("Recent", 0);
for (int i = 0; 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);
}
}
void MaterialsEditor::saveRecents()
{
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/Recent");
// Clear out the existing favorites
int count = param->GetInt("Recent", 0);
for (int i = 0; i < count; i++) {
QString key = QString::fromLatin1("MRU%1").arg(i);
param->RemoveASCII(key.toStdString().c_str());
}
// Add the current values
int size = _recents.size();
if (size > _recentMax) {
size = _recentMax;
}
param->SetInt("Recent", size);
int j = 0;
for (auto recent : _recents) {
QString key = QString::fromLatin1("MRU%1").arg(j);
param->SetASCII(key.toStdString().c_str(), recent.toStdString());
j++;
if (j >= size) {
break;
}
}
}
void MaterialsEditor::addRecent(const QString& uuid)
{
// Ensure it is a material. New, unsaved materials will not be
try {
const Materials::Material& material = _materialManager.getMaterial(uuid);
Q_UNUSED(material)
}
catch (const Materials::MaterialNotFound&) {
return;
}
// Ensure no duplicates
if (isRecent(uuid)) {
_recents.remove(uuid);
}
_recents.push_front(uuid);
while (_recents.size() > static_cast<std::size_t>(_recentMax)) {
_recents.pop_back();
}
saveRecents();
}
bool MaterialsEditor::isRecent(const QString& uuid) const
{
for (auto it : _recents) {
if (it == uuid) {
return true;
}
}
return false;
}
void MaterialsEditor::propertyChange(const QString& property, const QString value)
{
Base::Console().Log("MaterialsEditor::propertyChange(%s) = '%s'\n",
property.toStdString().c_str(),
value.toStdString().c_str());
if (_material.hasPhysicalProperty(property)) {
_material.setPhysicalValue(property, value);
}
else if (_material.hasAppearanceProperty(property)) {
_material.setAppearanceValue(property, value);
updatePreview();
}
_edited = true;
}
void MaterialsEditor::onURL(bool checked)
{
Q_UNUSED(checked)
Base::Console().Log("URL\n");
QString url = ui->editSourceURL->text();
if (url.length() > 0) {
QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode));
}
}
void MaterialsEditor::onPhysicalAdd(bool checked)
{
Q_UNUSED(checked)
ModelSelect dialog(this, Materials::ModelManager::ModelFilter_Physical);
dialog.setModal(true);
if (dialog.exec() == QDialog::Accepted) {
QString selected = dialog.selectedModel();
Base::Console().Log("Selected model '%s'\n", selected.toStdString().c_str());
_material.addPhysical(selected);
updateMaterial();
}
else {
Base::Console().Log("No model selected\n");
}
}
void MaterialsEditor::onAppearanceAdd(bool checked)
{
Q_UNUSED(checked)
ModelSelect dialog(this, Materials::ModelManager::ModelFilter_Appearance);
dialog.setModal(true);
if (dialog.exec() == QDialog::Accepted) {
QString selected = dialog.selectedModel();
Base::Console().Log("Selected model '%s'\n", selected.toStdString().c_str());
_material.addAppearance(selected);
updateMaterial();
}
else {
Base::Console().Log("No model selected\n");
}
}
void MaterialsEditor::onFavourite(bool checked)
{
Q_UNUSED(checked)
Base::Console().Log("Favorite\n");
auto selected = _material.getUUID();
if (isFavorite(selected)) {
removeFavorite(selected);
}
else {
addFavorite(selected);
}
}
void MaterialsEditor::onOk(bool checked)
{
Q_UNUSED(checked)
accept();
}
void MaterialsEditor::onCancel(bool checked)
{
Q_UNUSED(checked)
reject();
}
void MaterialsEditor::onSave(bool checked)
{
Q_UNUSED(checked)
saveMaterial();
}
void MaterialsEditor::saveMaterial()
{
MaterialSave dialog(&_material, this);
dialog.setModal(true);
if (dialog.exec() == QDialog::Accepted) {
_material.resetEditState();
refreshMaterialTree();
}
}
void MaterialsEditor::accept()
{
addRecent(_material.getUUID());
QDialog::accept();
}
void MaterialsEditor::reject()
{
QDialog::reject();
}
// QIcon MaterialsEditor::errorIcon(const QIcon &icon) const {
// auto pixmap = icon.pixmap();
// }
void MaterialsEditor::addMaterials(
QStandardItem& parent,
const std::shared_ptr<std::map<QString, Materials::MaterialTreeNode*>> modelTree,
const QIcon& folderIcon,
const QIcon& icon)
{
auto tree = ui->treeMaterials;
for (auto& mat : *modelTree) {
Materials::MaterialTreeNode* nodePtr = mat.second;
if (nodePtr->getType() == Materials::MaterialTreeNode::DataNode) {
const Materials::Material* 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 | Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled);
card->setData(QVariant(uuid), Qt::UserRole);
addExpanded(tree, &parent, card);
}
else {
auto node = new QStandardItem(folderIcon, mat.first);
addExpanded(tree, &parent, node);
node->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
auto treeMap = nodePtr->getFolder();
addMaterials(*node, treeMap, folderIcon, icon);
}
}
}
void MaterialsEditor::addExpanded(QTreeView* tree, QStandardItem* parent, QStandardItem* child)
{
parent->appendRow(child);
tree->setExpanded(child->index(), true);
}
void MaterialsEditor::addExpanded(QTreeView* tree, QStandardItemModel* parent, QStandardItem* child)
{
parent->appendRow(child);
tree->setExpanded(child->index(), true);
}
void MaterialsEditor::createPhysicalTree()
{
auto tree = ui->treePhysicalProperties;
auto model = new QStandardItemModel();
tree->setModel(model);
QStringList headers;
headers.append(QString::fromStdString("Property"));
headers.append(QString::fromStdString("Value"));
headers.append(QString::fromStdString("Type"));
model->setHorizontalHeaderLabels(headers);
tree->setColumnWidth(0, 250);
tree->setColumnWidth(1, 250);
tree->setColumnHidden(2, true);
tree->setHeaderHidden(false);
tree->setUniformRowHeights(true);
MaterialDelegate* delegate = new MaterialDelegate(this);
tree->setItemDelegateForColumn(1, delegate);
connect(delegate, &MaterialDelegate::propertyChange, this, &MaterialsEditor::propertyChange);
}
void MaterialsEditor::createPreviews()
{
_rendered = new QSvgWidget(QString::fromStdString(":/icons/preview-rendered.svg"));
_rendered->setMaximumWidth(64);
_rendered->setMinimumHeight(64);
ui->layoutAppearance->addWidget(_rendered);
_vectored = new QSvgWidget(QString::fromStdString(":/icons/preview-vector.svg"));
_vectored->setMaximumWidth(64);
_vectored->setMinimumHeight(64);
ui->layoutAppearance->addWidget(_vectored);
updatePreview();
}
void MaterialsEditor::createAppearanceTree()
{
auto tree = ui->treeAppearance;
auto model = new QStandardItemModel();
tree->setModel(model);
QStringList headers;
headers.append(QString::fromStdString("Property"));
headers.append(QString::fromStdString("Value"));
headers.append(QString::fromStdString("Type"));
model->setHorizontalHeaderLabels(headers);
tree->setColumnWidth(0, 250);
tree->setColumnWidth(1, 250);
tree->setColumnHidden(2, true);
tree->setHeaderHidden(false);
tree->setUniformRowHeights(false);
MaterialDelegate* delegate = new MaterialDelegate(this);
tree->setItemDelegateForColumn(1, delegate);
connect(delegate, &MaterialDelegate::propertyChange, this, &MaterialsEditor::propertyChange);
}
void MaterialsEditor::addRecents(QStandardItem* parent)
{
auto tree = ui->treeMaterials;
for (auto& uuid : _recents) {
try {
const Materials::Material& material = getMaterialManager().getMaterial(uuid);
QIcon icon = QIcon(material.getLibrary().getIconPath());
auto card = new QStandardItem(icon, material.getName());
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled);
card->setData(QVariant(uuid), Qt::UserRole);
addExpanded(tree, parent, card);
}
catch (const Materials::MaterialNotFound&) {
}
}
}
void MaterialsEditor::addFavorites(QStandardItem* parent)
{
auto tree = ui->treeMaterials;
for (auto& uuid : _favorites) {
try {
const Materials::Material& material = getMaterialManager().getMaterial(uuid);
QIcon icon = QIcon(material.getLibrary().getIconPath());
auto card = new QStandardItem(icon, material.getName());
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled);
card->setData(QVariant(uuid), Qt::UserRole);
addExpanded(tree, parent, card);
}
catch (const Materials::MaterialNotFound&) {
}
}
}
void MaterialsEditor::fillMaterialTree()
{
auto tree = ui->treeMaterials;
auto model = static_cast<QStandardItemModel*>(tree->model());
auto lib = new QStandardItem(QString::fromStdString("Favorites"));
lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
addExpanded(tree, model, lib);
addFavorites(lib);
lib = new QStandardItem(QString::fromStdString("Recent"));
lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
addExpanded(tree, model, lib);
addRecents(lib);
auto libraries = Materials::MaterialManager::getMaterialLibraries();
for (const auto& library : *libraries) {
lib = new QStandardItem(library->getName());
lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
addExpanded(tree, model, lib);
QIcon icon(library->getIconPath());
QIcon folderIcon(QString::fromStdString(":/icons/folder.svg"));
auto modelTree = _materialManager.getMaterialTree(*library);
addMaterials(*lib, modelTree, folderIcon, icon);
}
}
void MaterialsEditor::createMaterialTree()
{
// Materials::ModelManager &modelManager = getModelManager();
// Q_UNUSED(modelManager)
auto tree = ui->treeMaterials;
auto model = new QStandardItemModel();
tree->setModel(model);
tree->setHeaderHidden(true);
fillMaterialTree();
}
void MaterialsEditor::refreshMaterialTree()
{
auto tree = ui->treeMaterials;
auto model = static_cast<QStandardItemModel*>(tree->model());
model->clear();
fillMaterialTree();
}
void MaterialsEditor::updatePreview() const
{
QString diffuseColor;
QString highlightColor;
QString sectionColor;
if (_material.hasAppearanceProperty(QString::fromStdString("DiffuseColor"))) {
diffuseColor = _material.getAppearanceValueString(QString::fromStdString("DiffuseColor"));
}
else if (_material.hasAppearanceProperty(QString::fromStdString("ViewColor"))) {
diffuseColor = _material.getAppearanceValueString(QString::fromStdString("ViewColor"));
}
else if (_material.hasAppearanceProperty(QString::fromStdString("Color"))) {
diffuseColor = _material.getAppearanceValueString(QString::fromStdString("Color"));
}
if (_material.hasAppearanceProperty(QString::fromStdString("SpecularColor"))) {
highlightColor =
_material.getAppearanceValueString(QString::fromStdString("SpecularColor"));
}
if (_material.hasAppearanceProperty(QString::fromStdString("SectionColor"))) {
sectionColor = _material.getAppearanceValueString(QString::fromStdString("SectionColor"));
}
if ((diffuseColor.length() + highlightColor.length()) > 0) {
auto file = QFile(QString::fromStdString(":/icons/preview-rendered.svg"));
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QString svg = QTextStream(&file).readAll();
file.close();
if (diffuseColor.length() > 0) {
svg =
svg.replace(QString::fromStdString("#d3d7cf"), getColorHash(diffuseColor, 255));
svg =
svg.replace(QString::fromStdString("#555753"), getColorHash(diffuseColor, 125));
}
if (highlightColor.length() > 0) {
svg = svg.replace(QString::fromStdString("#fffffe"),
getColorHash(highlightColor, 255));
}
_rendered->load(svg.toUtf8());
}
}
if ((diffuseColor.length() + sectionColor.length()) > 0) {
auto file = QFile(QString::fromStdString(":/icons/preview-vector.svg"));
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QString svg = QTextStream(&file).readAll();
file.close();
if (diffuseColor.length() > 0) {
svg =
svg.replace(QString::fromStdString("#d3d7cf"), getColorHash(diffuseColor, 255));
svg =
svg.replace(QString::fromStdString("#555753"), getColorHash(diffuseColor, 125));
}
if (sectionColor.length() > 0) {
svg =
svg.replace(QString::fromStdString("#ffffff"), getColorHash(sectionColor, 255));
}
_vectored->load(svg.toUtf8());
}
}
}
QString MaterialsEditor::getColorHash(const QString& colorString, int colorRange) const
{
/*
returns a '#000000' string from a '(0.1,0.2,0.3)' string. Optionally the string
has a fourth value for alpha (transparency)
*/
std::stringstream stream(colorString.toStdString());
char c;
stream >> c; // read "("
double red;
stream >> red;
stream >> c; // ","
double green;
stream >> green;
stream >> c; // ","
double blue;
stream >> blue;
stream >> c; // ","
double alpha = 1.0;
if (c == ',') {
stream >> alpha;
}
QColor color(static_cast<int>(red * colorRange),
static_cast<int>(green * colorRange),
static_cast<int>(blue * colorRange),
static_cast<int>(alpha * colorRange));
return color.name();
}
void MaterialsEditor::updateMaterialAppearance()
{
QTreeView* tree = ui->treeAppearance;
QStandardItemModel* treeModel = static_cast<QStandardItemModel*>(tree->model());
treeModel->clear();
QStringList headers;
headers.append(QString::fromStdString("Property"));
headers.append(QString::fromStdString("Value"));
headers.append(QString::fromStdString("Type"));
treeModel->setHorizontalHeaderLabels(headers);
tree->setColumnWidth(0, 250);
tree->setColumnWidth(1, 250);
tree->setColumnHidden(2, true);
const std::vector<QString>* models = _material.getAppearanceModels();
if (models) {
for (auto it = models->begin(); it != models->end(); it++) {
QString uuid = *it;
try {
const Materials::Model& model = getModelManager().getModel(uuid);
QString name = model.getName();
auto modelRoot = new QStandardItem(name);
modelRoot->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled);
addExpanded(tree, treeModel, modelRoot);
for (auto itp = model.begin(); itp != model.end(); itp++) {
QList<QStandardItem*> items;
QString key = itp->first;
auto propertyItem = new QStandardItem(key);
propertyItem->setToolTip(itp->second.getDescription());
items.append(propertyItem);
auto valueItem = new QStandardItem(_material.getAppearanceValueString(key));
valueItem->setToolTip(itp->second.getDescription());
QVariant variant;
variant.setValue(&_material);
valueItem->setData(variant);
items.append(valueItem);
auto typeItem = new QStandardItem(itp->second.getPropertyType());
items.append(typeItem);
auto unitsItem = new QStandardItem(itp->second.getUnits());
items.append(unitsItem);
modelRoot->appendRow(items);
tree->setExpanded(modelRoot->index(), true);
}
}
catch (Materials::ModelNotFound const&) {
}
}
}
}
void MaterialsEditor::updateMaterialProperties()
{
QTreeView* tree = ui->treePhysicalProperties;
QStandardItemModel* treeModel = static_cast<QStandardItemModel*>(tree->model());
treeModel->clear();
QStringList headers;
headers.append(QString::fromStdString("Property"));
headers.append(QString::fromStdString("Value"));
headers.append(QString::fromStdString("Type"));
headers.append(QString::fromStdString("Units"));
treeModel->setHorizontalHeaderLabels(headers);
tree->setColumnWidth(0, 250);
tree->setColumnWidth(1, 250);
tree->setColumnHidden(2, true);
tree->setColumnHidden(3, true);
const std::vector<QString>* models = _material.getPhysicalModels();
if (models) {
for (auto it = models->begin(); it != models->end(); it++) {
QString uuid = *it;
try {
const Materials::Model& model = getModelManager().getModel(uuid);
QString name = model.getName();
auto modelRoot = new QStandardItem(name);
modelRoot->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled);
addExpanded(tree, treeModel, modelRoot);
for (auto itp = model.begin(); itp != model.end(); itp++) {
QList<QStandardItem*> items;
QString key = itp->first;
Materials::ModelProperty modelProperty =
static_cast<Materials::ModelProperty>(itp->second);
auto propertyItem = new QStandardItem(key);
propertyItem->setToolTip(modelProperty.getDescription());
items.append(propertyItem);
auto valueItem = new QStandardItem(_material.getPhysicalValueString(key));
valueItem->setToolTip(modelProperty.getDescription());
QVariant variant;
variant.setValue(&_material);
valueItem->setData(variant);
items.append(valueItem);
auto typeItem = new QStandardItem(modelProperty.getPropertyType());
items.append(typeItem);
auto unitsItem = new QStandardItem(modelProperty.getUnits());
items.append(unitsItem);
// addExpanded(tree, modelRoot, propertyItem);
modelRoot->appendRow(items);
tree->setExpanded(modelRoot->index(), true);
}
}
catch (Materials::ModelNotFound const&) {
}
}
}
}
void MaterialsEditor::updateMaterial()
{
// Update the general information
ui->editName->setText(_material.getName());
ui->editAuthorLicense->setText(_material.getAuthorAndLicense());
// ui->editParent->setText(_material.getName());
ui->editSourceURL->setText(_material.getURL());
ui->editSourceReference->setText(_material.getReference());
// ui->editTags->setText(_material.getName());
ui->editDescription->setText(_material.getDescription());
updateMaterialProperties();
updateMaterialAppearance();
updatePreview();
}
void MaterialsEditor::onSelectMaterial(const QItemSelection& selected,
const QItemSelection& deselected)
{
Q_UNUSED(deselected);
// Get the UUID before changing the underlying data model
QString uuid;
QStandardItemModel* model = static_cast<QStandardItemModel*>(ui->treeMaterials->model());
QModelIndexList indexes = selected.indexes();
for (auto it = indexes.begin(); it != indexes.end(); it++) {
QStandardItem* item = model->itemFromIndex(*it);
Base::Console().Log("%s\n", item->text().toStdString().c_str());
if (item) {
uuid = item->data(Qt::UserRole).toString();
break;
}
}
if (uuid.isEmpty() || uuid == _material.getUUID()) {
Base::Console().Log("*** Unchanged material '%s'\n", uuid.toStdString().c_str());
return;
}
// Ensure data is saved (or discarded) before changing materials
if (_material.getEditState() != Materials::Material::ModelEdit_None) {
// Prompt the user to save or discard changes
Base::Console().Log("*** Material edited!!!\n");
int res = confirmSave(this);
if (res == QMessageBox::Cancel) {
return;
}
}
// Get the selected material
try {
_material = getMaterialManager().getMaterial(uuid);
}
catch (Materials::ModelNotFound const&) {
Base::Console().Log("*** Unable to load material '%s'\n", uuid.toStdString().c_str());
Materials::Material empty;
_material = empty;
}
updateMaterial();
_material.resetEditState();
}
int MaterialsEditor::confirmSave(QWidget* parent)
{
QMessageBox box(parent ? parent : this);
box.setIcon(QMessageBox::Question);
box.setWindowTitle(QObject::tr("Unsaved Material"));
box.setText(QObject::tr("Do you want to save your changes to the material before closing?"));
box.setInformativeText(QObject::tr("If you don't save, your changes will be lost."));
box.setStandardButtons(QMessageBox::Discard | QMessageBox::Cancel | QMessageBox::Save);
box.setDefaultButton(QMessageBox::Save);
box.setEscapeButton(QMessageBox::Cancel);
// add shortcuts
QAbstractButton* saveBtn = box.button(QMessageBox::Save);
if (saveBtn->shortcut().isEmpty()) {
QString text = saveBtn->text();
text.prepend(QLatin1Char('&'));
saveBtn->setShortcut(QKeySequence::mnemonic(text));
}
QAbstractButton* discardBtn = box.button(QMessageBox::Discard);
if (discardBtn->shortcut().isEmpty()) {
QString text = discardBtn->text();
text.prepend(QLatin1Char('&'));
discardBtn->setShortcut(QKeySequence::mnemonic(text));
}
int res = QMessageBox::Cancel;
box.adjustSize(); // Silence warnings from Qt on Windows
switch (box.exec()) {
case QMessageBox::Save:
saveMaterial();
res = QMessageBox::Save;
break;
case QMessageBox::Discard:
res = QMessageBox::Discard;
break;
}
return res;
}
#include "moc_MaterialsEditor.cpp"

View File

@@ -0,0 +1,131 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATGUI_MATERIALSEDITOR_H
#define MATGUI_MATERIALSEDITOR_H
#include <boost/filesystem.hpp>
#include <QDialog>
#include <QDir>
#include <QStandardItem>
#include <QStyledItemDelegate>
#include <QSvgWidget>
#include <QTreeView>
#include <Mod/Material/App/MaterialManager.h>
#include <Mod/Material/App/Materials.h>
#include <Mod/Material/App/ModelManager.h>
namespace fs = boost::filesystem;
namespace MatGui
{
class Ui_MaterialsEditor;
class MaterialsEditor: public QDialog
{
Q_OBJECT
public:
explicit MaterialsEditor(QWidget* parent = nullptr);
~MaterialsEditor() override = default;
void propertyChange(const QString& property, const QString value);
void onFavourite(bool checked);
void onURL(bool checked);
void onPhysicalAdd(bool checked);
void onAppearanceAdd(bool checked);
void onOk(bool checked);
void onCancel(bool checked);
void onSave(bool checked);
void accept() override;
void reject() override;
Materials::MaterialManager& getMaterialManager()
{
return _materialManager;
}
Materials::ModelManager& getModelManager()
{
return _modelManager;
}
void updateMaterialAppearance();
void updateMaterialProperties();
void updateMaterial();
void onSelectMaterial(const QItemSelection& selected, const QItemSelection& deselected);
protected:
int confirmSave(QWidget* parent);
void saveMaterial();
private:
std::unique_ptr<Ui_MaterialsEditor> ui;
Materials::MaterialManager _materialManager;
Materials::ModelManager _modelManager;
Materials::Material _material;
QSvgWidget* _rendered;
QSvgWidget* _vectored;
bool _edited;
std::list<QString> _favorites;
std::list<QString> _recents;
int _recentMax;
void getFavorites();
void saveFavorites();
void addFavorite(const QString& uuid);
void removeFavorite(const QString& uuid);
bool isFavorite(const QString& uuid) const;
void getRecents();
void saveRecents();
void addRecent(const QString& uuid);
bool isRecent(const QString& uuid) const;
void updatePreview() const;
QString getColorHash(const QString& colorString, int colorRange = 255) const;
void addExpanded(QTreeView* tree, QStandardItem* parent, QStandardItem* child);
void addExpanded(QTreeView* tree, QStandardItemModel* parent, QStandardItem* child);
void addRecents(QStandardItem* parent);
void addFavorites(QStandardItem* parent);
void createPreviews();
void createAppearanceTree();
void createPhysicalTree();
void createMaterialTree();
void fillMaterialTree();
void refreshMaterialTree();
void
addMaterials(QStandardItem& parent,
const std::shared_ptr<std::map<QString, Materials::MaterialTreeNode*>> modelTree,
const QIcon& folderIcon,
const QIcon& icon);
bool isMaterial(const fs::path& p) const
{
return Materials::MaterialManager::isMaterial(p);
}
};
} // namespace MatGui
#endif // MATGUI_MATERIALSEDITOR_H

View File

@@ -0,0 +1,288 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MatGui::MaterialsEditor</class>
<widget class="QDialog" name="MatGui::MaterialsEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>835</width>
<height>542</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Materials</string>
</property>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTreeView" name="treeMaterials">
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectItems</enum>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
</widget>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tabGeneral">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<attribute name="title">
<string>General</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
<property name="leftMargin">
<number>7</number>
</property>
<property name="topMargin">
<number>7</number>
</property>
<property name="rightMargin">
<number>7</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item row="0" column="2">
<widget class="QLineEdit" name="editName"/>
</item>
<item row="7" column="0">
<widget class="QLabel" name="labelDescription">
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="editAuthorLicense"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelName">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="labelSourceReference">
<property name="text">
<string>Source Reference</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelParent">
<property name="text">
<string>Parent</string>
</property>
</widget>
</item>
<item row="7" column="2">
<widget class="QTextEdit" name="editDescription"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelAuthorLicense">
<property name="text">
<string>Author and License</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="editParent"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Tags</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="labelSourceURL">
<property name="text">
<string>Source URL</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLineEdit" name="editTags"/>
</item>
<item row="4" column="2">
<widget class="QLineEdit" name="editSourceReference"/>
</item>
<item row="3" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="editSourceURL"/>
</item>
<item>
<widget class="QPushButton" name="buttonURL">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonFavorite">
<property name="text">
<string>*</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabProperties">
<attribute name="title">
<string>Properties</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_43">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_23">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonPhysicalAdd">
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonPhysicalRemove">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTreeView" name="treePhysicalProperties"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabAppearance">
<attribute name="title">
<string>Appearance</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_42">
<item>
<layout class="QHBoxLayout" name="layoutAppearance"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonAppearanceAdd">
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonAppearanceRemove">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTreeView" name="treeAppearance"/>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="standardButtons">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,542 @@
/***************************************************************************
* 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 <QDesktopServices>
#include <QItemSelectionModel>
#include <QPushButton>
#include <QString>
#endif
#include <App/Application.h>
#include <Base/Interpreter.h>
#include <Gui/Application.h>
#include <Gui/Command.h>
#include <Gui/WaitCursor.h>
#include <Mod/Material/App/ModelManager.h>
#include "ModelSelect.h"
#include "ui_ModelSelect.h"
using namespace MatGui;
/* TRANSLATOR MatGui::ModelSelect */
ModelSelect::ModelSelect(QWidget* parent, Materials::ModelManager::ModelFilter filter)
: QDialog(parent)
, _filter(filter)
, ui(new Ui_ModelSelect)
{
ui->setupUi(this);
getFavorites();
getRecents();
createModelTree();
createModelProperties();
ui->buttonURL->setIcon(QIcon(QString::fromStdString(":/icons/internet-web-browser.svg")));
ui->buttonDOI->setIcon(QIcon(QString::fromStdString(":/icons/internet-web-browser.svg")));
connect(ui->standardButtons, &QDialogButtonBox::accepted, this, &ModelSelect::accept);
connect(ui->standardButtons, &QDialogButtonBox::rejected, this, &ModelSelect::reject);
QItemSelectionModel* selectionModel = ui->treeModels->selectionModel();
connect(selectionModel,
&QItemSelectionModel::selectionChanged,
this,
&ModelSelect::onSelectModel);
connect(ui->buttonURL, &QPushButton::clicked, this, &ModelSelect::onURL);
connect(ui->buttonDOI, &QPushButton::clicked, this, &ModelSelect::onDOI);
connect(ui->buttonFavorite, &QPushButton::clicked, this, &ModelSelect::onFavourite);
ui->standardButtons->button(QDialogButtonBox::Ok)->setEnabled(false);
ui->buttonFavorite->setEnabled(false);
}
void ModelSelect::getFavorites()
{
_favorites.clear();
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/Models/Favorites");
int count = param->GetInt("Favorites", 0);
for (int i = 0; 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);
}
}
void ModelSelect::saveFavorites()
{
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/Models/Favorites");
// Clear out the existing favorites
int count = param->GetInt("Favorites", 0);
for (int i = 0; i < count; i++) {
QString key = QString::fromLatin1("FAV%1").arg(i);
param->RemoveASCII(key.toStdString().c_str());
}
// Add the current values
param->SetInt("Favorites", _favorites.size());
int j = 0;
for (auto favorite : _favorites) {
QString key = QString::fromLatin1("FAV%1").arg(j);
param->SetASCII(key.toStdString().c_str(), favorite.toStdString());
j++;
}
}
void ModelSelect::addFavorite(const QString& uuid)
{
if (!isFavorite(uuid)) {
_favorites.push_back(uuid);
saveFavorites();
refreshModelTree();
}
}
void ModelSelect::removeFavorite(const QString& uuid)
{
if (isFavorite(uuid)) {
_favorites.remove(uuid);
saveFavorites();
refreshModelTree();
}
}
bool ModelSelect::isFavorite(const QString& uuid) const
{
for (auto it : _favorites) {
if (it == uuid) {
return true;
}
}
return false;
}
void ModelSelect::getRecents()
{
_recents.clear();
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/Models/Recent");
_recentMax = param->GetInt("RecentMax", 5);
int count = param->GetInt("Recent", 0);
for (int i = 0; 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);
}
}
void ModelSelect::saveRecents()
{
auto param = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/Models/Recent");
// Clear out the existing favorites
int count = param->GetInt("Recent", 0);
for (int i = 0; i < count; i++) {
QString key = QString::fromLatin1("MRU%1").arg(i);
param->RemoveASCII(key.toStdString().c_str());
}
// Add the current values
int size = _recents.size();
if (size > _recentMax) {
size = _recentMax;
}
param->SetInt("Recent", size);
int j = 0;
for (auto recent : _recents) {
QString key = QString::fromLatin1("MRU%1").arg(j);
param->SetASCII(key.toStdString().c_str(), recent.toStdString());
j++;
if (j >= size) {
break;
}
}
}
void ModelSelect::addRecent(const QString& uuid)
{
// Ensure no duplicates
if (isRecent(uuid)) {
_recents.remove(uuid);
}
_recents.push_front(uuid);
while (_recents.size() > static_cast<std::size_t>(_recentMax)) {
_recents.pop_back();
}
saveRecents();
}
bool ModelSelect::isRecent(const QString& uuid) const
{
for (auto it : _recents) {
if (it == uuid) {
return true;
}
}
return false;
}
/*
* Destroys the object and frees any allocated resources
*/
ModelSelect::~ModelSelect()
{
// no need to delete child widgets, Qt does it all for us
}
void ModelSelect::addExpanded(QTreeView* tree, QStandardItem* parent, QStandardItem* child)
{
parent->appendRow(child);
tree->setExpanded(child->index(), true);
}
void ModelSelect::addExpanded(QTreeView* tree, QStandardItemModel* parent, QStandardItem* child)
{
parent->appendRow(child);
tree->setExpanded(child->index(), true);
}
void ModelSelect::addModels(
QStandardItem& parent,
const std::shared_ptr<std::map<QString, Materials::ModelTreeNode*>> modelTree,
const QIcon& icon)
{
auto tree = ui->treeModels;
for (auto& mod : *modelTree) {
Materials::ModelTreeNode* nodePtr = mod.second;
if (nodePtr->getType() == Materials::ModelTreeNode::DataNode) {
const Materials::Model* model = nodePtr->getData();
QString uuid = model->getUUID();
auto card = new QStandardItem(icon, model->getName());
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled);
card->setData(QVariant(uuid), Qt::UserRole);
addExpanded(tree, &parent, card);
}
else {
auto node = new QStandardItem(mod.first);
addExpanded(tree, &parent, node);
node->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
auto treeMap = nodePtr->getFolder();
addModels(*node, treeMap, icon);
}
}
}
void ModelSelect::addRecents(QStandardItem* parent)
{
auto tree = ui->treeModels;
for (auto& uuid : _recents) {
try {
const Materials::Model& model = getModelManager().getModel(uuid);
if (getModelManager().passFilter(_filter, model.getType())) {
QIcon icon = QIcon(model.getLibrary().getIconPath());
auto card = new QStandardItem(icon, model.getName());
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled);
card->setData(QVariant(uuid), Qt::UserRole);
addExpanded(tree, parent, card);
}
}
catch (const Materials::ModelNotFound&) {
}
}
}
void ModelSelect::addFavorites(QStandardItem* parent)
{
auto tree = ui->treeModels;
for (auto& uuid : _favorites) {
try {
const Materials::Model& model = getModelManager().getModel(uuid);
if (getModelManager().passFilter(_filter, model.getType())) {
QIcon icon = QIcon(model.getLibrary().getIconPath());
auto card = new QStandardItem(icon, model.getName());
card->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled);
card->setData(QVariant(uuid), Qt::UserRole);
addExpanded(tree, parent, card);
}
}
catch (const Materials::ModelNotFound&) {
}
}
}
void ModelSelect::createModelTree()
{
// Materials::ModelManager modelManager;
auto tree = ui->treeModels;
auto model = new QStandardItemModel();
tree->setModel(model);
tree->setHeaderHidden(true);
fillTree();
}
void ModelSelect::refreshModelTree()
{
auto tree = ui->treeModels;
auto model = static_cast<QStandardItemModel*>(tree->model());
model->clear();
fillTree();
}
void ModelSelect::fillTree()
{
// Materials::ModelManager modelManager;
auto tree = ui->treeModels;
auto model = static_cast<QStandardItemModel*>(tree->model());
auto lib = new QStandardItem(QString::fromStdString("Favorites"));
lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
addExpanded(tree, model, lib);
addFavorites(lib);
lib = new QStandardItem(QString::fromStdString("Recent"));
lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
addExpanded(tree, model, lib);
addRecents(lib);
auto libraries = getModelManager().getModelLibraries();
for (auto library : *libraries) {
lib = new QStandardItem(library->getName());
lib->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
addExpanded(tree, model, lib);
auto modelTree = getModelManager().getModelTree(*library, _filter);
addModels(*lib, modelTree, QIcon(library->getIconPath()));
}
}
void ModelSelect::setHeaders(QStandardItemModel* model)
{
QStringList headers;
headers.append(QString::fromStdString("Inherited"));
headers.append(QString::fromStdString("Property"));
headers.append(QString::fromStdString("Units"));
headers.append(QString::fromStdString("Description"));
headers.append(QString::fromStdString("URL"));
model->setHorizontalHeaderLabels(headers);
}
void ModelSelect::setColumnWidths(QTableView* table)
{
table->setColumnWidth(0, 75);
table->setColumnWidth(1, 200);
table->setColumnWidth(2, 200);
table->setColumnWidth(3, 200);
table->setColumnWidth(4, 200);
}
void ModelSelect::createModelProperties()
{
auto table = ui->tableProperties;
auto model = new QStandardItemModel();
table->setModel(model);
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
setHeaders(model);
setColumnWidths(table);
// table->setHeaderHidden(false);
// table->setUniformRowHeights(true);
// table->setItemDelegate(new MaterialDelegate(this));
}
void ModelSelect::updateModelProperties(const Materials::Model& model)
{
QTableView* table = ui->tableProperties;
QStandardItemModel* tableModel = static_cast<QStandardItemModel*>(table->model());
tableModel->clear();
setHeaders(tableModel);
setColumnWidths(table);
for (auto itp = model.begin(); itp != model.end(); itp++) {
QList<QStandardItem*> items;
QString key = itp->first;
const Materials::ModelProperty modelProperty =
static_cast<const Materials::ModelProperty>(itp->second);
auto inherited =
new QStandardItem(QString::fromStdString(modelProperty.isInherited() ? "*" : ""));
// inherited->setToolTip(QString::fromStdString(modelProperty.getDescription()));
items.append(inherited);
auto propertyItem = new QStandardItem(key);
items.append(propertyItem);
auto unitsItem = new QStandardItem(modelProperty.getUnits());
items.append(unitsItem);
auto descriptionItem = new QStandardItem(modelProperty.getDescription());
items.append(descriptionItem);
auto urlItem = new QStandardItem(modelProperty.getURL());
items.append(urlItem);
// addExpanded(tree, modelRoot, propertyItem);
tableModel->appendRow(items);
}
}
void ModelSelect::updateMaterialModel(const QString& uuid)
{
Materials::Model model = getModelManager().getModel(uuid);
// Update the general information
ui->editName->setText(model.getName());
ui->editURL->setText(model.getURL());
ui->editDOI->setText(model.getDOI());
ui->editDescription->setText(model.getDescription());
if (model.getType() == Materials::Model::ModelType_Physical) {
ui->tabWidget->setTabText(1, QString::fromStdString("Properties"));
}
else {
ui->tabWidget->setTabText(1, QString::fromStdString("Appearance"));
}
updateModelProperties(model);
}
void ModelSelect::clearMaterialModel()
{
// Update the general information
ui->editName->setText(QString::fromStdString(""));
ui->editURL->setText(QString::fromStdString(""));
ui->editDOI->setText(QString::fromStdString(""));
ui->editDescription->setText(QString::fromStdString(""));
ui->tabWidget->setTabText(1, QString::fromStdString("Properties"));
QTableView* table = ui->tableProperties;
QStandardItemModel* tableModel = static_cast<QStandardItemModel*>(table->model());
tableModel->clear();
setHeaders(tableModel);
setColumnWidths(table);
}
void ModelSelect::onSelectModel(const QItemSelection& selected, const QItemSelection& deselected)
{
Q_UNUSED(deselected);
QStandardItemModel* model = static_cast<QStandardItemModel*>(ui->treeModels->model());
QModelIndexList indexes = selected.indexes();
for (auto it = indexes.begin(); it != indexes.end(); it++) {
QStandardItem* item = model->itemFromIndex(*it);
Base::Console().Log("%s\n", item->text().toStdString().c_str());
if (item) {
try {
_selected = item->data(Qt::UserRole).toString();
Base::Console().Log("\t%s\n", _selected.toStdString().c_str());
updateMaterialModel(_selected);
ui->standardButtons->button(QDialogButtonBox::Ok)->setEnabled(true);
ui->buttonFavorite->setEnabled(true);
}
catch (const std::exception& e) {
_selected = QString::fromStdString("");
Base::Console().Log("Error %s\n", e.what());
clearMaterialModel();
ui->standardButtons->button(QDialogButtonBox::Ok)->setEnabled(false);
ui->buttonFavorite->setEnabled(false);
}
}
}
}
void ModelSelect::onURL(bool checked)
{
Q_UNUSED(checked)
Base::Console().Log("URL\n");
QString url = ui->editURL->text();
if (url.length() > 0) {
QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode));
}
}
void ModelSelect::onDOI(bool checked)
{
Q_UNUSED(checked)
Base::Console().Log("DOI\n");
QString url = QString::fromStdString("https://doi.org/") + ui->editDOI->text();
if (url.length() > 0) {
QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode));
}
}
void ModelSelect::onFavourite(bool checked)
{
Q_UNUSED(checked)
Base::Console().Log("Favorite\n");
if (isFavorite(_selected)) {
removeFavorite(_selected);
}
else {
addFavorite(_selected);
}
}
void ModelSelect::accept()
{
addRecent(_selected);
QDialog::accept();
}
void ModelSelect::reject()
{
QDialog::reject();
}
#include "moc_ModelSelect.cpp"

View File

@@ -0,0 +1,108 @@
/***************************************************************************
* 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_MODELSELECT_H
#define MATGUI_MODELSELECT_H
#include <boost/filesystem.hpp>
#include <QDialog>
#include <QDir>
#include <QStandardItem>
#include <QTableView>
#include <QTreeView>
#include <Mod/Material/App/Materials.h>
namespace fs = boost::filesystem;
namespace MatGui
{
class Ui_ModelSelect;
class ModelSelect: public QDialog
{
Q_OBJECT
public:
explicit ModelSelect(
QWidget* parent = nullptr,
Materials::ModelManager::ModelFilter filter = Materials::ModelManager::ModelFilter_None);
~ModelSelect() override;
void onURL(bool checked);
void onDOI(bool checked);
void onFavourite(bool checked);
void onSelectModel(const QItemSelection& selected, const QItemSelection& deselected);
const QString& selectedModel() const
{
return _selected;
}
void accept() override;
void reject() override;
private:
void getFavorites();
void saveFavorites();
void addFavorite(const QString& uuid);
void removeFavorite(const QString& uuid);
bool isFavorite(const QString& uuid) const;
void getRecents();
void saveRecents();
void addRecent(const QString& uuid);
bool isRecent(const QString& uuid) const;
void addExpanded(QTreeView* tree, QStandardItem* parent, QStandardItem* child);
void addExpanded(QTreeView* tree, QStandardItemModel* parent, QStandardItem* child);
void addRecents(QStandardItem* parent);
void addFavorites(QStandardItem* parent);
void addModels(QStandardItem& parent,
const std::shared_ptr<std::map<QString, Materials::ModelTreeNode*>> modelTree,
const QIcon& icon);
void updateMaterialModel(const QString& uuid);
void clearMaterialModel();
void createModelTree();
void refreshModelTree();
void fillTree();
void setHeaders(QStandardItemModel* model);
void setColumnWidths(QTableView* table);
void updateModelProperties(const Materials::Model& model);
void createModelProperties();
Materials::ModelManager& getModelManager()
{
return _modelManager;
}
Materials::ModelManager::ModelFilter _filter;
std::unique_ptr<Ui_ModelSelect> ui;
Materials::ModelManager _modelManager;
QString _selected;
std::list<QString> _favorites;
std::list<QString> _recents;
int _recentMax;
};
} // namespace MatGui
#endif // MATGUI_MODELSELECT_H

View File

@@ -0,0 +1,197 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MatGui::ModelSelect</class>
<widget class="QDialog" name="MatGui::ModelSelect">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>705</width>
<height>489</height>
</rect>
</property>
<property name="windowTitle">
<string>Material Models</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTreeView" name="treeModels"/>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tabGeneral">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>365</width>
<height>409</height>
</size>
</property>
<attribute name="title">
<string>General</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="labelURL">
<property name="text">
<string>URL</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelDOI">
<property name="text">
<string>DOI</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QTextEdit" name="editDescription">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="editName">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelName">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLineEdit" name="editDOI">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonDOI">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="editURL">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonURL">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonFavorite">
<property name="toolTip">
<string>Add to favorites</string>
</property>
<property name="text">
<string>*</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabProperties">
<attribute name="title">
<string>Properties</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QTableView" name="tableProperties"/>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="standardButtons">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,22 @@
/***************************************************************************
* 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"

View File

@@ -0,0 +1,74 @@
/***************************************************************************
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* FreeCAD is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef MATGUI_PRECOMPILED_H
#define MATGUI_PRECOMPILED_H
#include <FCConfig.h>
// point at which warnings of overly long specifiers disabled (needed for VC6)
#ifdef _MSC_VER
#pragma warning(disable : 4251)
#pragma warning(disable : 4503)
#pragma warning(disable : 4786) // specifier longer then 255 chars
#pragma warning(disable : 4273)
#endif
#ifdef FC_OS_WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#endif
#ifdef _PreComp_
// standard
#include <cfloat>
#include <cmath>
// STL
#include <algorithm>
#include <map>
#include <sstream>
#include <string>
#include <vector>
// OpenCasCade
// #include <Mod/Part/App/OpenCascadeAll.h>
// Boost
#include <boost/algorithm/string/predicate.hpp>
#include <boost/regex.hpp>
// Qt Toolkit
#ifndef __QtAll__
#include <Gui/QtAll.h>
#endif
// // Inventor includes OpenGL
// #ifndef __InventorAll__
// # include <Gui/InventorAll.h>
// #endif
#endif //_PreComp_
#endif // MATGUI_PRECOMPILED_H

View File

@@ -0,0 +1,10 @@
<RCC>
<qresource>
<file>icons/MaterialWorkbench.svg</file>
<file>icons/Materials_Edit.svg</file>
<file>icons/preferences-material.svg</file>
<file>icons/preview-rendered.svg</file>
<file>icons/preview-vector.svg</file>
<file>icons/table.svg</file>
</qresource>
</RCC>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -0,0 +1,157 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64"
height="64"
id="svg2"
version="1.1"
inkscape:version="0.48.5 r10040"
sodipodi:docname="preview-vector.svg">
<defs
id="defs4">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="-206.25075 : 119.68854 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="183.50001 : 140.32143 : 1"
inkscape:persp3d-origin="48 : 32 : 1"
id="perspective3792" />
<linearGradient
id="linearGradient3755">
<stop
style="stop-color:#424242;stop-opacity:1;"
offset="0"
id="stop3757" />
<stop
style="stop-color:#9f9f9f;stop-opacity:1;"
offset="1"
id="stop3759" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3755"
id="linearGradient3761"
x1="75.724594"
y1="85.023956"
x2="41.895164"
y2="13.334851"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8.7571168"
inkscape:cx="39.859065"
inkscape:cy="30.925664"
inkscape:document-units="px"
inkscape:current-layer="g3794"
showgrid="true"
showborder="true"
inkscape:window-width="800"
inkscape:window-height="836"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="0"
inkscape:snap-bbox="false"
inkscape:snap-nodes="true"
inkscape:snap-global="true">
<inkscape:grid
type="xygrid"
id="grid3002"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:creator>
<cc:Agent>
<dc:title>$committer</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>preview-vector</dc:title>
<dc:date>2013-11-19</dc:date>
<dc:relation>http://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Material/Resources/icons/preview-vector.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-988.36218)">
<g
id="g3794"
style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.29399991;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate">
<path
style="fill:#d3d7cf;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 29.814326,991.36218 -8.814326,4 -1,7.00002 26,1 14.994091,-8.6577 z"
id="path3816"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
d="M 38.484871,1007.8171 40,1046.3622 l 21,-22.4044 0,-29.25647 z"
style="fill:#b7b7b7;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3818"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#555753;stroke:#555753;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 59.035745,9.6627807 59.108006,34.818961 40.91531,54.264958 42.698526,19.372964 z"
id="path3022"
inkscape:connector-curvature="0"
transform="translate(0,988.36218)"
sodipodi:nodetypes="ccccc" />
<path
d="m 21,995.36218 31,5.00002 -12,46 -30,-8 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3820"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
d="m 21,995.36218 31,5.00002 -12,46 -30,-8 z"
style="color:#000000;fill:none;stroke:#888a85;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path3820-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="64"
height="64"
viewBox="0 0 16.933333 16.933333"
version="1.1"
id="svg5"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2">
<linearGradient
id="linearGradient992">
<stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="0"
id="stop990" />
</linearGradient>
</defs>
<g
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:0.52916667;stroke-opacity:1;stroke-dasharray:none"
id="rect434"
width="14.684375"
height="14.122135"
x="1.1244792"
y="1.3559896" />
<path
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-dasharray:none;stroke-opacity:1"
d="M 1.1244792,4.9609349 15.643489,5.0270807"
id="path1407" />
<path
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-dasharray:none;stroke-opacity:1"
d="M 1.9182291,7.3752603 H 5.523177 v 0"
id="path2181" />
<path
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-dasharray:none;stroke-opacity:1"
d="m 6.8295573,7.3421875 h 3.6049477 v 0"
id="path2181-4" />
<path
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-dasharray:none;stroke-opacity:1"
d="m 11.426692,7.4083334 h 3.604948 v 0"
id="path2181-5" />
<path
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-dasharray:none;stroke-opacity:1"
d="m 1.9099611,12.567709 h 3.604948 v 0"
id="path2181-2" />
<path
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-dasharray:none;stroke-opacity:1"
d="m 6.8212891,12.534636 h 3.6049479 v 0"
id="path2181-4-0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-dasharray:none;stroke-opacity:1"
d="m 11.418424,12.600782 h 3.604948 v 0"
id="path2181-5-5" />
<path
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-dasharray:none;stroke-opacity:1"
d="m 1.9264977,10.021094 h 3.6049479 v 0"
id="path2181-46" />
<path
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-dasharray:none;stroke-opacity:1"
d="m 6.8378259,10.021094 h 3.6049471 v 0"
id="path2181-4-3" />
<path
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-dasharray:none;stroke-opacity:1"
d="m 11.43496,10.054167 h 3.604948 v 0"
id="path2181-5-7" />
<path
style="fill:#0000ff;stroke:#000000;stroke-width:0.25;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1"
d="M 29.5,17.805701 C 20.7,17.760754 11.657813,17.715771 9.40625,17.705739 L 5.3125,17.6875 v -5.75 -5.75 H 32 58.6875 v 5.875 5.875 L 52.09375,17.91246 C 48.467188,17.89869 38.3,17.850648 29.5,17.805701 Z"
id="path2364"
transform="scale(0.26458333)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1">
<context>
<name>MaterialEditor</name>
<message>
<location filename="../../materials-editor.ui" line="14"/>
<source>Material Editor</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../materials-editor.ui" line="22"/>
<source>Material card</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../materials-editor.ui" line="40"/>
<source>Opens the Product URL of this material in an external browser</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../materials-editor.ui" line="56"/>
<source>Existing material cards</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../materials-editor.ui" line="67"/>
<source>Opens an existing material card</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../materials-editor.ui" line="70"/>
<source>Open...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../materials-editor.ui" line="77"/>
<source>Saves this material as a card</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../materials-editor.ui" line="80"/>
<source>Save as...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../materials-editor.ui" line="96"/>
<source>Material parameter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../materials-editor.ui" line="115"/>
<source>Add / remove parameter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../materials-editor.ui" line="126"/>
<source>Add property</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../materials-editor.ui" line="133"/>
<source>Delete property</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Material</name>
<message>
<location filename="../../MaterialEditor.py" line="275"/>
<source>Product URL</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../MaterialEditor.py" line="337"/>
<source>UserDefined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../MaterialEditor.py" line="583"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

View File

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

View File

@@ -0,0 +1,47 @@
/***************************************************************************
* 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_WORKBENCH_H
#define MATGUI_WORKBENCH_H
#include <Gui/Workbench.h>
#include <Mod/Material/MaterialGlobal.h>
namespace MatGui
{
/**
* @author David Carter
*/
class MatGuiExport Workbench: public Gui::StdWorkbench
{
TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
Workbench();
~Workbench() override;
};
} // namespace MatGui
#endif // MATGUI_WORKBENCH_H

View File

@@ -23,3 +23,5 @@ import FreeCAD
# import for the FreeCAD Material card
FreeCAD.addImportType("FreeCAD Material Card (*.FCMat)", "importFCMat")
FreeCAD.__unit_test__ += [ "TestMaterialsApp" ]

View File

@@ -18,4 +18,30 @@
#* USA *
#* *
#***************************************************************************
"""Initialization of the Material Workbench graphical interface."""
import FreeCAD as App
import FreeCADGui as Gui
import os
class MaterialWorkbench(Gui.Workbench):
"""Part workbench object."""
def __init__(self):
self.__class__.Icon = os.path.join(App.getResourceDir(),
"Mod", "Material",
"Resources", "icons",
"MaterialWorkbench.svg")
self.__class__.MenuText = "Material"
self.__class__.ToolTip = "Material workbench"
def Initialize(self):
# load the module
import MatGui
def GetClassName(self):
return "MatGui::Workbench"
Gui.addWorkbench(MaterialWorkbench())

View File

@@ -1,135 +0,0 @@
# ***************************************************************************
# * Copyright (c) 2013-2015 Juergen Riegel <FreeCAD@juergen-riegel.net> *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program 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 program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
import sys
import FreeCAD
# The usage description if you use this tool from the command line ("__main__")
CommandlineUsage = """Material - Tool to work with FreeCAD Material definition cards
Usage:
Material [Options] card-file-name
Options:
-c, --output-csv=filename write a comma separated grid with the material data
Exit:
0 No Error or Warning found
1 Argument error, wrong or too few Arguments given
Tool to work with FreeCAD Material definition cards
Examples:
Material "StandardMaterial/Steel.FCMat"
Author:
(c) 2013 Juergen Riegel
mail@juergen-riegel.net
Licence: LGPL
Version:
0.1
"""
# see comments in module importFCMat, there is an independent parser implementation
# for reading and writing FCMat files
# inside FreeCAD mostly the one from importFCMat.py is used
def importFCMat(fileName):
"Read a FCMat file into a dictionary"
try:
import ConfigParser as configparser
except ImportError:
import configparser
FreeCAD.Console.PrintError(
'This mat card reader is probably deprecated and not widely used in FreeCAD. '
'See comment in Material.py module.\n'
)
Config = configparser.RawConfigParser()
Config.optionxform = str
Config.read(fileName, encoding='utf-8') # respect unicode filenames
dict1 = {}
for section in Config.sections():
options = Config.options(section)
for option in options:
dict1[option] = Config.get(section, option)
return dict1
def exportFCMat(fileName, matDict):
"Write a material dictionary to a FCMat file"
try:
import ConfigParser as configparser
except ImportError:
import configparser
import string
Config = configparser.RawConfigParser()
FreeCAD.Console.PrintError(
'This mat card writer is probably deprecated and not widely used in FreeCAD. '
'See comment in Material.py module.\n'
)
# create groups
for x in matDict.keys():
grp, key = string.split(x, sep='_')
if not Config.has_section(grp):
Config.add_section(grp)
# fill groups
for x in matDict.keys():
grp, key = string.split(x, sep='_')
Config.set(grp, key, matDict[x])
Preamble = "# This is a FreeCAD material-card file\n\n"
# Writing our configuration file to 'example.cfg'
with open(fileName, 'wb') as configfile:
configfile.write(Preamble)
Config.write(configfile)
if __name__ == '__main__':
import getopt
try:
opts, args = getopt.getopt(sys.argv[1:], "c:", ["output-csv="])
except getopt.GetoptError:
# print help information and exit:
sys.stderr.write(CommandlineUsage)
sys.exit(1)
# checking on the options
for o, a in opts:
if o in ("-c", "--output-csv"):
print("writing file: " + a + "\n")
OutPath = a
# running through the files
FileName = args[0]
kv_map = importFCMat(FileName)
for k in kv_map.keys():
print(repr(k) + " : " + repr(kv_map[k]))
sys.exit(0) # no error

View File

@@ -25,12 +25,15 @@ __author__ = "Yorik van Havre, Bernd Hahnebach"
__url__ = "http://www.freecad.org"
import os
from pathlib import PurePath
import sys
from PySide import QtCore, QtGui, QtSvg
import FreeCAD
import FreeCADGui
# import Material_rc
from materialtools.cardutils import get_material_preferred_directory, get_material_preferred_save_directory
# is this still needed after the move to card utils???
unicode = str
@@ -52,17 +55,21 @@ class MaterialEditor:
self.customprops = []
self.internalprops = []
self.groups = []
self.directory = FreeCAD.getResourceDir() + "Mod/Material"
self.directory = get_material_preferred_directory()
self.save_directory = get_material_preferred_save_directory()
if self.directory is None:
self.directory = FreeCAD.getResourceDir() + "Mod/Material"
self.materials = {}
self.cards = {}
self.icons = {}
self.initialIndex = -1
self.edited = False
self.card_path = card_path
filePath = os.path.dirname(__file__) + os.sep
self.iconPath = (filePath + "Resources" + os.sep + "icons" + os.sep)
# load the UI file from the same directory as this script
self.widget = FreeCADGui.PySideUic.loadUi(filePath + "materials-editor.ui")
self.widget = FreeCADGui.PySideUic.loadUi(filePath + "Resources" + os.sep + "ui" + os.sep + "materials-editor.ui")
# remove unused Help button
self.widget.setWindowFlags(self.widget.windowFlags()
& ~QtCore.Qt.WindowContextHelpButtonHint)
@@ -104,7 +111,7 @@ class MaterialEditor:
# currently closes the dialog
standardButtons.rejected.connect(self.reject)
standardButtons.accepted.connect(self.accept)
standardButtons.button(QtGui.QDialogButtonBox.Ok).clicked.connect(self.verify)
buttonOpen.clicked.connect(self.openfile)
buttonSave.clicked.connect(self.savefile)
buttonURL.clicked.connect(self.openProductURL)
@@ -117,6 +124,7 @@ class MaterialEditor:
treeView.setModel(model)
treeView.setUniformRowHeights(True)
treeView.setItemDelegate(MaterialsDelegate())
model.itemChanged.connect(self.modelChange)
# init model
self.implementModel()
@@ -181,6 +189,7 @@ class MaterialEditor:
# top.sortChildren(0)
# treeView.expandAll()
self.edited = False
def updateMatParamsInTree(self, data):
@@ -220,10 +229,20 @@ class MaterialEditor:
it = QtGui.QStandardItem(i)
userGroup.appendRow([item, it])
self.customprops.append(k)
self.edited = False
def chooseMaterial(self, index):
if index < 0:
return
if self.verifyMaterial():
"""
Save any unchanged data
"""
self.edited = False
else:
return
self.card_path = self.widget.ComboMaterial.itemData(index)
FreeCAD.Console.PrintMessage(
"choose_material in material editor:\n"
@@ -239,6 +258,21 @@ class MaterialEditor:
else:
FreeCAD.Console.PrintError("Material card not found: {}\n".format(self.card_path))
def verifyMaterial(self):
if self.edited:
reply = QtGui.QMessageBox.question(self.widget, #FreeCADGui.getMainWindow(),
translate("Material","The document has been modified."),
translate("Material","Do you want to save your changes?"),
QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard | QtGui.QMessageBox.Cancel,
QtGui.QMessageBox.Save)
if reply == QtGui.QMessageBox.Cancel:
return False
if reply == QtGui.QMessageBox.Save:
self.savefile()
return True
def updateCardsInCombo(self):
"""updates the contents of the materials combo with existing material cards"""
@@ -280,6 +314,32 @@ class MaterialEditor:
if url:
QtGui.QDesktopServices.openUrl(QtCore.QUrl(url, QtCore.QUrl.TolerantMode))
def modelChange(self, item):
"""
Called when an item in the tree is modified. This will set edited to True, but this
will be reset in the event of mass updates, such as loading a card
"""
self.edited = True
def verify(self, button):
"""
Verify that the user wants to save any changed data before exiting
"""
if self.edited:
reply = QtGui.QMessageBox.question(self.widget, #FreeCADGui.getMainWindow(),
translate("Material","The document has been modified."),
translate("Material","Do you want to save your changes?"),
QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard | QtGui.QMessageBox.Cancel,
QtGui.QMessageBox.Save)
if reply == QtGui.QMessageBox.Cancel:
return
if reply == QtGui.QMessageBox.Save:
self.savefile()
self.accept()
def accept(self):
""
@@ -340,6 +400,7 @@ class MaterialEditor:
it = QtGui.QStandardItem(value)
top.appendRow([item, it])
self.customprops.append(key)
self.edited = True
def deleteCustomProperty(self, key=None):
@@ -376,6 +437,8 @@ class MaterialEditor:
it.setText("")
buttonDeleteProperty.setProperty("text", "Delete value")
self.edited = True
buttonDeleteProperty.setEnabled(False)
def onClickTree(self, index):
@@ -505,12 +568,20 @@ class MaterialEditor:
return color.name()
def openfile(self):
if self.verifyMaterial():
"""
Save any unchanged data
"""
self.edited = False
else:
return
"Opens a FCMat file"
if self.category == "Solid":
directory = self.directory + os.sep + "StandardMaterial"
else:
directory = self.directory + os.sep + "FluidMaterial"
if self.card_path is None:
if self.card_path is None or len(self.card_path) == 0:
self.card_path = directory
filetuple = QtGui.QFileDialog.getOpenFileName(
QtGui.QApplication.activeWindow(),
@@ -529,8 +600,11 @@ class MaterialEditor:
# D:/FreeCAD-build/data/Mod\Material\FluidMaterial\Air.FCMat
# To keep it simple, we take a path from the ComboMaterial and change only the
# material card filename
if self.initialIndex > -1:
path = self.widget.ComboMaterial.itemData(self.initialIndex)
#
# Using the initialIndex variable won't work before a card os selected for the
# first time, so use index 1. Index 0 is a blank entry
if self.widget.ComboMaterial.count() > 1:
path = self.widget.ComboMaterial.itemData(1)
# at first check if we have a uniform usage
# (if a character is not present, rsplit delivers the initial string)
testBackslash = path.rsplit('\\', 1)[0]
@@ -590,19 +664,27 @@ class MaterialEditor:
filetuple = QtGui.QFileDialog.getSaveFileName(
QtGui.QApplication.activeWindow(),
"Save FreeCAD Material file",
self.directory + "/" + name + ".FCMat",
self.save_directory + "/" + name + ".FCMat",
"*.FCMat"
)
# a tuple of two empty strings returns True, so use the filename directly
filename = filetuple[0]
if filename:
self.directory = os.path.dirname(filename)
# should not be resource dir but user result dir instead
# Update the directories to the current save value
self.save_directory = os.path.dirname(filename)
self.directory = self.save_directory
self.card_path = filename
d = self.getDict()
# self.outputDict(d)
if d:
# Set the card name to match the filename
path = PurePath(filename)
d["CardName"] = path.stem
from importFCMat import write
write(filename, d)
self.edited = False
self.updateCardsInCombo()
def show(self):
@@ -854,8 +936,13 @@ def translate(context, text):
def openEditor(obj=None, prop=None):
"""openEditor([obj,prop]): opens the editor, optionally with
an object name and material property name to edit"""
editor = MaterialEditor(obj, prop)
editor.exec_()
param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material/Cards")
legacy = param.GetBool("LegacyEditor", True)
if legacy:
editor = MaterialEditor(obj, prop)
editor.exec_()
else:
FreeCADGui.runCommand('Materials_Edit',0)
def editMaterial(material=None, card_path=None, category="Solid"):

View File

@@ -0,0 +1,45 @@
/***************************************************************************
* 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 <FCGlobal.h>
#ifndef MATERIAL_GLOBAL_H
#define MATERIAL_GLOBAL_H
// Material
#ifndef MaterialsExport
#ifdef Material_EXPORTS
#define MaterialsExport FREECAD_DECL_EXPORT
#else
#define MaterialsExport FREECAD_DECL_IMPORT
#endif
#endif
// MatGui
#ifndef MatGuiExport
#ifdef MatGui_EXPORTS
#define MatGuiExport FREECAD_DECL_EXPORT
#else
#define MatGuiExport FREECAD_DECL_IMPORT
#endif
#endif
#endif // MATERIAL_GLOBAL_H

View File

@@ -2,5 +2,6 @@
<qresource>
<file>icons/preview-rendered.svg</file>
<file>icons/preview-vector.svg</file>
<file>ui/materials-editor.ui</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,15 @@
# File created by ConvertFCMat.py
General:
UUID: "d1f317f0-5ffa-4798-8ab3-af2ff0b5182c"
AuthorAndLicense: "(c) 2023 David Carter"
Name: "Aluminum"
Description: "Defines the Aluminum appearance properties"
AppearanceModels:
BasicRendering:
UUID: 'f006c7e4-35b7-43d5-bbf9-c5d572309e6e'
AmbientColor: "(0.3000, 0.3000, 0.3000, 1.0)"
DiffuseColor: "(0.3000, 0.3000, 0.3000, 1.0)"
EmissiveColor: "(0.0000, 0.0000, 0.0000, 1.0)"
Shininess: "0.0900"
SpecularColor: "(0.3000, 0.3000, 0.3000, 1.0)"
Transparency: "0.0"

Some files were not shown because too many files have changed in this diff Show More