diff --git a/app/src/main/java/com/dark98/santoku/fragment/PrinterConfigFragment.java b/app/src/main/java/com/dark98/santoku/fragment/PrinterConfigFragment.java index dcc9227..98a8bb0 100644 --- a/app/src/main/java/com/dark98/santoku/fragment/PrinterConfigFragment.java +++ b/app/src/main/java/com/dark98/santoku/fragment/PrinterConfigFragment.java @@ -196,6 +196,27 @@ public class PrinterConfigFragment extends ProfileListFragment { new OptionElement(def.options.get("elegoolink_bed_type")) )); } + if ("moonraker".equalsIgnoreCase(hostType) || "klipper".equalsIgnoreCase(hostType)) { + int insertIndex = list.size(); + for (int i = 0; i < list.size(); i++) { + OptionElement el = list.get(i); + if (el != null && el.simpleItem instanceof SubHeader) { + String title = ((SubHeader) el.simpleItem).title; + if ("Advanced".equals(title)) { + insertIndex = i; + break; + } + } + } + list.addAll(insertIndex, Arrays.asList( + new OptionElement(new SubHeader("Adaptive bed mesh")), + new OptionElement(def.options.get("bed_mesh_probe_distance")), + new OptionElement(def.options.get("bed_mesh_limit_min")), + new OptionElement(def.options.get("bed_mesh_limit_max")), + new OptionElement(def.options.get("adaptive_bed_mesh_margin")), + new OptionElement(new SpaceItem(0, ViewUtils.dp(4))) + )); + } return list; } diff --git a/app/src/main/java/com/dark98/santoku/slic3r/Slic3rConfigWrapper.java b/app/src/main/java/com/dark98/santoku/slic3r/Slic3rConfigWrapper.java index 806ef03..bd09f4c 100644 --- a/app/src/main/java/com/dark98/santoku/slic3r/Slic3rConfigWrapper.java +++ b/app/src/main/java/com/dark98/santoku/slic3r/Slic3rConfigWrapper.java @@ -96,7 +96,8 @@ public class Slic3rConfigWrapper { "machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e", "machine_min_extruding_rate", "machine_min_travel_rate", "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e", - "elegoolink_timelapse", "elegoolink_bed_leveling", "elegoolink_bed_type" + "elegoolink_timelapse", "elegoolink_bed_leveling", "elegoolink_bed_type", + "bed_mesh_probe_distance", "bed_mesh_limit_min", "bed_mesh_limit_max", "adaptive_bed_mesh_margin" ); public final static List PHYSICAL_PRINTER_CONFIG_KEYS = Arrays.asList( "preset_name", // temporary option to compatibility with older Slicer diff --git a/app/src/main/jni/libslic3r/GCode.cpp b/app/src/main/jni/libslic3r/GCode.cpp index 2e92c4f..34b1094 100644 --- a/app/src/main/jni/libslic3r/GCode.cpp +++ b/app/src/main/jni/libslic3r/GCode.cpp @@ -1159,6 +1159,14 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail m_label_objects.init(print.objects(), print.config().gcode_label_objects, print.config().gcode_flavor); file.write(m_label_objects.all_objects_header()); + bool adaptive_bed_mesh_ready = false; + Vec2d adaptive_bed_mesh_min; + Vec2d adaptive_bed_mesh_max; + int adaptive_probe_count_x = 0; + int adaptive_probe_count_y = 0; + const char *adaptive_bed_mesh_algo = nullptr; + const bool host_is_klipper = (this->config().host_type.value == htMoonraker); + // Update output variables after the extruders were initialized. m_placeholder_parser_integration.init(m_writer); // Let the start-up script prime the 1st printing tool. @@ -1206,7 +1214,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, @@ -1222,6 +1230,53 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail 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("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; + + 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()); + } + + 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)); // 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 @@ -1235,7 +1290,14 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // Enable ooze prevention if configured so. DoExport::init_ooze_prevention(print, m_ooze_prevention); - std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, initial_extruder_id); + const std::string &start_gcode_template = print.config().start_gcode.value; + const bool uses_adaptive_bed_mesh_placeholders = + start_gcode_template.find("adaptive_bed_mesh_min") != std::string::npos || + start_gcode_template.find("adaptive_bed_mesh_max") != std::string::npos || + start_gcode_template.find("bed_mesh_probe_count") != std::string::npos || + start_gcode_template.find("bed_mesh_algo") != std::string::npos; + + std::string start_gcode = this->placeholder_parser_process("start_gcode", start_gcode_template, initial_extruder_id); this->_print_first_layer_chamber_temperature(file, print, start_gcode, config().chamber_temperature.get_at(initial_extruder_id), false, false); this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true); @@ -1245,6 +1307,13 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), gcode_extrusion_role_to_string(GCodeExtrusionRole::Custom).c_str()); // Write the custom start G-code + if (host_is_klipper && adaptive_bed_mesh_ready && uses_adaptive_bed_mesh_placeholders) { + file.write_format("; Adaptive bed mesh: min=%.3f,%.3f max=%.3f,%.3f probe_count=%d,%d algo=%s\n", + adaptive_bed_mesh_min.x(), adaptive_bed_mesh_min.y(), + adaptive_bed_mesh_max.x(), adaptive_bed_mesh_max.y(), + adaptive_probe_count_x, adaptive_probe_count_y, + adaptive_bed_mesh_algo ? adaptive_bed_mesh_algo : ""); + } file.writeln(start_gcode); this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true); diff --git a/app/src/main/jni/libslic3r/PrintConfig.cpp b/app/src/main/jni/libslic3r/PrintConfig.cpp index 4765907..e717c7f 100644 --- a/app/src/main/jni/libslic3r/PrintConfig.cpp +++ b/app/src/main/jni/libslic3r/PrintConfig.cpp @@ -346,6 +346,35 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionPoints{ Vec2d(0, 0), Vec2d(200, 0), Vec2d(200, 200), Vec2d(0, 200) }); + def = this->add("bed_mesh_limit_min", coPoint); + def->label = L("Bed mesh min"); + def->tooltip = L("Minimum point for allowed bed mesh area. Default is no limits."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPoint(Vec2d(-99999, -99999))); + + def = this->add("bed_mesh_limit_max", coPoint); + def->label = L("Bed mesh max"); + def->tooltip = L("Maximum point for allowed bed mesh area. Default is no limits."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPoint(Vec2d(-99999, -99999))); + + def = this->add("bed_mesh_probe_distance", coPoint); + def->label = L("Probe point distance"); + def->tooltip = L("Distance between probe points for bed mesh, as X,Y."); + def->sidetext = L("mm"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionPoint(Vec2d(25, 25))); + + def = this->add("adaptive_bed_mesh_margin", coFloat); + def->label = L("Mesh margin"); + def->tooltip = L("Extra margin around first-layer print area used for adaptive bed mesh."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.0)); + def = this->add("auto_arrange_bed_clearance", coFloat); def->label = L("Auto-arrange bed clearance"); def->tooltip = L("Minimum distance from the bed edge when auto arranging."); @@ -2241,7 +2270,7 @@ void PrintConfigDef::init_fff_params() { "prusalink", "PrusaLink" }, { "prusaconnect", "PrusaConnect" }, { "octoprint", "OctoPrint" }, - { "moonraker", "Klipper (via Moonraker)" }, + { "moonraker", "Klipper (Moonraker)" }, { "duet", "Duet" }, { "flashair", "FlashAir" }, { "astrobox", "AstroBox" }, diff --git a/app/src/main/jni/libslic3r/PrintConfig.hpp b/app/src/main/jni/libslic3r/PrintConfig.hpp index 0974d1d..0abd112 100644 --- a/app/src/main/jni/libslic3r/PrintConfig.hpp +++ b/app/src/main/jni/libslic3r/PrintConfig.hpp @@ -857,7 +857,11 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionBool, avoid_crossing_perimeters)) ((ConfigOptionFloatOrPercent, avoid_crossing_perimeters_max_detour)) ((ConfigOptionPoints, bed_shape)) + ((ConfigOptionPoint, bed_mesh_probe_distance)) + ((ConfigOptionPoint, bed_mesh_limit_min)) + ((ConfigOptionPoint, bed_mesh_limit_max)) ((ConfigOptionInts, bed_temperature)) + ((ConfigOptionFloat, adaptive_bed_mesh_margin)) ((ConfigOptionFloat, bridge_acceleration)) ((ConfigOptionInts, bridge_fan_speed)) ((ConfigOptionBools, enable_dynamic_fan_speeds)) @@ -909,6 +913,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionFloat, perimeter_acceleration)) ((ConfigOptionStrings, post_process)) ((ConfigOptionBool, prefer_clockwise_movements)) + ((ConfigOptionEnum, host_type)) ((ConfigOptionString, printer_model)) ((ConfigOptionString, printer_notes)) ((ConfigOptionFloat, resolution))