1
/***************************************************************************
2
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
4
* This file is part of FreeCAD. *
6
* FreeCAD is free software: you can redistribute it and/or modify it *
7
* under the terms of the GNU Lesser General Public License as *
8
* published by the Free Software Foundation, either version 2.1 of the *
9
* License, or (at your option) any later version. *
11
* FreeCAD is distributed in the hope that it will be useful, but *
12
* WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14
* Lesser General Public License for more details. *
16
* You should have received a copy of the GNU Lesser General Public *
17
* License along with FreeCAD. If not, see *
18
* <https://www.gnu.org/licenses/>. *
20
**************************************************************************/
22
#include "PreCompiled.h"
25
#include <QDirIterator>
29
#include <App/Application.h>
30
#include <Base/Interpreter.h>
34
#include "ModelLoader.h"
35
#include "ModelManager.h"
38
using namespace Materials;
40
ModelEntry::ModelEntry(const std::shared_ptr<ModelLibrary>& library,
41
const QString& baseName,
42
const QString& modelName,
44
const QString& modelUuid,
45
const YAML::Node& modelData)
52
, _dereferenced(false)
55
std::unique_ptr<std::map<QString, std::shared_ptr<ModelEntry>>> ModelLoader::_modelEntryMap =
58
ModelLoader::ModelLoader(std::shared_ptr<std::map<QString, std::shared_ptr<Model>>> modelMap,
59
std::shared_ptr<std::list<std::shared_ptr<ModelLibrary>>> libraryList)
61
, _libraryList(libraryList)
66
void ModelLoader::addLibrary(std::shared_ptr<ModelLibrary> model)
68
_libraryList->push_back(model);
71
const QString ModelLoader::getUUIDFromPath(const QString& path)
75
throw ModelNotFound();
79
YAML::Node yamlroot = YAML::LoadFile(path.toStdString());
80
std::string base = "Model";
81
if (yamlroot["AppearanceModel"]) {
82
base = "AppearanceModel";
85
const QString uuid = QString::fromStdString(yamlroot[base]["UUID"].as<std::string>());
88
catch (YAML::Exception&) {
89
throw ModelNotFound();
93
std::shared_ptr<ModelEntry> ModelLoader::getModelFromPath(std::shared_ptr<ModelLibrary> library,
94
const QString& path) const
98
throw ModelNotFound();
102
std::string base = "Model";
106
yamlroot = YAML::LoadFile(path.toStdString());
107
if (yamlroot["AppearanceModel"]) {
108
base = "AppearanceModel";
111
uuid = yamlroot[base]["UUID"].as<std::string>();
112
name = yamlroot[base]["Name"].as<std::string>();
114
catch (YAML::Exception const&) {
115
throw InvalidModel();
118
std::shared_ptr<ModelEntry> model = std::make_shared<ModelEntry>(library,
119
QString::fromStdString(base),
120
QString::fromStdString(name),
122
QString::fromStdString(uuid),
128
void ModelLoader::showYaml(const YAML::Node& yaml) const
130
std::stringstream out;
133
std::string logData = out.str();
134
Base::Console().Log("%s\n", logData.c_str());
137
void ModelLoader::dereference(const QString& uuid,
138
std::shared_ptr<ModelEntry> parent,
139
std::shared_ptr<ModelEntry> child,
140
std::map<std::pair<QString, QString>, QString>* inheritances)
142
auto parentPtr = parent->getModelPtr();
143
auto parentBase = parent->getBase().toStdString();
144
auto childYaml = child->getModel();
145
auto childBase = child->getBase().toStdString();
147
std::set<QString> exclude;
148
exclude.insert(QString::fromStdString("Name"));
149
exclude.insert(QString::fromStdString("UUID"));
150
exclude.insert(QString::fromStdString("URL"));
151
exclude.insert(QString::fromStdString("Description"));
152
exclude.insert(QString::fromStdString("DOI"));
153
exclude.insert(QString::fromStdString("Inherits"));
155
auto parentProperties = (*parentPtr)[parentBase];
156
auto childProperties = childYaml[childBase];
157
for (auto it = childProperties.begin(); it != childProperties.end(); it++) {
158
std::string name = it->first.as<std::string>();
159
if (exclude.count(QString::fromStdString(name)) == 0) {
160
// showYaml(it->second);
161
if (!parentProperties[name]) {
162
parentProperties[name] = it->second;
163
// parentProperties[name]["Inherits"] = childYaml[childBase]["UUID"];
164
(*inheritances)[std::pair<QString, QString>(uuid, QString::fromStdString(name))] =
165
yamlValue(childYaml[childBase], "UUID", "");
169
// showYaml(*parentPtr);
173
void ModelLoader::dereference(std::shared_ptr<ModelEntry> model,
174
std::map<std::pair<QString, QString>, QString>* inheritances)
177
if (model->getDereferenced()) {
181
auto yamlModel = model->getModel();
182
auto base = model->getBase().toStdString();
183
if (yamlModel[base]["Inherits"]) {
184
auto inherits = yamlModel[base]["Inherits"];
185
for (auto it = inherits.begin(); it != inherits.end(); it++) {
186
QString nodeName = QString::fromStdString((*it)["UUID"].as<std::string>());
188
// This requires that all models have already been loaded undereferenced
190
std::shared_ptr<ModelEntry> child = (*_modelEntryMap)[nodeName];
191
dereference(model->getUUID(), model, child, inheritances);
193
catch (const std::out_of_range&) {
194
Base::Console().Log("Unable to find '%s' in model map\n",
195
nodeName.toStdString().c_str());
200
model->markDereferenced();
203
QString ModelLoader::yamlValue(const YAML::Node& node,
204
const std::string& key,
205
const std::string& defaultValue)
208
return QString::fromStdString(node[key].as<std::string>());
210
return QString::fromStdString(defaultValue);
213
void ModelLoader::addToTree(std::shared_ptr<ModelEntry> model,
214
std::map<std::pair<QString, QString>, QString>* inheritances)
216
std::set<QString> exclude;
217
exclude.insert(QString::fromStdString("Name"));
218
exclude.insert(QString::fromStdString("UUID"));
219
exclude.insert(QString::fromStdString("URL"));
220
exclude.insert(QString::fromStdString("Description"));
221
exclude.insert(QString::fromStdString("DOI"));
222
exclude.insert(QString::fromStdString("Inherits"));
224
auto yamlModel = model->getModel();
225
auto library = model->getLibrary();
226
auto base = model->getBase().toStdString();
227
auto name = model->getName();
228
auto directory = model->getDirectory();
229
auto uuid = model->getUUID();
231
QString description = yamlValue(yamlModel[base], "Description", "");
232
QString url = yamlValue(yamlModel[base], "URL", "");
233
QString doi = yamlValue(yamlModel[base], "DOI", "");
235
Model::ModelType type =
236
(base == "Model") ? Model::ModelType_Physical : Model::ModelType_Appearance;
238
Model finalModel(library, type, name, directory, uuid, description, url, doi);
240
// Add inheritance list
241
if (yamlModel[base]["Inherits"]) {
242
auto inherits = yamlModel[base]["Inherits"];
243
for (auto it = inherits.begin(); it != inherits.end(); it++) {
244
QString nodeName = QString::fromStdString((*it)["UUID"].as<std::string>());
246
finalModel.addInheritance(nodeName);
251
auto yamlProperties = yamlModel[base];
252
for (auto it = yamlProperties.begin(); it != yamlProperties.end(); it++) {
253
std::string propName = it->first.as<std::string>();
254
if (exclude.count(QString::fromStdString(propName)) == 0) {
255
// showYaml(it->second);
256
auto yamlProp = yamlProperties[propName];
257
auto propDisplayName = yamlValue(yamlProp, "DisplayName", "");
258
auto propType = yamlValue(yamlProp, "Type", "");
259
auto propUnits = yamlValue(yamlProp, "Units", "");
260
auto propURL = yamlValue(yamlProp, "URL", "");
261
auto propDescription = yamlValue(yamlProp, "Description", "");
262
// auto inherits = yamlValue(yamlProp, "Inherits", "");
264
ModelProperty property(QString::fromStdString(propName),
271
if (propType == QString::fromStdString("2DArray")
272
|| propType == QString::fromStdString("3DArray")) {
273
// Base::Console().Log("Reading columns\n");
275
auto cols = yamlProp["Columns"];
276
for (const auto& col : cols) {
277
std::string colName = col.first.as<std::string>();
278
// Base::Console().Log("\tColumns '%s'\n", colName.c_str());
280
auto colProp = cols[colName];
281
auto colPropDisplayName = yamlValue(colProp, "DisplayName", "");
282
auto colPropType = yamlValue(colProp, "Type", "");
283
auto colPropUnits = yamlValue(colProp, "Units", "");
284
auto colPropURL = yamlValue(colProp, "URL", "");
285
auto colPropDescription = yamlValue(colProp, "Description", "");
286
ModelProperty colProperty(QString::fromStdString(colName),
293
property.addColumn(colProperty);
297
auto key = std::pair<QString, QString>(uuid, QString::fromStdString(propName));
298
if (inheritances->count(key) > 0) {
299
property.setInheritance((*inheritances)[key]);
302
finalModel.addProperty(property);
306
(*_modelMap)[uuid] = library->addModel(finalModel, directory);
309
void ModelLoader::loadLibrary(std::shared_ptr<ModelLibrary> library)
311
if (_modelEntryMap == nullptr) {
312
_modelEntryMap = std::make_unique<std::map<QString, std::shared_ptr<ModelEntry>>>();
315
QDirIterator it(library->getDirectory(), QDirIterator::Subdirectories);
316
while (it.hasNext()) {
317
auto pathname = it.next();
318
QFileInfo file(pathname);
320
if (file.suffix().toStdString() == "yml") {
322
auto model = getModelFromPath(library, file.canonicalFilePath());
323
(*_modelEntryMap)[model->getUUID()] = model;
324
// showYaml(model->getModel());
326
catch (InvalidModel const&) {
327
Base::Console().Log("Invalid model '%s'\n", pathname.toStdString().c_str());
333
std::map<std::pair<QString, QString>, QString> inheritances;
334
for (auto it = _modelEntryMap->begin(); it != _modelEntryMap->end(); it++) {
335
dereference(it->second, &inheritances);
338
for (auto it = _modelEntryMap->begin(); it != _modelEntryMap->end(); it++) {
339
addToTree(it->second, &inheritances);
343
void ModelLoader::loadLibraries()
347
for (auto it = _libraryList->begin(); it != _libraryList->end(); it++) {
353
void ModelLoader::getModelLibraries()
355
auto param = App::GetApplication().GetParameterGroupByPath(
356
"User parameter:BaseApp/Preferences/Mod/Material/Resources");
357
bool useBuiltInMaterials = param->GetBool("UseBuiltInMaterials", true);
358
bool useMatFromModules = param->GetBool("UseMaterialsFromWorkbenches", true);
359
bool useMatFromConfigDir = param->GetBool("UseMaterialsFromConfigDir", true);
360
bool useMatFromCustomDir = param->GetBool("UseMaterialsFromCustomDir", true);
362
if (useBuiltInMaterials) {
363
QString resourceDir = QString::fromStdString(App::Application::getResourceDir()
364
+ "/Mod/Material/Resources/Models");
366
std::make_shared<ModelLibrary>(QString::fromStdString("System"),
368
QString::fromStdString(":/icons/freecad.svg"));
369
_libraryList->push_back(libData);
372
if (useMatFromModules) {
373
auto moduleParam = App::GetApplication().GetParameterGroupByPath(
374
"User parameter:BaseApp/Preferences/Mod/Material/Resources/Modules");
375
for (auto& group : moduleParam->GetGroups()) {
376
// auto module = moduleParam->GetGroup(group->GetGroupName());
377
auto moduleName = QString::fromStdString(group->GetGroupName());
378
auto modelDir = QString::fromStdString(group->GetASCII("ModuleModelDir", ""));
379
auto modelIcon = QString::fromStdString(group->GetASCII("ModuleIcon", ""));
381
if (modelDir.length() > 0) {
384
auto libData = std::make_shared<ModelLibrary>(moduleName, modelDir, modelIcon);
385
_libraryList->push_back(libData);
391
if (useMatFromConfigDir) {
392
QString resourceDir =
393
QString::fromStdString(App::Application::getUserAppDataDir() + "/Models");
394
if (!resourceDir.isEmpty()) {
395
QDir materialDir(resourceDir);
396
if (materialDir.exists()) {
397
auto libData = std::make_shared<ModelLibrary>(
398
QString::fromStdString("User"),
400
QString::fromStdString(":/icons/preferences-general.svg"));
401
_libraryList->push_back(libData);
406
if (useMatFromCustomDir) {
407
QString resourceDir = QString::fromStdString(param->GetASCII("CustomMaterialsDir", ""));
408
if (!resourceDir.isEmpty()) {
409
QDir materialDir(resourceDir);
410
if (materialDir.exists()) {
412
std::make_shared<ModelLibrary>(QString::fromStdString("Custom"),
414
QString::fromStdString(":/icons/user.svg"));
415
_libraryList->push_back(libData);