FreeCAD

Форк
0
/
ModelLoader.cpp 
419 строк · 16.2 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2023 David Carter <dcarter@david.carter.ca>             *
3
 *                                                                         *
4
 *   This file is part of FreeCAD.                                         *
5
 *                                                                         *
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.                       *
10
 *                                                                         *
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.                       *
15
 *                                                                         *
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/>.                                      *
19
 *                                                                         *
20
 **************************************************************************/
21

22
#include "PreCompiled.h"
23
#ifndef _PreComp_
24
#include <QString>
25
#include <QDirIterator>
26
#include <QFileInfo>
27
#endif
28

29
#include <App/Application.h>
30
#include <Base/Interpreter.h>
31

32

33
#include "Model.h"
34
#include "ModelLoader.h"
35
#include "ModelManager.h"
36

37

38
using namespace Materials;
39

40
ModelEntry::ModelEntry(const std::shared_ptr<ModelLibrary>& library,
41
                       const QString& baseName,
42
                       const QString& modelName,
43
                       const QString& dir,
44
                       const QString& modelUuid,
45
                       const YAML::Node& modelData)
46
    : _library(library)
47
    , _base(baseName)
48
    , _name(modelName)
49
    , _directory(dir)
50
    , _uuid(modelUuid)
51
    , _model(modelData)
52
    , _dereferenced(false)
53
{}
54

55
std::unique_ptr<std::map<QString, std::shared_ptr<ModelEntry>>> ModelLoader::_modelEntryMap =
56
    nullptr;
57

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)
60
    : _modelMap(modelMap)
61
    , _libraryList(libraryList)
62
{
63
    loadLibraries();
64
}
65

66
void ModelLoader::addLibrary(std::shared_ptr<ModelLibrary> model)
67
{
68
    _libraryList->push_back(model);
69
}
70

71
const QString ModelLoader::getUUIDFromPath(const QString& path)
72
{
73
    QFile file(path);
74
    if (!file.exists()) {
75
        throw ModelNotFound();
76
    }
77

78
    try {
79
        YAML::Node yamlroot = YAML::LoadFile(path.toStdString());
80
        std::string base = "Model";
81
        if (yamlroot["AppearanceModel"]) {
82
            base = "AppearanceModel";
83
        }
84

85
        const QString uuid = QString::fromStdString(yamlroot[base]["UUID"].as<std::string>());
86
        return uuid;
87
    }
88
    catch (YAML::Exception&) {
89
        throw ModelNotFound();
90
    }
91
}
92

93
std::shared_ptr<ModelEntry> ModelLoader::getModelFromPath(std::shared_ptr<ModelLibrary> library,
94
                                                          const QString& path) const
95
{
96
    QFile file(path);
97
    if (!file.exists()) {
98
        throw ModelNotFound();
99
    }
100

101
    YAML::Node yamlroot;
102
    std::string base = "Model";
103
    std::string uuid;
104
    std::string name;
105
    try {
106
        yamlroot = YAML::LoadFile(path.toStdString());
107
        if (yamlroot["AppearanceModel"]) {
108
            base = "AppearanceModel";
109
        }
110

111
        uuid = yamlroot[base]["UUID"].as<std::string>();
112
        name = yamlroot[base]["Name"].as<std::string>();
113
    }
114
    catch (YAML::Exception const&) {
115
        throw InvalidModel();
116
    }
117

118
    std::shared_ptr<ModelEntry> model = std::make_shared<ModelEntry>(library,
119
                                                                     QString::fromStdString(base),
120
                                                                     QString::fromStdString(name),
121
                                                                     path,
122
                                                                     QString::fromStdString(uuid),
123
                                                                     yamlroot);
124

125
    return model;
126
}
127

128
void ModelLoader::showYaml(const YAML::Node& yaml) const
129
{
130
    std::stringstream out;
131

132
    out << yaml;
133
    std::string logData = out.str();
134
    Base::Console().Log("%s\n", logData.c_str());
135
}
136

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)
141
{
142
    auto parentPtr = parent->getModelPtr();
143
    auto parentBase = parent->getBase().toStdString();
144
    auto childYaml = child->getModel();
145
    auto childBase = child->getBase().toStdString();
146

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"));
154

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", "");
166
            }
167
        }
168
    }
169
    // showYaml(*parentPtr);
170
}
171

172

173
void ModelLoader::dereference(std::shared_ptr<ModelEntry> model,
174
                              std::map<std::pair<QString, QString>, QString>* inheritances)
175
{
176
    // Avoid recursion
177
    if (model->getDereferenced()) {
178
        return;
179
    }
180

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

188
            // This requires that all models have already been loaded undereferenced
189
            try {
190
                std::shared_ptr<ModelEntry> child = (*_modelEntryMap)[nodeName];
191
                dereference(model->getUUID(), model, child, inheritances);
192
            }
193
            catch (const std::out_of_range&) {
194
                Base::Console().Log("Unable to find '%s' in model map\n",
195
                                    nodeName.toStdString().c_str());
196
            }
197
        }
198
    }
199

200
    model->markDereferenced();
201
}
202

203
QString ModelLoader::yamlValue(const YAML::Node& node,
204
                               const std::string& key,
205
                               const std::string& defaultValue)
