| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include <QDirIterator> |
| #include <QFileInfo> |
| #include <QString> |
|
|
| #include <App/Application.h> |
| #include <Base/FileInfo.h> |
| #include <Base/Interpreter.h> |
| #include <Base/Stream.h> |
|
|
|
|
| #include "Model.h" |
| #include "ModelLoader.h" |
| #include "ModelManager.h" |
|
|
|
|
| using namespace Materials; |
|
|
| ModelEntry::ModelEntry(const std::shared_ptr<ModelLibraryLocal>& 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, std::shared_ptr<ModelEntry>>> ModelLoader::_modelEntryMap = |
| nullptr; |
|
|
| ModelLoader::ModelLoader(std::shared_ptr<std::map<QString, std::shared_ptr<Model>>> modelMap, |
| std::shared_ptr<std::list<std::shared_ptr<ModelLibraryLocal>>> libraryList) |
| : _modelMap(modelMap) |
| , _libraryList(libraryList) |
| { |
| loadLibraries(); |
| } |
|
|
| void ModelLoader::addLibrary(std::shared_ptr<ModelLibraryLocal> model) |
| { |
| _libraryList->push_back(model); |
| } |
|
|
| const QString ModelLoader::getUUIDFromPath(const QString& path) |
| { |
| QFile file(path); |
| if (!file.exists()) { |
| throw ModelNotFound(); |
| } |
|
|
| try { |
| Base::FileInfo fi(path.toStdString()); |
| Base::ifstream str(fi); |
| YAML::Node yamlroot = YAML::Load(str); |
| 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&) { |
| throw ModelNotFound(); |
| } |
| } |
|
|
| std::shared_ptr<ModelEntry> ModelLoader::getModelFromPath(std::shared_ptr<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 { |
| Base::FileInfo fi(path.toStdString()); |
| Base::ifstream str(fi); |
| yamlroot = YAML::Load(str); |
| 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(); |
| } |
|
|
| auto localLibrary = std::static_pointer_cast<ModelLibraryLocal>(library); |
| std::shared_ptr<ModelEntry> model = std::make_shared<ModelEntry>(localLibrary, |
| 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, |
| std::shared_ptr<ModelEntry> parent, |
| std::shared_ptr<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(QStringLiteral("Name")); |
| exclude.insert(QStringLiteral("UUID")); |
| exclude.insert(QStringLiteral("URL")); |
| exclude.insert(QStringLiteral("Description")); |
| exclude.insert(QStringLiteral("DOI")); |
| exclude.insert(QStringLiteral("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.contains(QString::fromStdString(name))) { |
| |
| if (!parentProperties[name]) { |
| parentProperties[name] = it->second; |
| |
| (*inheritances)[std::pair<QString, QString>(uuid, QString::fromStdString(name))] = |
| yamlValue(childYaml[childBase], "UUID", ""); |
| } |
| } |
| } |
| |
| } |
|
|
|
|
| void ModelLoader::dereference(std::shared_ptr<ModelEntry> model, |
| std::map<std::pair<QString, QString>, QString>* inheritances) |
| { |
| |
| 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>()); |
|
|
| |
| try { |
| std::shared_ptr<ModelEntry> child = (*_modelEntryMap)[nodeName]; |
| dereference(model->getUUID(), model, child, inheritances); |
| } |
| catch (const std::out_of_range&) { |
| 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(std::shared_ptr<ModelEntry> model, |
| std::map<std::pair<QString, QString>, QString>* inheritances) |
| { |
| std::set<QString> exclude; |
| exclude.insert(QStringLiteral("Name")); |
| exclude.insert(QStringLiteral("UUID")); |
| exclude.insert(QStringLiteral("URL")); |
| exclude.insert(QStringLiteral("Description")); |
| exclude.insert(QStringLiteral("DOI")); |
| exclude.insert(QStringLiteral("Inherits")); |
|
|
| auto yamlModel = model->getModel(); |
| if (!model->getLibrary()->isLocal()) { |
| throw InvalidLibrary(); |
| } |
| auto library = model->getLibrary(); |
| auto base = model->getBase().toStdString(); |
| auto name = model->getName(); |
| auto directory = model->getDirectory(); |
| auto uuid = model->getUUID(); |
|
|
| 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(library, type, name, directory, uuid, description, url, doi); |
|
|
| |
| 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); |
| } |
| } |
|
|
| |
| auto yamlProperties = yamlModel[base]; |
| for (auto it = yamlProperties.begin(); it != yamlProperties.end(); it++) { |
| std::string propName = it->first.as<std::string>(); |
| if (!exclude.contains(QString::fromStdString(propName))) { |
| |
| auto yamlProp = yamlProperties[propName]; |
| auto propDisplayName = yamlValue(yamlProp, "DisplayName", ""); |
| auto propType = yamlValue(yamlProp, "Type", ""); |
| auto propUnits = yamlValue(yamlProp, "Units", ""); |
| auto propURL = yamlValue(yamlProp, "URL", ""); |
| auto propDescription = yamlValue(yamlProp, "Description", ""); |
| |
|
|
| ModelProperty property(QString::fromStdString(propName), |
| propDisplayName, |
| propType, |
| propUnits, |
| propURL, |
| propDescription); |
|
|
| if (propType == QStringLiteral("2DArray") || propType == QStringLiteral("3DArray")) { |
| |
| |
| auto cols = yamlProp["Columns"]; |
| for (const auto& col : cols) { |
| std::string colName = col.first.as<std::string>(); |
| |
|
|
| auto colProp = cols[colName]; |
| auto colPropDisplayName = yamlValue(colProp, "DisplayName", ""); |
| 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), |
| colPropDisplayName, |
| colPropType, |
| colPropUnits, |
| colPropURL, |
| colPropDescription); |
|
|
| property.addColumn(colProperty); |
| } |
| } |
|
|
| auto key = std::pair<QString, QString>(uuid, QString::fromStdString(propName)); |
| if (inheritances->contains(key)) { |
| property.setInheritance((*inheritances)[key]); |
| } |
|
|
| finalModel.addProperty(property); |
| } |
| } |
|
|
| (*_modelMap)[uuid] = library->addModel(finalModel, directory); |
| } |
|
|
| void ModelLoader::loadLibrary(std::shared_ptr<ModelLibraryLocal> library) |
| { |
| if (_modelEntryMap == nullptr) { |
| _modelEntryMap = std::make_unique<std::map<QString, std::shared_ptr<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") { |
| try { |
| auto model = getModelFromPath(library, file.canonicalFilePath()); |
| (*_modelEntryMap)[model->getUUID()] = model; |
| |
| } |
| catch (InvalidModel const&) { |
| Base::Console().log("Invalid model '%s'\n", pathname.toStdString().c_str()); |
| } |
| } |
| } |
| } |
|
|
| std::map<std::pair<QString, QString>, QString> inheritances; |
| 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); |
| } |
| } |
|
|
| 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 = std::make_shared<ModelLibraryLocal>(QStringLiteral("System"), |
| resourceDir, |
| QStringLiteral(":/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 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 = std::make_shared<ModelLibraryLocal>(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 = std::make_shared<ModelLibraryLocal>( |
| QStringLiteral("User"), |
| resourceDir, |
| QStringLiteral(":/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 = std::make_shared<ModelLibraryLocal>(QStringLiteral("Custom"), |
| resourceDir, |
| QStringLiteral(":/icons/user.svg")); |
| _libraryList->push_back(libData); |
| } |
| } |
| } |
| } |
|
|