From 2b8c1825169d19bc0cc0b05bd4c88faca4475e6e Mon Sep 17 00:00:00 2001 From: Dark98 Date: Sun, 15 Feb 2026 22:19:31 +0000 Subject: [PATCH] Updated Config Handling --- .../fragment/FilamentConfigFragment.java | 6 +- app/src/main/jni/libslic3r/GCode.cpp | 172 +++++++++++++----- app/src/main/jni/libslic3r/PrintApply.cpp | 2 + 3 files changed, 134 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/com/dark98/santoku/fragment/FilamentConfigFragment.java b/app/src/main/java/com/dark98/santoku/fragment/FilamentConfigFragment.java index 2e475db..d0bdd62 100644 --- a/app/src/main/java/com/dark98/santoku/fragment/FilamentConfigFragment.java +++ b/app/src/main/java/com/dark98/santoku/fragment/FilamentConfigFragment.java @@ -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 diff --git a/app/src/main/jni/libslic3r/GCode.cpp b/app/src/main/jni/libslic3r/GCode.cpp index 34b1094..890299c 100644 --- a/app/src/main/jni/libslic3r/GCode.cpp +++ b/app/src/main/jni/libslic3r/GCode.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -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 &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(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(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()) diff --git a/app/src/main/jni/libslic3r/PrintApply.cpp b/app/src/main/jni/libslic3r/PrintApply.cpp index 656bcf1..3444df7 100644 --- a/app/src/main/jni/libslic3r/PrintApply.cpp +++ b/app/src/main/jni/libslic3r/PrintApply.cpp @@ -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);