mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-06 23:31:19 +00:00
Code review items (raistlin7447): 1. PrintObject::slice() zeroes m_belt_min_z, m_belt_global_z_offset and m_belt_global_xy_correction before slicing. They were only written in belt mode, so a project switched to a normal printer, or whose tilt axis was set to None, kept the old offsets and shifted the adaptive infill octree and the organic support layers by them. 2. TreeModelVolumes shifts the support blockers into the raft-offset index space; a test now pins the index the blocker lands on. 3. The final-alignment clamp in libnest2d is opt-in (NfpPConfig::clamp_to_bin) and arrange sets it for belt printers only. Printers with an off-centre best_object_pos keep their alignment; a flat-bed test pins that. 4. The preview's belt view follows the loaded G-code, not the selected printer: GCodeProcessor carries the file's belt keys (and, for a belt file, its bed) into export_config_for_render(), and GCodeViewer enables the belt view from the header tilt. 5. belt_shift_layer_grid() also shifts the cached belt floor and the global Z offset, so a support-only or brim-only change after the purge-prism snap matches a fresh slice. 6. update_print_fff_config() resets raft_layers and draft_shield on a belt printer instead of only greying out the fields Print::validate() rejects. 7. GCodeWriter takes a first-layer point test instead of the FirstLayerPlane; GCode installs one that measures from the belt surface, like its extrusions, so the first-layer travel speed and the second-layer temperature change no longer depend on the gcode_remap_* convention. 8. belt_brim_clip_leading_edge() is exported and called by both the generator and the test. 9. Both phong.vs shaders use slope.up_direction for the overhang highlight. The pre-slice and G-code axis remaps are gated on belt_printer through BeltTransformPipeline::axis_remap_enabled(), so belt keys left in a profile cannot change a non-belt print. Tests requested in the review: belt-only keys at non-default values leave non-belt G-code unchanged; switching a sliced project from belt to non-belt (and tilt axis None) matches a fresh slice; a support-only change on a belt purge print matches a fresh slice; non-belt start G-code moves keep the first-layer Z in the processor; the belt brim's segment count catches a band emitted twice. The belt-to-non-belt test exposed an unrelated gap: invalidate_step(posSlice) re-invalidated posSupportMaterial but not posSimplifySupportPath, so after any re-slice the regenerated support paths were exported unsimplified. posSimplifySupportPath is now in that list. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
96 lines
3.6 KiB
C++
96 lines
3.6 KiB
C++
#include <catch2/catch_all.hpp>
|
|
|
|
#include "libslic3r/libslic3r.h"
|
|
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
|
#include "libslic3r/PrintConfig.hpp"
|
|
|
|
#include "test_utils.hpp"
|
|
|
|
#include <cstddef>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <catch2/matchers/catch_matchers.hpp>
|
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
|
|
|
using namespace Slic3r;
|
|
using Catch::Matchers::WithinAbs;
|
|
|
|
namespace {
|
|
|
|
float processed_belt_tilt(const std::string &gcode)
|
|
{
|
|
ScopedTemporaryFile temp(".gcode");
|
|
{
|
|
std::ofstream os(temp.string());
|
|
os << gcode;
|
|
}
|
|
GCodeProcessor proc;
|
|
proc.apply_config(FullPrintConfig{});
|
|
proc.process_file(temp.string());
|
|
return proc.get_result().belt_tilt_angle;
|
|
}
|
|
|
|
constexpr const char *body = "G1 X10 Y10 Z0.2 F3000\nG1 X20 Y10 E1 F1200\n";
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("The config block's belt angle does not mark G-code as belt G-code", "[GCodeProcessor][belt]")
|
|
{
|
|
// Every printer's config block lists belt_slice_rotation_angle (default 45), belt or not.
|
|
const std::string gcode = std::string("; CONFIG_BLOCK_START\n; belt_printer = 0\n; belt_slice_rotation_angle = 45\n; CONFIG_BLOCK_END\n") + body;
|
|
CHECK_THAT(processed_belt_tilt(gcode), WithinAbs(0., 1e-6));
|
|
}
|
|
|
|
TEST_CASE("The belt header's angle marks G-code as belt G-code", "[GCodeProcessor][belt]")
|
|
{
|
|
const std::string gcode = std::string("; belt_slice_rotation_angle = -45.0\n") + body +
|
|
"; CONFIG_BLOCK_START\n; belt_printer = 1\n; belt_slice_rotation_angle = -45\n; CONFIG_BLOCK_END\n";
|
|
CHECK_THAT(processed_belt_tilt(gcode), WithinAbs(45., 1e-6));
|
|
}
|
|
|
|
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")
|
|
// stores the real Z of a move inside the start G-code. Every other printer must keep
|
|
// the historical behaviour: a prepare-stage move is pinned to the first-layer height
|
|
// so the preview does not draw the start sequence's travel. The gate is the belt
|
|
// header, so a file without one, whatever its config block says, takes this path.
|
|
struct BBLPrinterGuard {
|
|
bool prev = GCodeProcessor::s_IsBBLPrinter;
|
|
BBLPrinterGuard() { GCodeProcessor::s_IsBBLPrinter = false; }
|
|
~BBLPrinterGuard() { GCodeProcessor::s_IsBBLPrinter = prev; }
|
|
} bbl_guard;
|
|
|
|
const std::string gcode =
|
|
"G90\n"
|
|
"G21\n"
|
|
"M83\n"
|
|
";TYPE:Custom\n"
|
|
"G1 E-1.5 F2100\n"
|
|
"G1 X45 Y0.3 Z50 F12000\n" // prepare-stage travel to a high Z
|
|
"G1 E1.5 F1800\n"
|
|
";TYPE:Outer wall\n"
|
|
"G1 X46 Y0.3 Z50 E0.05\n"
|
|
"; CONFIG_BLOCK_START\n; belt_printer = 0\n; belt_slice_rotation_angle = 45\n; CONFIG_BLOCK_END\n";
|
|
|
|
GCodeProcessor processor;
|
|
FullPrintConfig config;
|
|
config.initial_layer_print_height.value = 0.3;
|
|
processor.apply_config(config);
|
|
processor.process_buffer(gcode);
|
|
const GCodeProcessorResult &result = processor.get_result();
|
|
REQUIRE_THAT(result.belt_tilt_angle, WithinAbs(0., 1e-6));
|
|
|
|
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; }
|
|
REQUIRE(first_extrude < result.moves.size());
|
|
REQUIRE(first_extrude > 0);
|
|
|
|
// The extrusion keeps its real Z; the prepare-stage move before it is pinned to the
|
|
// first-layer height.
|
|
CHECK_THAT(result.moves[first_extrude].position.z(), WithinAbs(50., 1e-3));
|
|
CHECK_THAT(result.moves[first_extrude - 1].position.z(), WithinAbs(0.3, 1e-3));
|
|
}
|