| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #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); |
| | } |
| | } |
| | } |
| | } |
| |
|