mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-03 08:12:07 +00:00
Add belt-printer regression test for prepare-stage move Z
Processes a minimal belt start sequence through GCodeProcessor::process_buffer
and asserts the move preceding the first extrusion keeps its real Z, so it can
no longer back-transform to model Y~=0 and produce the phantom extrusion line.
Belt printers are non-Bambu, so the processor uses the compatible reserved
tags ("TYPE:"); the test sets s_IsBBLPrinter=false (saved/restored via an RAII
guard) to mirror the real printer. Proven to fail without the fix (the
prepare-stage move's Z is pinned to the first-layer height, 0 here) and pass
with it.
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "test_helpers.hpp"
|
||||
#include <cmath>
|
||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include "libslic3r/BeltGCodeWriter.hpp"
|
||||
@@ -885,7 +887,8 @@ SCENARIO("Belt: the first travel does not lift through the uninitialised origin"
|
||||
std::vector<unsigned int> extruder_ids { 0 };
|
||||
writer.set_extruders(extruder_ids);
|
||||
writer.set_extruder(0);
|
||||
writer.config.travel_speed.value = 100.0;
|
||||
// travel_speed became per-extruder (ConfigOptionFloatsNullable) upstream.
|
||||
writer.config.travel_speed.values = { 100.0 };
|
||||
writer.config.z_hop.values = { 0.4 };
|
||||
writer.config.retract_lift_above.values = { 0.0 };
|
||||
writer.config.retract_lift_below.values = { 0.0 };
|
||||
@@ -920,3 +923,77 @@ SCENARIO("Belt: the first travel does not lift through the uninitialised origin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regression test for the belt-printer "phantom extrusion line from Y=0" bug.
|
||||
//
|
||||
// GCodeProcessor::store_move_vertex pins a move's stored Z to the first-layer
|
||||
// height while m_processing_start_custom_gcode is set (the start G-code "prepare"
|
||||
// stage), because on a normal printer the toolhead Z there is not yet a real print
|
||||
// height. On a belt printer that override is wrong: Z is written explicitly and the
|
||||
// designed-view back-transform couples machine Z into the rendered model Y (the
|
||||
// belt tilt mixes the height and belt-feed axes). Overriding it back-transforms the
|
||||
// last prepare-stage move (the unretract right before the first extrusion) to
|
||||
// model Y ~= 0, and libvgcode then draws a phantom extrusion segment from Y ~= 0 to
|
||||
// the first real toolpath — rendered in the first extrusion role's color. The fix
|
||||
// keeps the real Z for belt printers (gated on belt_tilt_angle). Here we assert the
|
||||
// prepare-stage move keeps its real Z so it can no longer leak to Y ~= 0.
|
||||
SCENARIO("Belt: start-gcode prepare-stage moves keep their real Z", "[GCode][belt]")
|
||||
{
|
||||
// Belt printers are non-Bambu, so the G-code uses the "compatible" reserved
|
||||
// tags ("TYPE:" for the extrusion role). The processor selects the tag table
|
||||
// from the static s_IsBBLPrinter flag, so mirror the belt-printer setting here
|
||||
// (saved/restored so test ordering stays unaffected).
|
||||
struct BBLPrinterGuard {
|
||||
bool prev = GCodeProcessor::s_IsBBLPrinter;
|
||||
BBLPrinterGuard() { GCodeProcessor::s_IsBBLPrinter = false; }
|
||||
~BBLPrinterGuard() { GCodeProcessor::s_IsBBLPrinter = prev; }
|
||||
} bbl_guard;
|
||||
|
||||
GIVEN("A belt G-code whose start sequence travels to a high machine Z before the first extrusion") {
|
||||
// The leading "; belt_slice_rotation_angle = 45" header sets belt_tilt_angle
|
||||
// (parsed before the body), enabling the belt code path. ;TYPE:Custom before
|
||||
// any G1 turns on the prepare stage; ;TYPE:Outer wall turns it off, exactly
|
||||
// as a sliced belt print is laid out.
|
||||
const std::string gcode =
|
||||
"; belt_slice_rotation_angle = 45\n"
|
||||
"G90\n"
|
||||
"G21\n"
|
||||
"M83\n"
|
||||
";TYPE:Custom\n"
|
||||
"G1 E-1.5 F2100\n" // retract at the (0,0,0) origin
|
||||
"G1 X45 Y0.3 Z50 F12000\n" // travel to the approach point (prepare stage)
|
||||
"G1 E1.5 F1800\n" // unretract in place (prepare stage)
|
||||
";TYPE:Outer wall\n"
|
||||
"G1 X46 Y0.3 Z50 E0.05\n"; // first extrusion, same Z as the approach
|
||||
|
||||
GCodeProcessor processor;
|
||||
processor.process_buffer(gcode);
|
||||
const GCodeProcessorResult& result = processor.get_result();
|
||||
|
||||
THEN("the belt code path is active") {
|
||||
REQUIRE_THAT(result.belt_tilt_angle, Catch::Matchers::WithinAbs(45.0, 1e-4));
|
||||
}
|
||||
|
||||
WHEN("locating the first extrusion and the move that precedes it") {
|
||||
size_t first_extrude = result.moves.size();
|
||||
for (size_t i = 0; i < result.moves.size(); ++i)
|
||||
if (result.moves[i].type == EMoveType::Extrude) { first_extrude = i; break; }
|
||||
|
||||
THEN("an extrusion and a preceding move exist") {
|
||||
REQUIRE(first_extrude < result.moves.size());
|
||||
REQUIRE(first_extrude > 0);
|
||||
}
|
||||
|
||||
THEN("the preceding prepare-stage move shares the extrusion's real Z (no leak to Y=0)") {
|
||||
const float extrude_z = result.moves[first_extrude].position.z();
|
||||
const float prev_z = result.moves[first_extrude - 1].position.z();
|
||||
// The first extrusion is at the real Z=50; before the fix the
|
||||
// prepare-stage move's Z was pinned to the first-layer height
|
||||
// (0 here) instead, which back-transforms to model Y ~= 0 and
|
||||
// produces the phantom extrusion segment.
|
||||
REQUIRE_THAT(extrude_z, Catch::Matchers::WithinAbs(50.0, 1e-3));
|
||||
REQUIRE_THAT(prev_z, Catch::Matchers::WithinAbs(50.0, 1e-3));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user