206
{
207
    if (node[key]) {
208
        return QString::fromStdString(node[key].as<std::string>());
209
    }
210
    return QString::fromStdString(defaultValue);
211
}
212

213
void ModelLoader::addToTree(std::shared_ptr<ModelEntry> model,
214
                            std::map<std::pair<QString, QString>, QString>* inheritances)
215
{
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"));
223

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

231
    QString description = yamlValue(yamlModel[base], "Description", "");
232
    QString url = yamlValue(yamlModel[base], "URL", "");
233
    QString doi = yamlValue(yamlModel[base], "DOI", "");
234

235
    Model::ModelType type =
236
        (base == "Model") ? Model::ModelType_Physical : Model::ModelType_Appearance;
237

238
    Model finalModel(library, type, name, directory, uuid, description, url, doi);
239

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

246
            finalModel.addInheritance(nodeName);
247
        }
248
    }
249

250
    // Add property list
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", "");
263

264
            ModelProperty property(QString::fromStdString(propName),
265
                                   propDisplayName,
266
                                   propType,
267
                                   propUnits,
268
                                   propURL,
269
                                   propDescription);
270

271
            if (propType == QString::fromStdString("2DArray")
272
                || propType == QString::fromStdString("3DArray")) {
273
                // Base::Console().Log("Reading columns\n");
274
                // Read the columns
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());
279

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),
287
                                              colPropDisplayName,
288
                                              colPropType,
289
                                              colPropUnits,
290
                                              colPropURL,
291
                                              colPropDescription);
292

293
                    property.addColumn(colProperty);
294
                }
295
            }
296

297
            auto key = std::pair<QString, QString>(uuid, QString::fromStdString(propName));
298
            if (inheritances->count(key) > 0) {
299
                property.setInheritance((*inheritances)[key]);
300
            }
301

302
            finalModel.addProperty(property);
303
        }
304
    }
305

306
    (*_modelMap)[uuid] = library->addModel(finalModel, directory);
307
}
308

309
void ModelLoader::loadLibrary(std::shared_ptr<ModelLibrary> library)
310
{
311
    if (_modelEntryMap == nullptr) {
312
        _modelEntryMap = std::make_unique<std::map<QString, std::shared_ptr<ModelEntry>>>();
313
    }
314

315
    QDirIterator it(library->getDirectory(), QDirIterator::Subdirectories);
316
    while (it.hasNext()) {
317
        auto pathname = it.next();
318
        QFileInfo file(pathname);
319
        if (file.isFile()) {
320
            if (file.suffix().toStdString() == "yml") {
321
                try {
322
                    auto model = getModelFromPath(library, file.canonicalFilePath());
323
                    (*_modelEntryMap)[model->getUUID()] = model;
324
                    // showYaml(model->getModel());
325
                }
326
                catch (InvalidModel const&) {
327
                    Base::Console().Log("Invalid model '%s'\n", pathname.toStdString().c_str());
328
                }
329
            }
330
        }
331
    }
332

333
    std::map<std::pair<QString, QString>, QString> inheritances;
334
    for (auto it = _modelEntryMap->begin(); it != _modelEntryMap->end(); it++) {
335
        dereference(it->second, &inheritances);
336
    }
337

338
    for (auto it = _modelEntryMap->begin(); it != _modelEntryMap->end(); it++) {
339
        addToTree(it->second, &inheritances);
340
    }
341
}
342

343
void ModelLoader::loadLibraries()
344
{
345
    getModelLibraries();
346
    if (_libraryList) {
347
        for (auto it = _libraryList->begin(); it != _libraryList->end(); it++) {
348
            loadLibrary(*it);
349
        }
350
    }
351
}
352

353
void ModelLoader::getModelLibraries()
354
{
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);
361

362
    if (useBuiltInMaterials) {
363
        QString resourceDir = QString::fromStdString(App::Application::getResourceDir()
364
                                                     + "/Mod/Material/Resources/Models");
365
        auto libData =
366
            std::make_shared<ModelLibrary>(QString::fromStdString("System"),
367
                                           resourceDir,
368
                                           QString::fromStdString(":/icons/freecad.svg"));
369
        _libraryList->push_back(libData);
370
    }
371

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", ""));
380

381
            if (modelDir.length() > 0) {
382
                QDir dir(modelDir);
383
                if (dir.exists()) {
384
                    auto libData = std::make_shared<ModelLibrary>(moduleName, modelDir, modelIcon);
385
                    _libraryList->push_back(libData);
386
                }
387
            }
388
        }
389
    }
390

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"),
399
                    resourceDir,
400
                    QString::fromStdString(":/icons/preferences-general.svg"));
401
                _libraryList->push_back(libData);
402
            }
403
        }
404
    }
405

406
    if (useMatFromCustomDir) {
407
        QString resourceDir = QString::fromStdString(param->GetASCII("CustomMaterialsDir", ""));
408
        if (!resourceDir.isEmpty()) {
409
            QDir materialDir(resourceDir);
410
            if (materialDir.exists()) {
411
                auto libData =
412
                    std::make_shared<ModelLibrary>(QString::fromStdString("Custom"),
413
                                                   resourceDir,
414
                                                   QString::fromStdString(":/icons/user.svg"));
415
                _libraryList->push_back(libData);
416
            }
417
        }
418
    }
419
}
420

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.