Merge upstream/main into belt-printer

Brings belt-printer up to main 4b4a261787. Resolutions:

- G-code header (#15897, #15915): main moved the header, config and
  thumbnail block later in _do_export; write_belt_header() moves with it,
  still after the thumbnails and outside the BTT_TFT gate.
- _extrude: first-layer acceleration keeps the per-path first-layer plane
  test with main's cached nozzle index (#16028); main's set_speed out-param
  form (#16108) everywhere else.
- GCodeWriter (#16108): the arc-to-polyline fallback for machine mappings
  that cannot express G2/G3 now runs in the out-param extrude_arc_to_xy,
  which is the overload GCode calls, and appends to the caller's string.
- GCodeProcessorResult: the belt fields join main's forwarding assign.
- Clipper2 (#15969): belt arrange helpers take Slic3r::Point; the tree
  support join types lose their ClipperLib qualifier.
- CLI arrange (#15837): belt printers still reserve no wipe tower.
- Wipe tower options (#15841): the two new sparse-layer toggles are hidden
  for belt printers like the rest of the tower options.
- Keyboard shortcuts (#15706): main's registry replaces the old key switch;
  the belt view toggle is re-registered in the next commit.
- Print::process: the belt purge-plan undo runs before main's SliceStarted
  event.
- scripts/filament_id_snapshot.json: deleted on main (a77209af8f).
- Includes and appended tests: union of both sides.
This commit is contained in:
harrierpigeon
2026-10-04 19:20:33 -05:00
11471 changed files with 2992690 additions and 2720441 deletions
+113 -50
View File
@@ -1,18 +1,39 @@
#include "GCodeWriter.hpp"
#include "FirstLayerPlane.hpp"
#include "Config.hpp"
#include "CustomGCode.hpp"
#include "Extruder.hpp"
#include "Geometry.hpp"
#include "I18N.hpp"
#include "Point.hpp"
#include "Polygon.hpp"
#include "PrintConfig.hpp"
#include "ClipperUtils.hpp"
#include "Geometry/ArcWelder.hpp"
#include "Line.hpp"
#include "LocalesUtils.hpp"
#include "libslic3r.h"
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdlib>
#include <array>
#include <cstdint>
#include <charconv>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <limits>
#include <map>
#include <assert.h>
#include <GCode/GCodeProcessor.hpp>
#include <string>
#include <vector>
#include <utility>
#include <sstream>
#include <stdexcept>
#include <math.h>
#ifdef __APPLE__
#include <boost/spirit/include/karma.hpp>
@@ -413,26 +434,35 @@ std::string GCodeWriter::set_acceleration_internal(Acceleration type, unsigned i
last_value = acceleration;
std::ostringstream gcode;
if (FLAVOR_IS(gcfRepetier))
gcode << (separate_travel ? "M202 X" : "M201 X") << acceleration << " Y" << acceleration;
else if (FLAVOR_IS(gcfRepRapFirmware) || FLAVOR_IS(gcfMarlinFirmware))
gcode << (separate_travel ? "M204 T" : "M204 P") << acceleration;
else if (FLAVOR_IS(gcfKlipper)) {
gcode << "SET_VELOCITY_LIMIT ACCEL=" << acceleration;
const std::string value = std::to_string(acceleration);
std::string gcode;
if (FLAVOR_IS(gcfRepetier)) {
gcode += separate_travel ? "M202 X" : "M201 X";
gcode += value;
gcode += " Y";
gcode += value;
} else if (FLAVOR_IS(gcfRepRapFirmware) || FLAVOR_IS(gcfMarlinFirmware)) {
gcode += separate_travel ? "M204 T" : "M204 P";
gcode += value;
} else if (FLAVOR_IS(gcfKlipper)) {
gcode.reserve(96);
gcode += "SET_VELOCITY_LIMIT ACCEL=";
gcode += value;
if (this->config.accel_to_decel_enable) {
gcode << " ACCEL_TO_DECEL=" << acceleration * this->config.accel_to_decel_factor / 100;
gcode += " ACCEL_TO_DECEL=";
gcode += float_to_string_decimal_point(acceleration * this->config.accel_to_decel_factor / 100);
if (GCodeWriter::full_gcode_comment)
gcode << " ; adjust ACCEL_TO_DECEL";
gcode += " ; adjust ACCEL_TO_DECEL";
}
} else {
gcode += "M204 S";
gcode += value;
}
else
gcode << "M204 S" << acceleration;
if (GCodeWriter::full_gcode_comment) gcode << " ; adjust acceleration";
gcode << "\n";
if (GCodeWriter::full_gcode_comment) gcode += " ; adjust acceleration";
gcode += "\n";
return gcode.str();
return gcode;
}
std::string GCodeWriter::set_jerk_xy(double jerk)
@@ -498,37 +528,40 @@ std::string GCodeWriter::set_accel_and_jerk(unsigned int acceleration, double je
if (EXTRUDER_LIMIT(m_max_acceleration) > 0 && acceleration > EXTRUDER_LIMIT(m_max_acceleration))
acceleration = EXTRUDER_LIMIT(m_max_acceleration);
bool is_empty = true;
std::ostringstream gcode;
gcode << "SET_VELOCITY_LIMIT";
if (acceleration != 0 && acceleration != m_last_acceleration) {
gcode << " ACCEL=" << acceleration;
if (this->config.accel_to_decel_enable) {
gcode << " ACCEL_TO_DECEL=" << acceleration * this->config.accel_to_decel_factor / 100;
}
m_last_acceleration = acceleration;
is_empty = false;
}
// Clamp the jerk to the allowed maximum.
if (EXTRUDER_LIMIT(m_max_jerk_x) > 0 && jerk > EXTRUDER_LIMIT(m_max_jerk_x))
jerk = EXTRUDER_LIMIT(m_max_jerk_x);
if (EXTRUDER_LIMIT(m_max_jerk_y) > 0 && jerk > EXTRUDER_LIMIT(m_max_jerk_y))
jerk = EXTRUDER_LIMIT(m_max_jerk_y);
if (jerk > 0.01 && !is_approx(jerk, m_last_jerk)) {
gcode << " SQUARE_CORNER_VELOCITY=" << jerk;
m_last_jerk = jerk;
is_empty = false;
}
if(is_empty)
const bool set_acceleration = acceleration != 0 && acceleration != m_last_acceleration;
const bool set_jerk = jerk > 0.01 && !is_approx(jerk, m_last_jerk);
if (!set_acceleration && !set_jerk)
return std::string();
if (GCodeWriter::full_gcode_comment)
gcode << " ; adjust VELOCITY_LIMIT(accel/jerk)";
gcode << "\n";
std::string gcode;
gcode.reserve(96);
gcode += "SET_VELOCITY_LIMIT";
if (set_acceleration) {
gcode += " ACCEL=";
gcode += std::to_string(acceleration);
if (this->config.accel_to_decel_enable) {
gcode += " ACCEL_TO_DECEL=";
gcode += float_to_string_decimal_point(acceleration * this->config.accel_to_decel_factor / 100);
}
m_last_acceleration = acceleration;
}
if (set_jerk) {
gcode += " SQUARE_CORNER_VELOCITY=";
gcode += float_to_string_decimal_point(jerk);
m_last_jerk = jerk;
}
return gcode.str();
if (GCodeWriter::full_gcode_comment)
gcode += " ; adjust VELOCITY_LIMIT(accel/jerk)";
gcode += "\n";
return gcode;
}
@@ -787,6 +820,13 @@ double GCodeWriter::get_extruder_retracted_length(const int filament_id)
}
std::string GCodeWriter::set_speed(double F, const std::string &comment, const std::string &cooling_marker)
{
std::string gcode;
this->set_speed(gcode, F, comment, cooling_marker);
return gcode;
}
void GCodeWriter::set_speed(std::string &out, double F, const std::string &comment, const std::string &cooling_marker)
{
assert(F > 0.);
assert(F < 100000.);
@@ -797,7 +837,7 @@ std::string GCodeWriter::set_speed(double F, const std::string &comment, const s
//BBS
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
w.emit_string(cooling_marker);
return w.string();
w.append_to(out);
}
std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment)
@@ -1210,6 +1250,13 @@ bool GCodeWriter::will_move_z(double z) const
}
std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string &comment, bool force_no_extrusion)
{
std::string gcode;
this->extrude_to_xy(gcode, point, dE, comment, force_no_extrusion);
return gcode;
}
void GCodeWriter::extrude_to_xy(std::string &out, const Vec2d &point, double dE, const std::string &comment, bool force_no_extrusion)
{
m_pos(0) = point(0);
m_pos(1) = point(1);
@@ -1233,23 +1280,25 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std:
w.emit_e(filament()->E());
//BBS
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
return w.string();
w.append_to(out);
}
// Approximate an arc with linear extrusions, for machine mappings that cannot
// express a G2/G3 (see extrude_arc_to_xy). center_offset is I/J: the centre
// relative to the CURRENT position, which is why this must run before m_pos is
// updated.
std::string GCodeWriter::extrude_arc_as_polyline(const Vec2d &point, const Vec2d &center_offset,
double dE, const bool is_ccw,
const std::string &comment, bool force_no_extrusion)
void GCodeWriter::extrude_arc_as_polyline(std::string &out, const Vec2d &point, const Vec2d &center_offset,
double dE, const bool is_ccw,
const std::string &comment, bool force_no_extrusion)
{
const Vec2d start = Vec2d(m_pos.x(), m_pos.y());
const Vec2d centre = start + center_offset;
const double r = (start - centre).norm();
if (r < EPSILON)
if (r < EPSILON) {
// Degenerate: no arc to speak of, so a single move is exact.
return this->extrude_to_xy(point, dE, comment, force_no_extrusion);
this->extrude_to_xy(out, point, dE, comment, force_no_extrusion);
return;
}
double a0 = std::atan2(start.y() - centre.y(), start.x() - centre.x());
double a1 = std::atan2(point.y() - centre.y(), point.x() - centre.x());
@@ -1262,20 +1311,25 @@ std::string GCodeWriter::extrude_arc_as_polyline(const Vec2d &point, const Vec2d
const double dmax = (tol >= r) ? PI : 2. * std::acos(1. - tol / r);
const int n = std::max(2, int(std::ceil(std::abs(sweep) / std::max(dmax, EPSILON))));
std::string out;
for (int i = 1; i <= n; ++ i) {
const double a = a0 + sweep * (double(i) / double(n));
const Vec2d p = (i == n) ? point
: Vec2d(centre.x() + r * std::cos(a), centre.y() + r * std::sin(a));
out += this->extrude_to_xy(p, dE / double(n), i == n ? comment : std::string(), force_no_extrusion);
this->extrude_to_xy(out, p, dE / double(n), i == n ? comment : std::string(), force_no_extrusion);
}
return out;
}
//BBS: generate G2 or G3 extrude which moves by arc
//point is end point which means X and Y axis
//center_offset is I and J axis
std::string GCodeWriter::extrude_arc_to_xy(const Vec2d& point, const Vec2d& center_offset, double dE, const bool is_ccw, const std::string& comment, bool force_no_extrusion)
{
std::string gcode;
this->extrude_arc_to_xy(gcode, point, center_offset, dE, is_ccw, comment, force_no_extrusion);
return gcode;
}
void GCodeWriter::extrude_arc_to_xy(std::string &out, const Vec2d& point, const Vec2d& center_offset, double dE, const bool is_ccw, const std::string& comment, bool force_no_extrusion)
{
// Arcs emit only X/Y/I/J, so a mapping that moves logical X or Y cannot be
// expressed as a G2/G3. GCode::should_disable_arc_fitting() normally stops
@@ -1289,8 +1343,10 @@ std::string GCodeWriter::extrude_arc_to_xy(const Vec2d& point, const Vec2d& cent
// its diameter and a full circle a stationary blob -- so approximate the arc
// with linear segments bounded by a chord tolerance, splitting dE between
// them in proportion to arc length.
if (! m_kinematics->supports_arc_moves())
return this->extrude_arc_as_polyline(point, center_offset, dE, is_ccw, comment, force_no_extrusion);
if (! m_kinematics->supports_arc_moves()) {
this->extrude_arc_as_polyline(out, point, center_offset, dE, is_ccw, comment, force_no_extrusion);
return;
}
m_pos(0) = point(0);
m_pos(1) = point(1);
@@ -1306,10 +1362,17 @@ std::string GCodeWriter::extrude_arc_to_xy(const Vec2d& point, const Vec2d& cent
w.emit_e(filament()->E());
//BBS
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
return w.string();
w.append_to(out);
}
std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment, bool force_no_extrusion)
{
std::string gcode;
this->extrude_to_xyz(gcode, point, dE, comment, force_no_extrusion);
return gcode;
}
void GCodeWriter::extrude_to_xyz(std::string &out, const Vec3d &point, double dE, const std::string &comment, bool force_no_extrusion)
{
// Check if Z actually changes (at export precision) before emitting it.
// ZAA sloped extrusions call this for every segment, but many consecutive
@@ -1341,7 +1404,7 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std
w.emit_e(filament()->E());
//BBS
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
return w.string();
w.append_to(out);
}
std::string GCodeWriter::retract(bool before_wipe, double retract_length)