diff --git a/include/communication/CommandLine.h b/include/communication/CommandLine.h index 47b27f6586..5f70994517 100644 --- a/include/communication/CommandLine.h +++ b/include/communication/CommandLine.h @@ -5,6 +5,7 @@ #define COMMANDLINE_H #include +#include #include //Loading JSON documents to get settings from them. #include //To store the command line arguments. #include //To store the command line arguments. @@ -212,6 +213,20 @@ class CommandLine : public Communication * \return The first definition file that matches the definition ID. */ static std::string findDefinitionFile(const std::string& definition_id, const std::vector& search_directories); + + /* + * \brief Read the resolved JSON values from a file. + * \param element The path to the file to read the JSON values from. + * \return The resolved JSON values. + */ + static std::optional>> readResolvedJsonValues(const std::filesystem::path& element); + + /* + * \brief Read the resolved JSON values from a document. + * \param document The document to read the JSON values from. + * \return The resolved JSON values. + */ + static std::optional>> readResolvedJsonValues(const rapidjson::Document& document); }; } // namespace cura diff --git a/src/Application.cpp b/src/Application.cpp index 14f5a1bf7d..cb0f17b297 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -131,6 +131,7 @@ void Application::printHelp() const fmt::print(" -p\n\tLog progress information.\n"); fmt::print(" -d Add definition search paths seperated by a `:` (Unix) or `;` (Windows)\n"); fmt::print(" -j\n\tLoad settings.def.json file to register all settings and their defaults.\n"); + fmt::print(" -r\n\tLoad a json file containing resolved setting values.\n"); fmt::print(" -s =\n\tSet a setting to a value for the last supplied object, \n\textruder train, or general settings.\n"); fmt::print(" -l \n\tLoad an STL model. \n"); fmt::print(" -g\n\tSwitch setting focus to the current mesh group only.\n\tUsed for one-at-a-time printing.\n"); diff --git a/src/MeshGroup.cpp b/src/MeshGroup.cpp index f71b2f7bd5..b518d77328 100644 --- a/src/MeshGroup.cpp +++ b/src/MeshGroup.cpp @@ -293,6 +293,11 @@ bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const Mat spdlog::info("loading '{}' took {:03.3f} seconds", filename, load_timer.restart()); return true; } + else + { + spdlog::warn("loading '{}' failed", filename); + return false; + } } spdlog::warn("Unable to recognize the extension of the file. Currently only .stl and .STL are supported."); return false; diff --git a/src/communication/CommandLine.cpp b/src/communication/CommandLine.cpp index de88960fbf..fb078b0df7 100644 --- a/src/communication/CommandLine.cpp +++ b/src/communication/CommandLine.cpp @@ -8,12 +8,16 @@ #include #include //To check if files exist. #include //For std::accumulate. +#include #include //Loading JSON documents to get settings from them. #include #include #include #include +#include +#include #include +#include #include #include @@ -22,6 +26,7 @@ #include "Application.h" //To get the extruders for material estimates. #include "ExtruderTrain.h" #include "FffProcessor.h" //To start a slice and get time estimates. +#include "MeshGroup.h" #include "Slice.h" #include "utils/Matrix4x3D.h" //For the mesh_rotation_matrix setting. #include "utils/format/filesystem_path.h" @@ -354,6 +359,107 @@ void CommandLine::sliceNext() last_settings->add(key, value); break; } + case 'r': + { + // read in resolved values + + argument_index++; + if (argument_index >= arguments_.size()) + { + spdlog::error("Missing setting name and value with -s argument."); + exit(1); + } + argument = arguments_[argument_index]; + const auto settings = readResolvedJsonValues(std::filesystem::path{ argument }); + + if (! settings.has_value()) + { + spdlog::error("Failed to load JSON file: {}", argument); + exit(1); + } + + constexpr std::string_view global_identifier = "global"; + constexpr std::string_view extruder_identifier = "extruder."; + constexpr std::string_view model_identifier = "model."; + constexpr std::string_view limit_to_extruder_identifier = "limit_to_extruder"; + + // Split the settings into global, extruder and model settings. This is needed since the order in which the settings are applied is important. + // first global settings, then extruder settings, then model settings. The order of these stacks is not enforced in the JSON files. + std::unordered_map global_settings; + std::unordered_map> extruder_settings; + std::unordered_map> model_settings; + std::unordered_map limit_to_extruder; + + for (const auto& [key, values] : settings.value()) + { + if (key == global_identifier) + { + global_settings = values; + } + else if (key.starts_with(extruder_identifier)) + { + extruder_settings[key] = values; + } + else if (key == limit_to_extruder_identifier) + { + limit_to_extruder = values; + } + else + { + model_settings[key] = values; + } + } + + for (const auto& [setting_key, setting_value] : global_settings) + { + slice.scene.settings.add(setting_key, setting_value); + } + + for (const auto& [key, values] : extruder_settings) + { + const auto extruder_nr = std::stoi(key.substr(extruder_identifier.size())); + while (slice.scene.extruders.size() <= static_cast(extruder_nr)) + { + slice.scene.extruders.emplace_back(extruder_nr, &slice.scene.settings); + } + for (const auto& [setting_key, setting_value] : values) + { + slice.scene.extruders[extruder_nr].settings_.add(setting_key, setting_value); + } + } + + for (const auto& [key, values] : model_settings) + { + const auto model_name = key; + + cura::MeshGroup mesh_group; + for (const auto& [setting_key, setting_value] : values) + { + mesh_group.settings.add(setting_key, setting_value); + } + + const auto transformation = mesh_group.settings.get("mesh_rotation_matrix"); + const auto extruder_nr = mesh_group.settings.get("extruder_nr"); + + if (! loadMeshIntoMeshGroup(&mesh_group, model_name.c_str(), transformation, slice.scene.extruders[extruder_nr].settings_)) + { + spdlog::error("Failed to load model: {}. (error number {})", model_name, errno); + exit(1); + } + + slice.scene.mesh_groups.push_back(std::move(mesh_group)); + } + for (const auto& [key, value] : limit_to_extruder) + { + const auto extruder_nr = std::stoi(value.substr(extruder_identifier.size())); + if (extruder_nr >= 0) + { + slice.scene.limit_to_extruder[key] = &slice.scene.extruders[extruder_nr]; + } + } + + break; + } default: { spdlog::error("Unknown option: -{}", argument[1]); @@ -586,6 +692,55 @@ void CommandLine::loadJSONSettings(const rapidjson::Value& element, Settings& se } } +std::optional>> CommandLine::readResolvedJsonValues(const std::filesystem::path& json_filename) +{ + std::ifstream file(json_filename, std::ios::binary); + if (! file) + { + spdlog::error("Couldn't open JSON file: {}", json_filename); + return std::nullopt; + } + + std::vector read_buffer(std::istreambuf_iterator(file), {}); + rapidjson::MemoryStream memory_stream(read_buffer.data(), read_buffer.size()); + + rapidjson::Document json_document; + json_document.ParseStream(memory_stream); + if (json_document.HasParseError()) + { + spdlog::error("Error parsing JSON (offset {}): {}", json_document.GetErrorOffset(), GetParseError_En(json_document.GetParseError())); + return std::nullopt; + } + + return readResolvedJsonValues(json_document); +} + +std::optional>> CommandLine::readResolvedJsonValues(const rapidjson::Document& document) +{ + if (! document.IsObject()) + { + return std::nullopt; + } + + std::unordered_map> result; + for (rapidjson::Value::ConstMemberIterator resolved_key = document.MemberBegin(); resolved_key != document.MemberEnd(); resolved_key++) + { + std::unordered_map values; + for (rapidjson::Value::ConstMemberIterator resolved_value = resolved_key->value.MemberBegin(); resolved_value != resolved_key->value.MemberEnd(); resolved_value++) + { + std::string value_string; + if (! jsonValue2Str(resolved_value->value, value_string)) + { + spdlog::warn("Unrecognized data type in JSON setting {}", resolved_value->name.GetString()); + continue; + } + values.emplace(resolved_value->name.GetString(), value_string); + } + result.emplace(resolved_key->name.GetString(), std::move(values)); + } + return result; +} + std::string CommandLine::findDefinitionFile(const std::string& definition_id, const std::vector& search_directories) { for (const auto& search_directory : search_directories)