Updated Config Handling

This commit is contained in:
Dark98
2026-02-15 22:19:31 +00:00
parent 213e723276
commit 2b8c182516
3 changed files with 134 additions and 46 deletions
@@ -258,7 +258,11 @@ public class FilamentConfigFragment extends ProfileListFragment {
@Override
protected void onResetConfig() {
currentConfig = new ConfigObject(Santoku.CONFIG.findFilament(Santoku.CONFIG.presets.get("filament")));
ConfigObject base = Santoku.CONFIG.findFilament(Santoku.CONFIG.presets.get("filament"));
if (base == null && Santoku.CONFIG.filamentConfigs != null && !Santoku.CONFIG.filamentConfigs.isEmpty()) {
base = Santoku.CONFIG.filamentConfigs.get(0);
}
currentConfig = base != null ? new ConfigObject(base) : ConfigObject.createCustomFilamentProfile();
}
@Override
+127 -45
View File
@@ -52,6 +52,7 @@
#include <chrono>
#include <math.h>
#include <optional>
#include <regex>
#include <string>
#include <string_view>
@@ -105,6 +106,29 @@ namespace Slic3r {
gcode += '\n';
}
static std::string rewrite_simple_curly_placeholders_to_legacy(const std::string &templ)
{
// Convert {name} or {name[0]} to legacy [name] / [name_0].
// Only touches simple, standalone placeholders to avoid breaking control macros.
static const std::regex pattern(R"(\{([A-Za-z_][A-Za-z0-9_]*)(?:\[(\d+)\])?\})");
std::string out;
out.reserve(templ.size());
size_t last_pos = 0;
for (auto it = std::sregex_iterator(templ.begin(), templ.end(), pattern); it != std::sregex_iterator(); ++it) {
const std::smatch &m = *it;
out.append(templ, last_pos, m.position() - last_pos);
const std::string &name = m[1].str();
const std::string &idx = m[2].str();
if (! idx.empty())
out += "[" + name + "_" + idx + "]";
else
out += "[" + name + "]";
last_pos = m.position() + m.length();
}
out.append(templ, last_pos, std::string::npos);
return out;
}
// Return true if tch_prefix is found in custom_gcode
static bool custom_gcode_changes_tool(const std::string& custom_gcode, const std::string& tch_prefix, unsigned next_extruder)
{
@@ -1087,6 +1111,8 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
m_placeholder_parser_integration.parser = print.placeholder_parser();
// Ensure all print config options are available for { } placeholder expressions.
m_placeholder_parser_integration.parser.apply_config(print.full_print_config());
m_placeholder_parser_integration.parser.update_timestamp();
m_placeholder_parser_integration.context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count());
// Enable passing global variables between PlaceholderParser invocations.
@@ -1181,6 +1207,29 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
this->placeholder_parser().set("has_wipe_tower", has_wipe_tower);
this->placeholder_parser().set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming);
this->placeholder_parser().set("total_toolchanges", tool_ordering.toolchanges_count());
{
const DynamicPrintConfig &cfg = print.full_print_config();
if (const ConfigOption *opt = cfg.optptr("first_layer_temperature")) {
this->placeholder_parser().set("first_layer_temperature", opt->clone());
} else {
this->placeholder_parser().set("first_layer_temperature", new ConfigOptionInts(print.config().first_layer_temperature.values));
}
if (const ConfigOption *opt = cfg.optptr("temperature")) {
this->placeholder_parser().set("temperature", opt->clone());
} else {
this->placeholder_parser().set("temperature", new ConfigOptionInts(print.config().temperature.values));
}
if (const ConfigOption *opt = cfg.optptr("first_layer_bed_temperature")) {
this->placeholder_parser().set("first_layer_bed_temperature", opt->clone());
} else {
this->placeholder_parser().set("first_layer_bed_temperature", new ConfigOptionInts(print.config().first_layer_bed_temperature.values));
}
if (const ConfigOption *opt = cfg.optptr("chamber_temperature")) {
this->placeholder_parser().set("chamber_temperature", opt->clone());
} else {
this->placeholder_parser().set("chamber_temperature", new ConfigOptionInts(print.config().chamber_temperature.values));
}
}
{
const char *bed_type_label = (print.config().elegoolink_bed_type.value == ElegooBedType::PTE) ? "Side A" : "Side B";
this->placeholder_parser().set("curr_bed_type", new ConfigOptionString(bed_type_label));
@@ -1214,7 +1263,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
this->placeholder_parser().set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() }));
this->placeholder_parser().set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
}
if (host_is_klipper) {
{
// Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line.
// It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower.
// It does NOT encompass user extrusions generated by custom G-code,
@@ -1224,59 +1273,64 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
pts->values.reserve(print.first_layer_convex_hull().size());
for (const Point &pt : print.first_layer_convex_hull().points)
pts->values.emplace_back(unscale(pt));
BoundingBoxf bbox(pts->values);
BoundingBoxf first_layer_bbox(pts->values);
this->placeholder_parser().set("first_layer_print_convex_hull", pts.release());
this->placeholder_parser().set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() }));
this->placeholder_parser().set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() }));
this->placeholder_parser().set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
this->placeholder_parser().set("first_layer_print_min", new ConfigOptionFloats({ first_layer_bbox.min.x(), first_layer_bbox.min.y() }));
this->placeholder_parser().set("first_layer_print_max", new ConfigOptionFloats({ first_layer_bbox.max.x(), first_layer_bbox.max.y() }));
this->placeholder_parser().set("first_layer_print_size", new ConfigOptionFloats({ first_layer_bbox.size().x(), first_layer_bbox.size().y() }));
this->placeholder_parser().set("num_extruders", int(print.config().nozzle_diameter.values.size()));
BoundingBoxf bed_bbox(print.config().bed_shape.values);
BoundingBoxf mesh_bbox = bbox;
if (bed_bbox.defined) {
const double margin = print.config().adaptive_bed_mesh_margin.value;
double min_x = bbox.min.x() - margin;
double min_y = bbox.min.y() - margin;
double max_x = bbox.max.x() + margin;
double max_y = bbox.max.y() + margin;
if (host_is_klipper) {
BoundingBoxf bed_bbox(print.config().bed_shape.values);
BoundingBoxf mesh_bbox = first_layer_bbox;
if (bed_bbox.defined) {
const double margin = print.config().adaptive_bed_mesh_margin.value;
double min_x = first_layer_bbox.min.x() - margin;
double min_y = first_layer_bbox.min.y() - margin;
double max_x = first_layer_bbox.max.x() + margin;
double max_y = first_layer_bbox.max.y() + margin;
min_x = std::max(min_x, bed_bbox.min.x());
min_y = std::max(min_y, bed_bbox.min.y());
max_x = std::min(max_x, bed_bbox.max.x());
max_y = std::min(max_y, bed_bbox.max.y());
min_x = std::max(min_x, bed_bbox.min.x());
min_y = std::max(min_y, bed_bbox.min.y());
max_x = std::min(max_x, bed_bbox.max.x());
max_y = std::min(max_y, bed_bbox.max.y());
const Vec2d limit_min = print.config().bed_mesh_limit_min.value;
const Vec2d limit_max = print.config().bed_mesh_limit_max.value;
if (limit_min.x() > -99998 || limit_min.y() > -99998) {
min_x = std::max(min_x, limit_min.x());
min_y = std::max(min_y, limit_min.y());
}
if (limit_max.x() > -99998 || limit_max.y() > -99998) {
max_x = std::min(max_x, limit_max.x());
max_y = std::min(max_y, limit_max.y());
const Vec2d limit_min = print.config().bed_mesh_limit_min.value;
const Vec2d limit_max = print.config().bed_mesh_limit_max.value;
if (limit_min.x() > -99998 || limit_min.y() > -99998) {
min_x = std::max(min_x, limit_min.x());
min_y = std::max(min_y, limit_min.y());
}
if (limit_max.x() > -99998 || limit_max.y() > -99998) {
max_x = std::min(max_x, limit_max.x());
max_y = std::min(max_y, limit_max.y());
}
mesh_bbox.min = Vec2d(min_x, min_y);
mesh_bbox.max = Vec2d(max_x, max_y);
}
mesh_bbox.min = Vec2d(min_x, min_y);
mesh_bbox.max = Vec2d(max_x, max_y);
adaptive_bed_mesh_min = mesh_bbox.min;
adaptive_bed_mesh_max = mesh_bbox.max;
adaptive_bed_mesh_ready = true;
this->placeholder_parser().set("adaptive_bed_mesh_min", new ConfigOptionFloats({ mesh_bbox.min.x(), mesh_bbox.min.y() }));
this->placeholder_parser().set("adaptive_bed_mesh_max", new ConfigOptionFloats({ mesh_bbox.max.x(), mesh_bbox.max.y() }));
const Vec2d probe_dist = print.config().bed_mesh_probe_distance.value;
const double probe_dist_x = std::max(1.0, probe_dist.x());
const double probe_dist_y = std::max(1.0, probe_dist.y());
const int probe_count_x = std::max(3, int(std::ceil(mesh_bbox.size().x() / probe_dist_x)) + 1);
const int probe_count_y = std::max(3, int(std::ceil(mesh_bbox.size().y() / probe_dist_y)) + 1);
adaptive_probe_count_x = probe_count_x;
adaptive_probe_count_y = probe_count_y;
this->placeholder_parser().set("bed_mesh_probe_count", new ConfigOptionInts({ probe_count_x, probe_count_y }));
adaptive_bed_mesh_algo = (probe_count_x < 4 || probe_count_y < 4) ? "lagrange" : "bicubic";
this->placeholder_parser().set("bed_mesh_algo", new ConfigOptionString(adaptive_bed_mesh_algo));
}
}
adaptive_bed_mesh_min = mesh_bbox.min;
adaptive_bed_mesh_max = mesh_bbox.max;
adaptive_bed_mesh_ready = true;
this->placeholder_parser().set("adaptive_bed_mesh_min", new ConfigOptionFloats({ mesh_bbox.min.x(), mesh_bbox.min.y() }));
this->placeholder_parser().set("adaptive_bed_mesh_max", new ConfigOptionFloats({ mesh_bbox.max.x(), mesh_bbox.max.y() }));
const Vec2d probe_dist = print.config().bed_mesh_probe_distance.value;
const double probe_dist_x = std::max(1.0, probe_dist.x());
const double probe_dist_y = std::max(1.0, probe_dist.y());
const int probe_count_x = std::max(3, int(std::ceil(mesh_bbox.size().x() / probe_dist_x)) + 1);
const int probe_count_y = std::max(3, int(std::ceil(mesh_bbox.size().y() / probe_dist_y)) + 1);
adaptive_probe_count_x = probe_count_x;
adaptive_probe_count_y = probe_count_y;
this->placeholder_parser().set("bed_mesh_probe_count", new ConfigOptionInts({ probe_count_x, probe_count_y }));
adaptive_bed_mesh_algo = (probe_count_x < 4 || probe_count_y < 4) ? "lagrange" : "bicubic";
this->placeholder_parser().set("bed_mesh_algo", new ConfigOptionString(adaptive_bed_mesh_algo));
{
// PlaceholderParser currently substitues non-existent vector values with the zero'th value, which is harmful in the case of "is_extruder_used[]"
// as Slicer may lie about availability of such non-existent extruder.
// We rather sacrifice 256B of memory before we change the behavior of the PlaceholderParser, which should really only fill in the non-existent
@@ -1829,6 +1883,34 @@ std::string GCodeGenerator::placeholder_parser_process(
}
catch (std::runtime_error &err)
{
// Retry with legacy placeholders for simple {name} / {name[idx]} patterns.
if (std::string(err.what()).find("Not a variable name") != std::string::npos) {
const std::string compat_templ = rewrite_simple_curly_placeholders_to_legacy(templ);
if (compat_templ != templ) {
ppi.update_from_gcodewriter(m_writer, m_print->wipe_tower_data());
std::string output = ppi.parser.process(compat_templ, current_extruder_id, config_override, &ppi.output_config, &ppi.context);
ppi.validate_output_vector_variables();
if (const std::vector<double> &pos = ppi.opt_position->values; ppi.position != pos) {
m_writer.update_position({ pos[0], pos[1], pos[2] });
this->last_position = this->gcode_to_point({ pos[0], pos[1] });
}
for (const Extruder &e : m_writer.extruders()) {
unsigned int eid = e.id();
assert(eid < ppi.num_extruders);
if ( eid < ppi.num_extruders) {
if (! m_writer.config.use_relative_e_distances && ! is_approx(ppi.e_position[eid], ppi.opt_e_position->values[eid]))
const_cast<Extruder&>(e).set_position(ppi.opt_e_position->values[eid]);
if (! is_approx(ppi.e_retracted[eid], ppi.opt_e_retracted->values[eid]) ||
! is_approx(ppi.e_restart_extra[eid], ppi.opt_e_restart_extra->values[eid]))
const_cast<Extruder&>(e).set_retracted(ppi.opt_e_retracted->values[eid], ppi.opt_e_restart_extra->values[eid]);
}
}
return output;
}
}
// Collect the names of failed template substitutions for error reporting.
auto it = ppi.failed_templates.find(name);
if (it == ppi.failed_templates.end())
@@ -1022,6 +1022,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone());
m_placeholder_parser.set("physical_printer_preset", new_full_config.option("physical_printer_settings_id")->clone());
// Make all config options available to the PlaceholderParser.
m_placeholder_parser.apply_config(new_full_config);
// We want the filament overrides to be applied over their respective extruder parameters by the PlaceholderParser.
// see "Placeholders do not respect filament overrides." GH issue #3649
m_placeholder_parser.apply_config(filament_overrides);