From 1591423f9154be34b66bcae839343f1d2bf4d295 Mon Sep 17 00:00:00 2001 From: SoftFever <103989404+SoftFever@users.noreply.github.com> Date: Wed, 14 Oct 2026 00:39:09 +0800 Subject: [PATCH] Fix crash when opening a G-code file exported by OrcaSlicer (#16441) --- src/libslic3r/GCode/GCodeProcessor.cpp | 3 +- tests/fff_print/test_gcode_processor.cpp | 73 ++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 83b33890fc..ac047be180 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -3752,7 +3752,8 @@ DynamicConfig GCodeProcessor::export_config_for_render() const config.set_key_value("filament_is_support", new ConfigOptionBools(m_parser.get_config().filament_is_support.values)); config.set_key_value("filament_type", new ConfigOptionStrings(m_parser.get_config().filament_type.values)); config.set_key_value("filament_map", new ConfigOptionInts(m_parser.get_config().filament_map.values)); - config.apply(m_belt_render_config); + // += clones the options; apply() would need a definition this config does not have. + config += m_belt_render_config; return config; } diff --git a/tests/fff_print/test_gcode_processor.cpp b/tests/fff_print/test_gcode_processor.cpp index d45d6534f7..82b2e760a3 100644 --- a/tests/fff_print/test_gcode_processor.cpp +++ b/tests/fff_print/test_gcode_processor.cpp @@ -2,14 +2,19 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/GCode/GCodeProcessor.hpp" +#include "libslic3r/Model.hpp" +#include "libslic3r/Print.hpp" #include "libslic3r/PrintConfig.hpp" #include "test_utils.hpp" #include #include +#include #include +#include #include +#include #include #include @@ -49,6 +54,74 @@ TEST_CASE("The belt header's angle marks G-code as belt G-code", "[GCodeProcesso CHECK_THAT(processed_belt_tilt(gcode), WithinAbs(45., 1e-6)); } +TEST_CASE("A loaded G-code's belt settings and bed replace the selected printer's in the preview", "[GCodeProcessor][belt][Regression]") +{ + // Loading a G-code applies the selected printer's config to the Print, then the file's render + // config on top, so the preview's belt view and back-transform follow the file. The selected + // printer is always the other kind, and every belt and bed key differs between the two. + const bool file_is_belt = GENERATE(false, true); + CAPTURE(file_is_belt); + + DynamicPrintConfig file = DynamicPrintConfig::full_print_config(); + file.set_deserialize_strict({ + { "belt_printer", file_is_belt }, + { "belt_slice_rotation", "y" }, + { "belt_slice_rotation_angle", -30 }, + { "gcode_remap_x", "rev_x" }, + { "gcode_remap_y", "pos_z" }, + { "gcode_remap_z", "pos_y" }, + { "belt_frame_tilt_decouple", 1 }, + { "belt_frame_tilt_angle", 35 }, + { "printable_area", "0x0,300x0,300x250,0x250" }, + { "printable_height", 123 }, + }); + DynamicPrintConfig selected = DynamicPrintConfig::full_print_config(); + selected.set_deserialize_strict({ + { "belt_printer", !file_is_belt }, + { "belt_slice_rotation", "x" }, + { "belt_slice_rotation_angle", 45 }, + { "gcode_remap_x", "pos_x" }, + { "gcode_remap_y", "pos_y" }, + { "gcode_remap_z", "pos_z" }, + { "belt_frame_tilt_decouple", 0 }, + { "belt_frame_tilt_angle", 50 }, + { "printable_area", "0x0,180x0,180x180,0x180" }, + { "printable_height", 200 }, + }); + + ScopedTemporaryFile temp(".gcode"); + { + std::ofstream os(temp.string()); + REQUIRE(os.is_open()); + // A full config block, as the loader rejects one with fewer than 80 values. + os << "; generated by OrcaSlicer\n" << body << "; CONFIG_BLOCK_START\n"; + for (const std::string &key : file.keys()) + os << "; " << key << " = " << file.opt_serialize(key) << '\n'; + os << "; CONFIG_BLOCK_END\n"; + REQUIRE(os.good()); + } + GCodeProcessor processor; + REQUIRE_NOTHROW(processor.process_file(temp.string())); + + Model model; + Print print; + print.apply(model, selected); + REQUIRE_NOTHROW(print.apply_config_for_render(processor.export_config_for_render())); + + // The file's belt settings win either way, so a non-belt file turns a selected belt printer's view off. + for (const char *key : { "belt_printer", "belt_slice_rotation", "belt_slice_rotation_angle", "gcode_remap_x", + "gcode_remap_y", "gcode_remap_z", "belt_frame_tilt_decouple", "belt_frame_tilt_angle" }) { + CAPTURE(key); + CHECK(print.config().opt_serialize(key) == file.opt_serialize(key)); + } + // Only a belt file brings the bed it was sliced for. + const DynamicPrintConfig &bed_source = file_is_belt ? file : selected; + for (const char *key : { "printable_area", "printable_height" }) { + CAPTURE(key); + CHECK(print.config().opt_serialize(key) == bed_source.opt_serialize(key)); + } +} + TEST_CASE("Non-belt start G-code moves keep the first-layer Z in the processor", "[GCodeProcessor]") { // The belt path (GCodeWriter tests: "start-gcode prepare-stage moves keep their real Z")