Merge upstream/main into belt-printer

Brings the belt-printer work up to date with 591 upstream commits.

Conflict resolutions (12 files, 42 hunks):

- GCode.cpp: adopted upstream's per-filament/per-nozzle config refactor
  (get_filament_config_index, NOZZLE_CONFIG), the extracted
  generate_timelapse_gcode + farthest-point timelapse, and the
  ConfigOptionFloatsNullable calibration options. Re-applied the belt
  hooks on top: init_belt_writer / axis remap / FirstLayerPlane setup,
  on_set_origin, the belt-corrected calib_z for the volumetric speed
  tower, and path_on_first_layer (belt's per-path first-layer test) in
  place of upstream's layer-index on_first_layer() in the acceleration,
  jerk and overhang-detection paths. Swept upstream's new m_writer.
  uses to m_writer-> since belt holds the writer by unique_ptr.
- interpolate_value_across_layers: kept upstream's banded stepping and
  belt's object-Z-span ratio; dropped upstream's duplicate ratio decl.
- Plater.cpp: took upstream's guarded add_model(...) early-returns and
  the VFA vfa_layer_height plumbing; kept the belt temp-tower path,
  _calib_apply_belt_mode and belt_calib_flip_ringing_tower. Dropped the
  VFA "cut upper" block, superseded upstream by model scaling.
- Brim.cpp: upstream's ObjectInstanceID-keyed brimAreaMap, keeping the
  belt early-return.
- 3DScene.cpp: kept both the belt build-plate tilt up_direction and
  upstream's per-extruder printable-height shading.
- GCodeViewer.cpp: kept upstream's dim-previous-layers setup and belt's
  exemption from the same-result early return.
- TreeSupport.cpp: upstream's >= 0 roof-layer fix inside belt's
  belt-floor branch.
- calib.cpp / GCode.hpp / GCodeWriter.{cpp,hpp} / Print.hpp: upstream's
  additions adapted to belt's pointer-held writer and helpers.
- Custom.json: kept profile version 02.04.00.03 (belt) over upstream's
  02.04.00.01; both bumped from 02.04.00.00.

Building this tree needs the wxInspector dependency, which upstream
added in the interim (python3 and wxWidgets 3.3.2 were already present
in the shared deps prefix).
This commit is contained in:
harrierpigeon
2026-08-02 16:09:27 -05:00
9423 changed files with 2619356 additions and 1294333 deletions
+242 -77
View File
@@ -3,6 +3,8 @@
#include "Geometry.hpp"
#include "I18N.hpp"
#include "PrintConfig.hpp"
#include "ClipperUtils.hpp"
#include "Line.hpp"
#include <algorithm>
#include <iomanip>
#include <iostream>
@@ -59,6 +61,39 @@ bool GCodeWriter::supports_separate_travel_acceleration(GCodeFlavor flavor)
void GCodeWriter::apply_print_config(const PrintConfig &print_config)
{
this->config.apply(print_config, true);
// Some machine limits are stride-2 (normal, silent) pairs, here we extract the value that will be used,
// which is always normal mode at the moment
// TODO: support silent? Any printer actually have that?
auto get_machine_limits = [](const std::string key, const ConfigOptionFloats& opt) -> std::vector<double> {
unsigned int stride = 1;
unsigned int offset = 0;
if (printer_options_with_variant_2.count(key) > 0) {
stride = 2;
// offset = <TODO: current print mode>;
}
std::vector<double> results;
results.reserve(opt.values.size() / stride);
for (unsigned int i = offset; i < opt.values.size(); i += stride) {
results.emplace_back(opt.values[i]);
}
return results;
};
auto rounded = [](std::vector<double>&& vec) -> std::vector<double>&&{
std::transform(vec.cbegin(), vec.cend(), vec.begin(), [](const double v) { return std::round(v); });
return std::move(vec);
};
auto to_uint = [](const std::vector<double>& vec) {
std::vector<unsigned int> r;
std::transform(vec.begin(), vec.end(), std::back_inserter(r), [](const double v) { return static_cast<unsigned int>(v); });
return r;
};
#define LIMITS(OPT) get_machine_limits(#OPT, print_config.OPT)
#define LIMITS_UINT(OPT) to_uint(rounded(LIMITS(OPT)))
m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
bool use_mach_limits = print_config.gcode_flavor.value == gcfMarlinLegacy || print_config.gcode_flavor.value == gcfMarlinFirmware ||
print_config.gcode_flavor.value == gcfKlipper || print_config.gcode_flavor.value == gcfRepRapFirmware;
@@ -66,29 +101,118 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config)
// For Klipper, SET_VELOCITY_LIMIT ACCEL= applies to all moves, so the effective cap
// is the minimum of the extruding limit and the per-axis X/Y limits.
// This ensures user-configured Motion Ability limits are honoured (#12244).
unsigned int extruding_limit = std::lrint(print_config.machine_max_acceleration_extruding.values.front());
auto extruding_limit = LIMITS_UINT(machine_max_acceleration_extruding);
if (print_config.gcode_flavor.value == gcfKlipper) {
unsigned int x_limit = std::lrint(print_config.machine_max_acceleration_x.values.front());
unsigned int y_limit = std::lrint(print_config.machine_max_acceleration_y.values.front());
if (x_limit > 0) extruding_limit = std::min(extruding_limit, x_limit);
if (y_limit > 0) extruding_limit = std::min(extruding_limit, y_limit);
auto x_limit = LIMITS_UINT(machine_max_acceleration_x);
auto y_limit = LIMITS_UINT(machine_max_acceleration_y);
for (size_t i = 0; i < extruding_limit.size(); i++) {
if (x_limit[i] > 0) extruding_limit[i] = std::min(extruding_limit[i], x_limit[i]);
if (y_limit[i] > 0) extruding_limit[i] = std::min(extruding_limit[i], y_limit[i]);
}
}
m_max_acceleration = extruding_limit;
m_max_acceleration = std::move(extruding_limit);
} else {
m_max_acceleration = 0;
m_max_acceleration.clear();
}
if (use_mach_limits && supports_separate_travel_acceleration(print_config.gcode_flavor.value)) {
m_max_travel_acceleration = LIMITS_UINT(machine_max_acceleration_travel);
} else {
m_max_travel_acceleration.clear();
}
m_max_travel_acceleration = static_cast<unsigned int>(
std::round((use_mach_limits && supports_separate_travel_acceleration(print_config.gcode_flavor.value)) ?
print_config.machine_max_acceleration_travel.values.front() :
0));
if (use_mach_limits) {
m_max_jerk_x = std::lrint(print_config.machine_max_jerk_x.values.front());
m_max_jerk_y = std::lrint(print_config.machine_max_jerk_y.values.front());
m_max_junction_deviation = (print_config.machine_max_junction_deviation.values.front());
};
m_max_jerk_z = print_config.machine_max_jerk_z.values.front();
m_max_jerk_e = print_config.machine_max_jerk_e.values.front();
m_max_jerk_x = rounded(LIMITS(machine_max_jerk_x));
m_max_jerk_y = rounded(LIMITS(machine_max_jerk_y));
m_max_junction_deviation = LIMITS(machine_max_junction_deviation);
} else {
m_max_jerk_x.clear();
m_max_jerk_y.clear();
m_max_junction_deviation.clear();
}
m_max_jerk_z = LIMITS(machine_max_jerk_z);
m_max_jerk_e = LIMITS(machine_max_jerk_e);
m_resolution = print_config.resolution.value;
#undef LIMITS
#undef LIMITS_UINT
// Orca: capture the printable area(s) so a spiral lift can be skipped when its
// circle would leave the boundary and collide with the print limits. Full polygons
// are stored (not a bounding box) so the check stays correct for non-rectangular
// beds, and per-extruder areas are kept so printers with different boundaries per
// extruder use the right limit for whichever extruder is active.
auto to_scaled_polygon = [](const Pointfs &pts) {
Polygon poly;
poly.points.reserve(pts.size());
for (const Vec2d &p : pts)
poly.points.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
poly.make_counter_clockwise();
return poly;
};
m_bed_printable_area.points.clear();
m_extruder_printable_areas.clear();
if (print_config.printable_area.values.size() >= 3)
m_bed_printable_area = to_scaled_polygon(print_config.printable_area.values);
const std::vector<Pointfs> &extruder_areas = print_config.extruder_printable_area.values;
if (!extruder_areas.empty()) {
m_extruder_printable_areas.resize(extruder_areas.size());
for (size_t i = 0; i < extruder_areas.size(); ++i) {
if (extruder_areas[i].size() < 3) {
// No dedicated area for this extruder: it can reach the whole bed.
m_extruder_printable_areas[i] = m_bed_printable_area;
continue;
}
Polygon extruder_poly = to_scaled_polygon(extruder_areas[i]);
if (m_bed_printable_area.points.size() < 3) {
m_extruder_printable_areas[i] = std::move(extruder_poly);
continue;
}
// The reachable area is the extruder area clipped to the bed. Bed shapes are
// convex in practice, so keep the largest resulting contour.
Polygons clipped = intersection(extruder_poly, m_bed_printable_area);
const Polygon *largest = nullptr;
double best_area = 0.;
for (const Polygon &p : clipped) {
double a = std::abs(p.area());
if (a > best_area) { best_area = a; largest = &p; }
}
m_extruder_printable_areas[i] = largest ? *largest : std::move(extruder_poly);
}
}
}
const Polygon *GCodeWriter::active_printable_area() const
{
if (const Extruder *e = this->filament()) {
size_t id = e->extruder_id();
if (id < m_extruder_printable_areas.size() && m_extruder_printable_areas[id].points.size() >= 3)
return &m_extruder_printable_areas[id];
}
if (m_bed_printable_area.points.size() >= 3)
return &m_bed_printable_area;
return nullptr;
}
bool GCodeWriter::spiral_lift_fits_printable_area(const Vec2d &center, double radius) const
{
const Polygon *area = this->active_printable_area();
if (area == nullptr)
return true; // Boundary unknown: don't restrict (preserve previous behavior).
const Point c = Point::new_scale(center.x(), center.y());
const double r_scaled = scale_(radius);
const double r2 = r_scaled * r_scaled;
// The spiral traces a full circle of `radius` around `center`, so the center must lie
// inside the printable area and every edge must be at least `radius` away from it.
if (!area->contains(c))
return false;
const Points &pts = area->points;
for (size_t i = 0, n = pts.size(); i < n; ++i)
if (Line::distance_to_squared(c, pts[i], pts[(i + 1) % n]) < r2)
return false;
return true;
}
void GCodeWriter::set_extruders(std::vector<unsigned int> extruder_ids)
@@ -97,6 +221,7 @@ void GCodeWriter::set_extruders(std::vector<unsigned int> extruder_ids)
m_filament_extruders.clear();
//ORCA: Reset current extruder ID and clear pointers to prevent dangling pointers when extruders are recreated.
m_curr_extruder_id = -1;
m_cached_extruder_idx = 0;
std::fill(m_curr_filament_extruder.begin(), m_curr_filament_extruder.end(), nullptr);
m_filament_extruders.reserve(extruder_ids.size());
for (unsigned int extruder_id : extruder_ids)
@@ -243,14 +368,18 @@ std::string GCodeWriter::set_chamber_temperature(int temperature, bool wait)
return gcode.str();
}
#define EXTRUDER_LIMIT(OPT) \
(filament() ? ((OPT).size() <= filament()->extruder_id() ? 0 : (OPT)[filament()->extruder_id()]) : \
((OPT).empty() ? 0 : *std::max_element((OPT).cbegin(), (OPT).cend())))
// copied from PrusaSlicer
std::string GCodeWriter::set_acceleration_internal(Acceleration type, unsigned int acceleration)
{
// Clamp the acceleration to the allowed maximum.
if (type == Acceleration::Print && m_max_acceleration > 0 && acceleration > m_max_acceleration)
acceleration = m_max_acceleration;
if (type == Acceleration::Travel && m_max_travel_acceleration > 0 && acceleration > m_max_travel_acceleration)
acceleration = m_max_travel_acceleration;
if (type == Acceleration::Print && EXTRUDER_LIMIT(m_max_acceleration) > 0 && acceleration > EXTRUDER_LIMIT(m_max_acceleration))
acceleration = EXTRUDER_LIMIT(m_max_acceleration);
if (type == Acceleration::Travel && EXTRUDER_LIMIT(m_max_travel_acceleration) > 0 && acceleration > EXTRUDER_LIMIT(m_max_travel_acceleration))
acceleration = EXTRUDER_LIMIT(m_max_travel_acceleration);
// Are we setting travel acceleration for a flavour that supports separate travel and print acc?
bool separate_travel = (type == Acceleration::Travel && supports_separate_travel_acceleration(this->config.gcode_flavor));
@@ -293,10 +422,10 @@ std::string GCodeWriter::set_jerk_xy(double jerk)
std::ostringstream gcode;
if (FLAVOR_IS(gcfKlipper)) {
// Clamp the jerk to the allowed maximum.
if (m_max_jerk_x > 0 && jerk > m_max_jerk_x)
jerk = m_max_jerk_x;
if (m_max_jerk_y > 0 && jerk > m_max_jerk_y)
jerk = m_max_jerk_y;
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);
gcode << "SET_VELOCITY_LIMIT SQUARE_CORNER_VELOCITY=" << jerk;
@@ -305,12 +434,12 @@ std::string GCodeWriter::set_jerk_xy(double jerk)
double jerk_xy = jerk;
// Clamp against the X machine limit
if (m_max_jerk_x > 0 && jerk_xy > m_max_jerk_x)
jerk_xy = m_max_jerk_x;
if (EXTRUDER_LIMIT(m_max_jerk_x) > 0 && jerk_xy > EXTRUDER_LIMIT(m_max_jerk_x))
jerk_xy = EXTRUDER_LIMIT(m_max_jerk_x);
// Clamp against the Y machine limit as well to be safe
if (m_max_jerk_y > 0 && jerk_xy > m_max_jerk_y)
jerk_xy = m_max_jerk_y;
if (EXTRUDER_LIMIT(m_max_jerk_y) > 0 && jerk_xy > EXTRUDER_LIMIT(m_max_jerk_y))
jerk_xy = EXTRUDER_LIMIT(m_max_jerk_y);
// Output the lowest safe limit using ONLY the X parameter
gcode << "M207 X" << jerk_xy;
@@ -318,16 +447,16 @@ std::string GCodeWriter::set_jerk_xy(double jerk)
double jerk_x = jerk;
double jerk_y = jerk;
// Clamp the axis jerk to the allowed maximum.
if (m_max_jerk_x > 0 && jerk > m_max_jerk_x)
jerk_x = m_max_jerk_x;
if (m_max_jerk_y > 0 && jerk > m_max_jerk_y)
jerk_y = m_max_jerk_y;
if (EXTRUDER_LIMIT(m_max_jerk_x) > 0 && jerk > EXTRUDER_LIMIT(m_max_jerk_x))
jerk_x = EXTRUDER_LIMIT(m_max_jerk_x);
if (EXTRUDER_LIMIT(m_max_jerk_y) > 0 && jerk > EXTRUDER_LIMIT(m_max_jerk_y))
jerk_y = EXTRUDER_LIMIT(m_max_jerk_y);
gcode << "M205 X" << jerk_x << " Y" << jerk_y;
}
//the is_bbl check should be in the else statement above so that it doesn't inadverently added Z & E to klipper
if (m_is_bbl_printers)
gcode << std::setprecision(2) << " Z" << m_max_jerk_z << " E" << m_max_jerk_e;
gcode << std::setprecision(2) << " Z" << EXTRUDER_LIMIT(m_max_jerk_z) << " E" << EXTRUDER_LIMIT(m_max_jerk_e);
if (GCodeWriter::full_gcode_comment) gcode << " ; adjust jerk";
gcode << "\n";
@@ -343,8 +472,8 @@ std::string GCodeWriter::set_accel_and_jerk(unsigned int acceleration, double je
throw std::runtime_error(_u8L("set_accel_and_jerk() is only supported by Klipper"));
// Clamp the acceleration to the allowed maximum.
if (m_max_acceleration > 0 && acceleration > m_max_acceleration)
acceleration = m_max_acceleration;
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;
@@ -358,10 +487,10 @@ std::string GCodeWriter::set_accel_and_jerk(unsigned int acceleration, double je
is_empty = false;
}
// Clamp the jerk to the allowed maximum.
if (m_max_jerk_x > 0 && jerk > m_max_jerk_x)
jerk = m_max_jerk_x;
if (m_max_jerk_y > 0 && jerk > m_max_jerk_y)
jerk = m_max_jerk_y;
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;
@@ -382,13 +511,13 @@ std::string GCodeWriter::set_accel_and_jerk(unsigned int acceleration, double je
std::string GCodeWriter::set_junction_deviation(double junction_deviation){
std::ostringstream gcode;
if (FLAVOR_IS(gcfMarlinFirmware) && m_max_junction_deviation > 0 && junction_deviation > 0) {
if (FLAVOR_IS(gcfMarlinFirmware) && EXTRUDER_LIMIT(m_max_junction_deviation) > 0 && junction_deviation > 0) {
// Clamp the junction deviation to the allowed maximum.
gcode << "M205 J";
if (junction_deviation <= m_max_junction_deviation) {
if (junction_deviation <= EXTRUDER_LIMIT(m_max_junction_deviation)) {
gcode << std::fixed << std::setprecision(3) << junction_deviation;
} else {
gcode << std::fixed << std::setprecision(3) << m_max_junction_deviation;
gcode << std::fixed << std::setprecision(3) << EXTRUDER_LIMIT(m_max_junction_deviation);
}
if (GCodeWriter::full_gcode_comment) {
gcode << " ; Junction Deviation";
@@ -577,36 +706,36 @@ std::string GCodeWriter::update_progress(unsigned int num, unsigned int tot, boo
std::string GCodeWriter::toolchange_prefix() const
{
std::string gcode = "T";
// Orca: the manual-filament-change tag must stay ahead of the flavor selection so
// MMU manual-change handling keeps working.
if (config.manual_filament_change)
gcode = ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Manual_Tool_Change) + "T";
else {
if (m_is_bbl_printers)
gcode = "M1020 S";
else {
if (FLAVOR_IS(gcfMakerWare))
gcode = "M135 T";
else if (FLAVOR_IS(gcfSailfish))
gcode = "M108 T";
}
}
return gcode;
return ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Manual_Tool_Change) + "T";
return FLAVOR_IS(gcfMakerWare) ? "M135 T" :
FLAVOR_IS(gcfSailfish) ? "M108 T" : "T";
}
std::string GCodeWriter::toolchange(unsigned int filament_id)
std::string GCodeWriter::toolchange(unsigned int filament_id, int nozzle_id)
{
// set the new extruder
auto filament_extruder_iter = Slic3r::lower_bound_by_predicate(m_filament_extruders.begin(), m_filament_extruders.end(), [filament_id](const Extruder &e) { return e.id() < filament_id; });
assert(filament_extruder_iter != m_filament_extruders.end() && filament_extruder_iter->id() == filament_id);
m_curr_extruder_id = filament_extruder_iter->extruder_id();
m_curr_filament_extruder[m_curr_extruder_id] = &*filament_extruder_iter;
m_cached_extruder_idx = get_extruder_index(this->config, filament_id);
// return the toolchange command
// if we are running a single-extruder setup, just set the extruder and return nothing
std::ostringstream gcode;
// Orca: also emit for non-BBL single-extruder multi-filament setups (MMU-style).
if (this->multiple_extruders || (this->config.filament_diameter.values.size() > 1 && !is_bbl_printers())) {
// Orca: call toolchange_prefix() to get the correct command prefix based on the configuration and flavor.
gcode << this->toolchange_prefix() << filament_id;
// Orca: manual filament change keeps its tag line even on BBL machines, so the
// M1020 form must not shadow it. nozzle_id is signed: the null-safe nozzle
// lookup legitimately yields -1 ("no specific nozzle"), matching the literal
// H-1 the stock change templates emit; an unsigned would wrap.
if (m_is_bbl_printers && !config.manual_filament_change)
gcode << "M1020 S" << filament_id << " H" << nozzle_id;
else
gcode << this->toolchange_prefix() << filament_id;
if (GCodeWriter::full_gcode_comment)
gcode << " ; change extruder";
gcode << "\n";
@@ -615,6 +744,25 @@ std::string GCodeWriter::toolchange(unsigned int filament_id)
return gcode.str();
}
// Current parked-retract length of the filament's extruder, share-aware. m_filament_extruders is
// sorted by id (see toolchange), so a lower_bound lookup finds the entry; unknown filament ids
// degrade to 0 rather than dereferencing end().
double GCodeWriter::get_extruder_retracted_length(const int filament_id)
{
double res = 0.0;
auto filament_extruder_iter = Slic3r::lower_bound_by_predicate(m_filament_extruders.begin(), m_filament_extruders.end(),
[filament_id](const Extruder &e) { return (int) e.id() < filament_id; });
if (filament_extruder_iter == m_filament_extruders.end() || (int) filament_extruder_iter->id() != filament_id)
return res;
if (filament_extruder_iter->is_share_extruder())
res = filament_extruder_iter->get_share_retracted_length();
else
res = filament_extruder_iter->get_single_retracted_length();
return res;
}
std::string GCodeWriter::set_speed(double F, const std::string &comment, const std::string &cooling_marker)
{
assert(F > 0.);
@@ -647,7 +795,7 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com
w.emit_xy(point_on_plate);
}
auto speed = m_is_first_layer
? this->config.get_abs_value("initial_layer_travel_speed") : this->config.travel_speed.value;
? this->config.get_abs_value_at("initial_layer_travel_speed", m_cached_extruder_idx) : this->config.travel_speed.get_at(m_cached_extruder_idx);
w.emit_f(speed * 60.0);
//BBS
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
@@ -701,14 +849,19 @@ std::string GCodeWriter::eager_lift(const LiftType type) {
}
// BBS: spiral lift only safe with known position
// TODO: check the arc will move within bed area
if (effective_type == LiftType::SpiralLift && this->is_current_position_clear()) {
double radius = target_lift / (2 * PI * atan(filament()->travel_slope()));
// static spiral alignment when no move in x,y plane.
// spiral centra is a radius distance to the right (y=0)
// spiral centra is a radius distance to the right (y=0)
Vec2d ij_offset = { radius, 0 };
if (target_lift > 0) {
// Orca: keep the spiral inside the active extruder's printable area, otherwise
// fall back to a normal lift to avoid colliding with the print boundary. m_pos
// includes the plate offset, so remove it to match the printable area coordinates.
const Vec2d spiral_center = { m_pos.x() - m_x_offset + ij_offset.x(), m_pos.y() - m_y_offset + ij_offset.y() };
if (target_lift > 0 && this->spiral_lift_fits_printable_area(spiral_center, radius)) {
lift_move = this->_spiral_travel_to_z(m_pos(2) + target_lift, ij_offset, "spiral lift Z");
} else if (target_lift > 0) {
lift_move = _travel_to_z(m_pos(2) + target_lift, "normal lift Z");
}
}
//BBS: if position is unknown use normal lift
@@ -734,7 +887,7 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
// BBS
Vec3d dest_point = point;
auto travel_speed =
m_is_first_layer ? this->config.get_abs_value("initial_layer_travel_speed") : this->config.travel_speed.value;
m_is_first_layer ? this->config.get_abs_value_at("initial_layer_travel_speed", m_cached_extruder_idx) : this->config.travel_speed.get_at(m_cached_extruder_idx);
//BBS: a z_hop need to be handle when travel
if (std::abs(m_to_lift) > EPSILON) {
assert(std::abs(m_lifted) < EPSILON);
@@ -763,7 +916,15 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
double radius = delta(2) / (2 * PI * atan(this->filament()->travel_slope()));
Vec2d ij_offset = radius * delta_no_z.normalized();
ij_offset = { -ij_offset(1), ij_offset(0) };
slop_move = this->_spiral_travel_to_z(target(2), ij_offset, "spiral lift Z");
// Orca: only perform the spiral lift if its full circle stays inside the
// printable area of the active extruder, otherwise fall back to a normal
// lift to avoid colliding with the print boundary. `source` is already in
// bed coordinates (plate offset removed), matching the printable area.
const Vec2d spiral_center = { source.x() + ij_offset.x(), source.y() + ij_offset.y() };
if (this->spiral_lift_fits_printable_area(spiral_center, radius))
slop_move = this->_spiral_travel_to_z(target(2), ij_offset, "spiral lift Z");
else
slop_move = _travel_to_z(target.z(), "normal lift Z");
}
//BBS: SlopeLift
else if (m_to_lift_type == LiftType::SlopeLift &&
@@ -831,13 +992,13 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
{
//force to move xy first then z after filament change
w.emit_xy(Vec2d(point_on_plate.x(), point_on_plate.y()));
w.emit_f(this->config.travel_speed.value * 60.0);
w.emit_f(this->config.travel_speed.get_at(m_cached_extruder_idx) * 60.0);
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
out_string = w.string() + _travel_to_z(point_on_plate.z(), comment);
} else {
GCodeG1Formatter w;
w.emit_xyz(point_on_plate);
w.emit_f(this->config.travel_speed.value * 60.0);
w.emit_f(this->config.travel_speed.get_at(m_cached_extruder_idx) * 60.0);
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
out_string = w.string();
}
@@ -870,10 +1031,10 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment)
{
m_pos(2) = z;
double speed = this->config.travel_speed_z.value;
double speed = this->config.travel_speed_z.get_at(m_cached_extruder_idx);
if (speed == 0.) {
speed = m_is_first_layer ? this->config.get_abs_value("initial_layer_travel_speed")
: this->config.travel_speed.value;
speed = m_is_first_layer ? this->config.get_abs_value_at("initial_layer_travel_speed", m_cached_extruder_idx)
: this->config.travel_speed.get_at(m_cached_extruder_idx);
}
GCodeG1Formatter w;
@@ -893,11 +1054,11 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment)
std::string GCodeWriter::_spiral_travel_to_z(double z, const Vec2d &ij_offset, const std::string &comment)
{
std::string output;
double speed = this->config.travel_speed_z.value;
double speed = this->config.travel_speed_z.get_at(m_cached_extruder_idx);
if (speed == 0.) {
speed = m_is_first_layer ? this->config.get_abs_value("initial_layer_travel_speed")
: this->config.travel_speed.value;
speed = m_is_first_layer ? this->config.get_abs_value_at("initial_layer_travel_speed", m_cached_extruder_idx)
: this->config.travel_speed.get_at(m_cached_extruder_idx);
}
if (!this->config.enable_arc_fitting) { // Orca: if arc fitting is disabled, approximate the arc with small linear segments
@@ -1105,7 +1266,7 @@ std::string GCodeWriter::_retract(double length, double restart_extra, const std
return gcode;
}
std::string GCodeWriter::unretract()
std::string GCodeWriter::unretract(float extra_retract)
{
std::string gcode;
@@ -1121,7 +1282,9 @@ std::string GCodeWriter::unretract()
//BBS
// use G1 instead of G0 because G0 will blend the restart with the previous travel move
GCodeG1Formatter w;
w.emit_e(filament()->E());
// extra_retract over-extrudes for the PETG pre-extrusion; 0 by
// default -> identical to the plain deretract E position.
w.emit_e(filament()->E() + extra_retract);
w.emit_f(filament()->deretract_speed() * 60.);
//BBS
w.emit_comment(GCodeWriter::full_gcode_comment, " ; unretract");
@@ -1254,8 +1417,9 @@ std::string GCodeWriter::set_extruder(unsigned int filament_id)
auto filament_ext_it = Slic3r::lower_bound_by_predicate(m_filament_extruders.begin(), m_filament_extruders.end(), [filament_id](const Extruder &e) { return e.id() < filament_id; });
unsigned int extruder_id = filament_ext_it->extruder_id();
assert(filament_ext_it != m_filament_extruders.end() && filament_ext_it->id() == filament_id);
//TODO: optmize here, pass extruder_id to toolchange
return this->need_toolchange(filament_id) ? this->toolchange(filament_id) : "";
// Orca: writer-only context (calibration paths) has no nozzle grouping; the
// filament's own extruder id is the correct degenerate nozzle value.
return this->need_toolchange(filament_id) ? this->toolchange(filament_id, (int) extruder_id) : "";
}
void GCodeWriter::init_extruder(unsigned int filament_id)
@@ -1265,6 +1429,7 @@ void GCodeWriter::init_extruder(unsigned int filament_id)
assert(filament_extruder_iter != m_filament_extruders.end() && filament_extruder_iter->id() == filament_id);
m_curr_extruder_id = filament_extruder_iter->extruder_id();
m_curr_filament_extruder[m_curr_extruder_id] = &*filament_extruder_iter;
m_cached_extruder_idx = get_extruder_index(this->config, filament_id);
}
}