Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to command line settings to read in resolved values #2082

Merged
merged 13 commits into from
May 28, 2024
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
16 changes: 16 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 Down Expand Up @@ -212,6 +214,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<std::unordered_map<std::string, std::unordered_map<std::string, std::string>>> 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<std::unordered_map<std::string, std::unordered_map<std::string, std::string>>> readResolvedJsonValues(const rapidjson::Document& document);
};

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

#include "geometry/OpenLinesSet.h"
#include "geometry/Point2LL.h"
#include "geometry/Polygon.h"
casperlamboo marked this conversation as resolved.
Show resolved Hide resolved

namespace cura
{
Expand Down
1 change: 1 addition & 0 deletions include/settings/types/LayerIndex.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <fmt/format.h>

#include "geometry/Polygon.h"
casperlamboo marked this conversation as resolved.
Show resolved Hide resolved
#include "utils/types/generic.h"

namespace cura
Expand Down
1 change: 1 addition & 0 deletions include/slicer.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "geometry/LinesSet.h"
#include "geometry/OpenLinesSet.h"
#include "geometry/OpenPolyline.h"
#include "geometry/Polygon.h"
casperlamboo marked this conversation as resolved.
Show resolved Hide resolved
#include "geometry/Shape.h"
#include "settings/EnumSettings.h"

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
5 changes: 5 additions & 0 deletions src/MeshGroup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
casperlamboo marked this conversation as resolved.
Show resolved Hide resolved
}
spdlog::warn("Unable to recognize the extension of the file. Currently only .stl and .STL are supported.");
return false;
Expand Down
155 changes: 155 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,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<std::string, std::string> global_settings;
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> extruder_settings;
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> 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 +692,55 @@ void CommandLine::loadJSONSettings(const rapidjson::Value& element, Settings& se
}
}

std::optional<std::unordered_map<std::string, std::unordered_map<std::string, std::string>>> CommandLine::readResolvedJsonValues(const std::filesystem::path& json_filename)
casperlamboo marked this conversation as resolved.
Show resolved Hide resolved
{
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<std::unordered_map<std::string, std::unordered_map<std::string, std::string>>> CommandLine::readResolvedJsonValues(const rapidjson::Document& document)
{
if (! document.IsObject())
{
return std::nullopt;
}

std::unordered_map<std::string, std::unordered_map<std::string, std::string>> 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