Skip to content

Commit

Permalink
Add option to command line settings to read in resolved values (#2082)
Browse files Browse the repository at this point in the history
  • Loading branch information
HellAholic authored May 28, 2024
2 parents 09dfa73 + a33d560 commit 3fb48b6
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 1 deletion.
14 changes: 13 additions & 1 deletion include/MeshGroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,21 @@ class Matrix4x3D;
* One MeshGroup is a whole which is printed at once.
* Generally there is one single MeshGroup, though when using one-at-a-time printing, multiple MeshGroups are processed consecutively.
*/
class MeshGroup : public NoCopy
class MeshGroup
{
public:
MeshGroup() = default;

~MeshGroup() = default;

MeshGroup(MeshGroup&& other) noexcept = default;

MeshGroup& operator=(MeshGroup&& other) noexcept = default;

/* Copying a MeshGroup is not allowed */
MeshGroup(const MeshGroup& other) = delete;
MeshGroup& operator=(const MeshGroup& other) = delete;

std::vector<Mesh> meshes;
Settings settings;

Expand Down
19 changes: 19 additions & 0 deletions include/communication/CommandLine.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
#define COMMANDLINE_H

#include <filesystem>
#include <optional>
#include <rapidjson/document.h> //Loading JSON documents to get settings from them.
#include <string> //To store the command line arguments.
#include <unordered_map>
#include <vector> //To store the command line arguments.

#include "Communication.h" //The class we're implementing.
Expand All @@ -15,6 +17,9 @@ namespace cura
{
class Settings;

using setting_map = std::unordered_map<std::string, std::string>;
using container_setting_map = std::unordered_map<std::string, setting_map>;

/*
* \brief When slicing via the command line, interprets the command line
* arguments to initiate a slice.
Expand Down Expand Up @@ -212,6 +217,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<std::filesystem::path>& 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<container_setting_map> readResolvedJsonValues(const std::filesystem::path& json_filename);

/*
* \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<container_setting_map> readResolvedJsonValues(const rapidjson::Document& document);
};

} // namespace cura
Expand Down
1 change: 1 addition & 0 deletions include/geometry/Shape.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#define GEOMETRY_SHAPE_H

#include "geometry/LinesSet.h"
#include "geometry/Polygon.h"
#include "settings/types/Angle.h"

namespace cura
Expand Down
1 change: 1 addition & 0 deletions src/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <setting>=<value>\n\tSet a setting to a value for the last supplied object, \n\textruder train, or general settings.\n");
fmt::print(" -l <model_file>\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");
Expand Down
2 changes: 2 additions & 0 deletions src/MeshGroup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ bool loadMeshIntoMeshGroup(MeshGroup* meshgroup, const char* filename, const Mat
spdlog::info("loading '{}' took {:03.3f} seconds", filename, load_timer.restart());
return true;
}
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;
Expand Down
175 changes: 175 additions & 0 deletions src/communication/CommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
#include <filesystem>
#include <fstream> //To check if files exist.
#include <numeric> //For std::accumulate.
#include <optional>
#include <rapidjson/error/en.h> //Loading JSON documents to get settings from them.
#include <rapidjson/rapidjson.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
#include <string>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
#include <utility>

#include <range/v3/all.hpp>
#include <spdlog/details/os.h>
Expand All @@ -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"
Expand Down Expand Up @@ -354,6 +359,127 @@ void CommandLine::sliceNext()
last_settings->add(key, value);
break;
}
case 'r':
{
/*
* read in resolved values from a json file. The json format of the file resolved settings is the following:
*
* ```
* {
* "global": [SETTINGS],
* "extruder.0": [SETTINGS],
* "extruder.1": [SETTINGS],
* "model.stl": [SETTINGS]
* }
* ```
* where `[SETTINGS]` follow the schema
* ```
* {
* [key: string]: bool | string | number | number[] | number[][]
* }
* ```
* There can be any number of extruders (denoted with `extruder.n`) and any number of models (denoted with `[modelname].stl`).
* The key of the model values will also be the filename of the relevant model, when running CuraEngine with this option the
* model file with that same name _must_ be in the same folder as the resolved settings json.
*/

argument_index++;
if (argument_index >= arguments_.size())
{
spdlog::error("Missing setting name and value with -r 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<std::string, std::string> global_settings;
container_setting_map extruder_settings;
container_setting_map model_settings;
std::unordered_map<std::string, std::string> 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<size_t>(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<Matrix4x3D>("mesh_rotation_matrix");
const auto extruder_nr = mesh_group.settings.get<size_t>("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]);
Expand Down Expand Up @@ -586,6 +712,55 @@ void CommandLine::loadJSONSettings(const rapidjson::Value& element, Settings& se
}
}

std::optional<container_setting_map> 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<char> read_buffer(std::istreambuf_iterator<char>(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<container_setting_map> CommandLine::readResolvedJsonValues(const rapidjson::Document& document)
{
if (! document.IsObject())
{
return std::nullopt;
}

container_setting_map result;
for (rapidjson::Value::ConstMemberIterator resolved_key = document.MemberBegin(); resolved_key != document.MemberEnd(); resolved_key++)
{
std::unordered_map<std::string, std::string> 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<std::filesystem::path>& search_directories)
{
for (const auto& search_directory : search_directories)
Expand Down

0 comments on commit 3fb48b6

Please sign in to comment.