Merge upstream/hanif/belt-printer-fixes into belt/final-round

Brings in upstream/belt-printer (the Sept 14 main merge) plus Hanif Koh's
21 review-fix commits from PR #15685, on top of the MachineKinematics
refactor and the purge-prism / tree-support / first-layer-speed fixes.

Conflict resolution:
- BeltGCodeWriter is gone (kinematics refactor), so Hanif's plate-offset
  fix for it is ported into GCodeWriter: the first-layer-plane checks in
  travel_to_xy / travel_to_xyz / _travel_to_z now evaluate the plate-local
  point, and BeltGCode::init_belt_writer hands the stored plate origin to
  the writer it installs.
- init_belt_writer(Print&) takes Hanif's signature; the BBL flag is set on
  the surviving writer by GCode::_do_export.
- The shared emit_belt_brim_bands() loop keeps the BeltFloorObjectGuard the
  local branch added, so apron bands classify first-layer height against
  their own object.
- eager_lift keeps effective_type: it now carries set_force_normal_lift().
- GCodeWriter's initializer list follows Hanif's member order with
  m_kinematics in its declared position.
- TreeSupport::detect_overhangs uses Hanif's clamped build_plate_tilt_slope()
  for the non-belt path and the belt shear for the belt path.
This commit is contained in:
harrierpigeon
2026-09-30 15:22:19 -05:00
4942 changed files with 62842 additions and 21578 deletions
+31 -46
View File
@@ -2962,10 +2962,9 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
m_fan_mover.release();
m_ordering_cache.clear();
m_writer->set_is_bbl_machine(is_bbl_printers);
// Belt printer: initialize belt-specific writer via virtual hook.
this->init_belt_writer(print, is_bbl_printers);
this->init_belt_writer(print);
m_writer->set_is_bbl_machine(is_bbl_printers);
// Standalone axis remap (works with or without belt mode).
// Sync the writer's remap state to the current export UNCONDITIONALLY — even at
@@ -5350,8 +5349,7 @@ LayerResult GCode::process_belt_brim_layer(
// skip these layers entirely.
{
char buf[64];
sprintf(buf, ";%s%g\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change).c_str(), print_z);
gcode += buf;
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change) + "\n";
sprintf(buf, ";Z:%g\n", print_z);
gcode += buf;
const float band_height = float(height);
@@ -5382,7 +5380,12 @@ LayerResult GCode::process_belt_brim_layer(
print.config().layer_change_gcode.value, m_writer->filament()->id(), &config) + "\n";
}
gcode += this->emit_belt_brim_bands(print, layers, single_object_instance_idx);
// Objects sharing this apron Z may use different brim filaments; print each in its own tool.
for (const unsigned int brim_extruder : layer_tools.extruders) {
if (m_writer->filament() == nullptr || m_writer->filament()->id() != brim_extruder)
gcode += this->set_extruder(brim_extruder, print_z);
gcode += this->emit_belt_brim_bands(print, layers, single_object_instance_idx, brim_extruder);
}
result.gcode = std::move(gcode);
return result;
@@ -5396,7 +5399,8 @@ LayerResult GCode::process_belt_brim_layer(
// object layer, takes the ordinary path, and the band would be silently dropped.
std::string GCode::emit_belt_brim_bands(const Print &print,
const std::vector<LayerToPrint> &layers,
const size_t single_object_instance_idx)
const size_t single_object_instance_idx,
const unsigned int extruder_id)
{
std::string gcode;
for (const LayerToPrint &ltp : layers) {
@@ -5404,9 +5408,15 @@ std::string GCode::emit_belt_brim_bands(const Print &print,
if (band == nullptr || band->fills.empty() || ltp.original_object == nullptr)
continue;
const PrintObject &object = *ltp.original_object;
// belt_brim_filament() is 1-based.
if (! object.has_belt_brim() || static_cast<unsigned int>(object.belt_brim_filament() - 1) != extruder_id)
continue;
// Speeds, flow and retraction all read m_config.
m_config.apply(print.default_region_config());
m_config.apply(object.config(), true);
// Apron bands have no Layer at all (m_layer is null here), so the belt
// floor owner has to be named the same way the object brim names it.
BeltFloorObjectGuard floor_owner{ m_belt_floor_object, &object };
const size_t i_begin = single_object_instance_idx == size_t(-1) ? 0 : single_object_instance_idx;
const size_t i_end = single_object_instance_idx == size_t(-1) ? object.instances().size()
: single_object_instance_idx + 1;
@@ -6631,42 +6641,11 @@ LayerResult GCode::process_layer(
std::set<std::pair<size_t, size_t>> belt_brim_emitted;
// Emit every ORDINARY-layer apron band (belt_brim_prologue band coinciding with an
// object/support layer) whose brim filament is this pass's extruder. Mirrors
// emit_belt_brim_bands() per band, but filtered to one brim filament so each band
// object/support layer) whose brim filament is this pass's extruder, so each band
// prints in the correct tool's pass (Finding B). extruder_id is 0-based (the
// reindexed tool domain); belt_brim_filament() is 1-based, so subtract one.
// reindexed tool domain).
auto emit_belt_brim_for_extruder = [this, &print, &layers, single_object_instance_idx](unsigned int extruder_id) -> std::string {
std::string gc;
for (const LayerToPrint &ltp : layers) {
const BeltBrimBand *band = ltp.belt_brim_band;
if (band == nullptr || band->fills.empty() || ltp.original_object == nullptr)
continue;
const PrintObject &object = *ltp.original_object;
if (! object.has_belt_brim() || (unsigned int)(object.belt_brim_filament() - 1) != extruder_id)
continue;
// Speeds, flow and retraction all read m_config.
m_config.apply(print.default_region_config());
m_config.apply(object.config(), true);
// Apron bands have no Layer at all (m_layer is null here), so the belt
// floor owner has to be named the same way the object brim names it.
BeltFloorObjectGuard floor_owner{ m_belt_floor_object, &object };
const size_t i_begin = single_object_instance_idx == size_t(-1) ? 0 : single_object_instance_idx;
const size_t i_end = single_object_instance_idx == size_t(-1) ? object.instances().size()
: single_object_instance_idx + 1;
for (size_t i = i_begin; i < i_end && i < object.instances().size(); ++ i) {
// Band geometry is object-local, like the object's own extrusions.
const Point &offset = object.instances()[i].shift;
this->set_origin(unscale(offset));
this->on_set_origin(&object, offset);
m_avoid_crossing_perimeters.use_external_mp();
for (const ExtrusionEntity *ee : band->fills.entities)
if (ee != nullptr)
gc += this->extrude_entity(*ee, "brim", NOZZLE_CONFIG(support_speed));
m_avoid_crossing_perimeters.use_external_mp(false);
m_avoid_crossing_perimeters.disable_once();
}
}
return gc;
return this->emit_belt_brim_bands(print, layers, single_object_instance_idx, extruder_id);
};
for (unsigned int extruder_id : layer_tools.extruders)
@@ -6699,8 +6678,13 @@ LayerResult GCode::process_layer(
all_label_ids.insert(inst.label_object_id);
break;
}
std::vector<size_t> filament_instances_id(all_label_ids.begin(), all_label_ids.end());
m_filament_instances_code = _encode_label_ids_to_base64(filament_instances_id);
// Orca: A scheduled extruder may have no object instances on this layer.
// Clear any pending mask so it cannot be emitted for the wrong toolchange.
m_filament_instances_code.clear();
if (!all_label_ids.empty()) {
std::vector<size_t> filament_instances_id(all_label_ids.begin(), all_label_ids.end());
m_filament_instances_code = _encode_label_ids_to_base64(filament_instances_id);
}
}
// The inline _extrude hook may already have taken the snapshot mid-extrusion on a
@@ -8675,9 +8659,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
}
variable_speed = std::any_of(new_points.begin(), new_points.end(),
[speed](const ProcessedPoint &p) { return fabs(double(p.speed) - speed) > 1; }); // Ignore small speed variations (under 1mm/sec)
if (!NOZZLE_CONFIG(enable_overhang_speed) && FILAMENT_CONFIG(enable_overhang_bridge_fan) && m_enable_cooling_markers) {
for (ProcessedPoint &point : new_points)
point.speed = speed;
if (FILAMENT_CONFIG(enable_overhang_bridge_fan) && m_enable_cooling_markers) {
if (!NOZZLE_CONFIG(enable_overhang_speed))
for (ProcessedPoint &point : new_points)
point.speed = speed;
variable_speed = new_points.size() > 1;
}
}