mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-07 01:57:38 +00:00
Fix belt brim emission: dropped first-contact bands, tool selection, inner-only predicate (A,B,C,D)
- Emit coincident belt_brim_by_layer bands even when the leading object layer has no InstanceVisit (zero-extrusion lead-in / no coinciding support), so the brim at first belt contact is no longer dropped. - Register each coincident band's brim filament in ToolOrdering and emit each band exactly once, in its brim-filament pass; emit ordinary-layer aprons in the brim pass before object extrusion (correct tool, brim-first) instead of with whatever tool was active. - has_belt_brim(): inner-only brims need brim_width>0 (leading/extra produce no inner geometry), fixing spurious prime-tower/spiral rejection; mirror in wants_brim. Single-extruder/single-object output is unchanged except previously-dropped bands now print.
This commit is contained in:
@@ -5891,16 +5891,12 @@ LayerResult GCode::process_layer(
|
||||
//BBS: set layer time fan speed after layer change gcode
|
||||
gcode += ";_SET_FAN_SPEED_CHANGING_LAYER\n";
|
||||
|
||||
// Belt printers: an apron band prints below its own object's first layer, but with
|
||||
// several objects on the belt another one can already be printing at this print_z.
|
||||
// The layer then has an object layer and takes this ordinary path instead of the
|
||||
// brim-only branch, so the band has to be emitted here or it would be dropped.
|
||||
// Before any object extrusion at this Z, as the brim must go down first.
|
||||
if (print.has_belt_brim()) {
|
||||
const Vec2d saved_origin = m_origin;
|
||||
gcode += this->emit_belt_brim_bands(print, layers, single_object_instance_idx);
|
||||
this->set_origin(saved_origin);
|
||||
}
|
||||
// Belt printers: ordinary-layer apron bands (a band whose print_z coincides with an
|
||||
// object/support layer, so it takes this path rather than the brim-only branch) are
|
||||
// NOT emitted here anymore. They used to be laid down with whatever tool happened to
|
||||
// be active; instead they are now emitted inside the extruder loop below, in their
|
||||
// own brim-filament pass and before that pass's object extrusion, so the brim goes
|
||||
// down first with the correct tool. See the emit_belt_brim_for_extruder call.
|
||||
|
||||
//Calibration Layer-specific GCode
|
||||
// ORCA-Belt: on belt printers the calibration object is counter-rotated to
|
||||
@@ -6584,6 +6580,49 @@ LayerResult GCode::process_layer(
|
||||
|
||||
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
|
||||
m_skirt_group_done.resize(print.skirt_brim_groups().size());
|
||||
|
||||
// Belt brim bookkeeping. A coincident belt_brim_by_layer band must be emitted
|
||||
// exactly once, in its object's brim-filament pass; this records which have gone
|
||||
// down so the in-visit emit and the end-of-layer orphan sweep never double it.
|
||||
// Key = (LayerToPrint index, instance_id).
|
||||
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
|
||||
// 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.
|
||||
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 <p : 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);
|
||||
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;
|
||||
};
|
||||
|
||||
for (unsigned int extruder_id : layer_tools.extruders)
|
||||
{
|
||||
if (print.config().skirt_type == stCombined && !print.skirt_brim_groups().empty()) {
|
||||
@@ -6681,6 +6720,16 @@ LayerResult GCode::process_layer(
|
||||
if (layer_tools.has_wipe_tower && m_wipe_tower)
|
||||
m_last_processor_extrusion_role = erWipeTower;
|
||||
|
||||
// Belt printers: now that this pass's tool is selected, lay down any ordinary-layer
|
||||
// apron band whose brim filament is this extruder, before the object extrusion at
|
||||
// this Z (brim goes down first, with the correct tool). Restore the origin so the
|
||||
// object-setup code below is unaffected.
|
||||
if (print.has_belt_brim()) {
|
||||
const Vec2d saved_origin = m_origin;
|
||||
gcode += emit_belt_brim_for_extruder(extruder_id);
|
||||
this->set_origin(saved_origin);
|
||||
}
|
||||
|
||||
auto &filament_plan = filament_to_print_instances[extruder_id];
|
||||
std::vector<InstanceToPrint> &instances_to_print = filament_plan.first;
|
||||
const std::vector<InstanceVisit> &instance_visits = filament_plan.second;
|
||||
@@ -6697,8 +6746,20 @@ LayerResult GCode::process_layer(
|
||||
const LayerToPrint &layer_to_print = layers[instance_to_print.layer_id];
|
||||
if (visit.first_visit && print_wipe_extrusions == (is_anything_overridden ? 1 : 0)) {
|
||||
gcode += generate_object_skirt_group(print, instance_to_print.print_object, instance_to_print.instance_id, layer_tools, layer, extruder_id);
|
||||
gcode += generate_object_brim(print, instance_to_print.print_object, instance_to_print.instance_id, first_layer,
|
||||
layer_to_print.object_layer);
|
||||
const PrintObject &vobj = instance_to_print.print_object;
|
||||
if (vobj.has_belt_brim()) {
|
||||
// Coincident belt brim: emit once, only in this object's brim-filament
|
||||
// pass (extruder_id and belt_brim_filament()-1 are both 0-based here),
|
||||
// and dedup on the LayerToPrint index (not Layer::id()) so the orphan
|
||||
// sweep below never re-emits it.
|
||||
if (extruder_id == (unsigned int)(vobj.belt_brim_filament() - 1) &&
|
||||
belt_brim_emitted.insert({ instance_to_print.layer_id, instance_to_print.instance_id }).second)
|
||||
gcode += generate_object_brim(print, vobj, instance_to_print.instance_id, first_layer,
|
||||
layer_to_print.object_layer);
|
||||
} else {
|
||||
gcode += generate_object_brim(print, vobj, instance_to_print.instance_id, first_layer,
|
||||
layer_to_print.object_layer);
|
||||
}
|
||||
}
|
||||
|
||||
// To control print speed of the 1st object layer printed over raft interface.
|
||||
@@ -6886,6 +6947,37 @@ LayerResult GCode::process_layer(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Belt brim orphan sweep (Finding C). A coincident belt_brim_by_layer band lives on
|
||||
// an object layer, but that layer can yield no InstanceVisit above - a zero-extrusion
|
||||
// lead-in slice with no coinciding support - so the in-visit emit never fired and the
|
||||
// band would be dropped. Emit any such band exactly once here, keyed the same way as
|
||||
// the in-visit emit so already-printed bands are skipped. These orphan layers carry
|
||||
// no object material, so ending on the brim's position is harmless; we still save and
|
||||
// restore m_origin, and only toolchange when the brim filament differs from the active
|
||||
// one - a no-op on single-extruder prints, keeping their output unchanged.
|
||||
if (print.has_belt_brim()) {
|
||||
const Vec2d saved_origin = m_origin;
|
||||
for (const LayerToPrint <p : layers) {
|
||||
const PrintObject *obj = ltp.original_object;
|
||||
if (obj == nullptr || ! obj->has_belt_brim() || ltp.object_layer == nullptr)
|
||||
continue;
|
||||
const size_t ltp_idx = size_t(<p - layers.data());
|
||||
const unsigned int brim0 = (unsigned int)(obj->belt_brim_filament() - 1);
|
||||
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) ? obj->instances().size()
|
||||
: single_object_instance_idx + 1;
|
||||
for (size_t instance_id = i_begin; instance_id < i_end && instance_id < obj->instances().size(); ++ instance_id) {
|
||||
if (! belt_brim_emitted.insert({ ltp_idx, instance_id }).second)
|
||||
continue;
|
||||
if (m_writer->filament() == nullptr || m_writer->filament()->id() != brim0)
|
||||
gcode += this->set_extruder(brim0, print_z);
|
||||
gcode += generate_object_brim(print, *obj, instance_id, first_layer, ltp.object_layer);
|
||||
}
|
||||
}
|
||||
this->set_origin(saved_origin);
|
||||
}
|
||||
|
||||
if (first_layer) {
|
||||
for (auto iter = by_extruder.begin(); iter != by_extruder.end(); ++iter) {
|
||||
if (!iter->second.empty())
|
||||
|
||||
@@ -876,14 +876,9 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
|
||||
// push. Deliberately not layer_tools.has_object, which drives skirt marking
|
||||
// and wiping overrides.
|
||||
if (! object.belt_brim_prologue().empty()) {
|
||||
unsigned int brim_filament = 0;
|
||||
for (size_t i = 0; i < object.num_printing_regions(); ++ i) {
|
||||
const unsigned int f = object.printing_region(i).config().outer_wall_filament_id.value;
|
||||
if (f > 0 && (brim_filament == 0 || f < brim_filament))
|
||||
brim_filament = f;
|
||||
}
|
||||
if (brim_filament == 0)
|
||||
brim_filament = 1;
|
||||
// 1-based, same domain the object/support pushes above use; reindexed to 0-based
|
||||
// with the rest of the list later.
|
||||
const unsigned int brim_filament = object.belt_brim_filament();
|
||||
for (const BeltBrimBand &band : object.belt_brim_prologue()) {
|
||||
if (band.fills.empty())
|
||||
continue;
|
||||
@@ -893,6 +888,25 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
|
||||
}
|
||||
}
|
||||
|
||||
// Coincident brim bands (belt_brim_by_layer) print ON an object layer rather than
|
||||
// below it, but that layer can produce no InstanceVisit in process_layer - a
|
||||
// zero-extrusion lead-in slice with no coinciding support - and the band would then
|
||||
// be silently dropped. Register the brim filament on every layer that carries a
|
||||
// coincident band, in the same 1-based domain as the prologue push above, so a brim
|
||||
// pass always exists there.
|
||||
if (object.has_belt_brim()) {
|
||||
const unsigned int brim_filament = object.belt_brim_filament();
|
||||
const auto &by_layer = object.belt_brim_by_layer();
|
||||
const size_t n = std::min(by_layer.size(), object.layers().size());
|
||||
for (size_t i = 0; i < n; ++ i) {
|
||||
if (by_layer[i].empty())
|
||||
continue;
|
||||
LayerTools &layer_tools = this->tools_for_layer(object.layers()[i]->print_z);
|
||||
layer_tools.extruders.push_back(brim_filament);
|
||||
layer_tools.has_belt_brim = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& layer : m_layer_tools) {
|
||||
// Sort and remove duplicates
|
||||
sort_remove_duplicates(layer.extruders);
|
||||
@@ -940,6 +954,14 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_
|
||||
// below the object's first layer, and treating those layers as raft would put a
|
||||
// wipe tower at negative Z. Belt brim and the prime tower are mutually
|
||||
// exclusive (rejected in Print::validate()), so simply drop the clause there.
|
||||
//
|
||||
// Gate on config.belt_printer, NOT on has_belt_brim: every layer below the
|
||||
// object bottom on a belt printer is legitimately a sub-object stream - brim
|
||||
// apron, belt support printed below Z0, or the object's own lead-in - and none of
|
||||
// them is ever raft, because Print::validate() rejects raft_layers>0 on a belt
|
||||
// printer outright. Narrowing this to has_belt_brim would reclassify
|
||||
// belt-support-below-floor layers as raft on brim-less belt prints and reintroduce
|
||||
// the negative-Z wipe tower, so the broad belt_printer gate is correct.
|
||||
const bool belt_no_raft_gap = config.belt_printer.value;
|
||||
for (LayerTools < : m_layer_tools)
|
||||
lt.has_wipe_tower |= (lt.has_object && (config.timelapse_type == TimelapseType::tlSmooth || lt.wipe_tower_partitions > 0))
|
||||
|
||||
@@ -1374,9 +1374,15 @@ StringObjectException Print::validate(std::vector<StringObjectException> *warnin
|
||||
|
||||
for (const PrintObject *object : m_objects) {
|
||||
const PrintObjectConfig &ocfg = object->config();
|
||||
// Mirror PrintObject::has_belt_brim(): an inner-only brim needs a positive
|
||||
// brim_width (leading/extra widen only the outer ring), so keep this
|
||||
// predicate in step or the belt-brim warnings below would fire for a brim
|
||||
// that has_belt_brim() rejects.
|
||||
const bool wants_brim = ocfg.brim_type != btNoBrim
|
||||
&& (ocfg.brim_width.value > 0. || ocfg.leading_brim_length.value > 0.
|
||||
|| ocfg.extra_brim_width.value > 0.);
|
||||
&& (ocfg.brim_type == btInnerOnly
|
||||
? ocfg.brim_width.value > 0.
|
||||
: (ocfg.brim_width.value > 0. || ocfg.leading_brim_length.value > 0.
|
||||
|| ocfg.extra_brim_width.value > 0.));
|
||||
if (! wants_brim)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -392,6 +392,12 @@ public:
|
||||
// trimming and the spiral vase probe, and widening it would perturb belt
|
||||
// support output.
|
||||
bool has_belt_brim() const;
|
||||
// Brim filament for this object's belt brim, returned in the 1-based domain of
|
||||
// PrintRegion::outer_wall_filament_id and the raw values pushed into
|
||||
// LayerTools::extruders in ToolOrdering::collect_extruders (ToolOrdering reindexes
|
||||
// the whole list to 0-based afterwards). Lowest positive outer_wall_filament_id
|
||||
// over the printing regions, 1 if none is explicitly set.
|
||||
unsigned int belt_brim_filament() const;
|
||||
// False when this object's instances sit at different points ALONG the belt, which
|
||||
// would need a separate set of bands each. Public so validate() can explain it.
|
||||
bool belt_brim_instances_compatible() const;
|
||||
|
||||
@@ -1171,12 +1171,37 @@ bool PrintObject::has_belt_brim() const
|
||||
return false;
|
||||
if (m_config.brim_type == btNoBrim)
|
||||
return false;
|
||||
if (m_config.brim_width.value <= 0. && m_config.leading_brim_length.value <= 0.
|
||||
&& m_config.extra_brim_width.value <= 0.)
|
||||
// An inner-only brim has no leading/extra geometry: leading_brim_length and
|
||||
// extra_brim_width both widen the OUTER ring, which btInnerOnly never emits, so it
|
||||
// produces nothing unless brim_width itself is positive. Every other brim type is
|
||||
// satisfied by any one of the three widths. Requiring the width here (instead of
|
||||
// "any width") stops has_belt_brim() - and therefore Print::validate() - from
|
||||
// rejecting the prime tower / spiral vase for a brim that would never be drawn.
|
||||
if (m_config.brim_type == btInnerOnly) {
|
||||
if (m_config.brim_width.value <= 0.)
|
||||
return false;
|
||||
} else if (m_config.brim_width.value <= 0. && m_config.leading_brim_length.value <= 0.
|
||||
&& m_config.extra_brim_width.value <= 0.) {
|
||||
return false;
|
||||
}
|
||||
return ! this->has_raft();
|
||||
}
|
||||
|
||||
unsigned int PrintObject::belt_brim_filament() const
|
||||
{
|
||||
// 1-based, matching PrintRegion::outer_wall_filament_id and the raw values pushed
|
||||
// into LayerTools::extruders in ToolOrdering::collect_extruders (the whole list is
|
||||
// reindexed to 0-based later). Lowest positive outer-wall filament over the
|
||||
// printing regions; 1 when none is explicitly set.
|
||||
unsigned int brim_filament = 0;
|
||||
for (size_t i = 0; i < this->num_printing_regions(); ++ i) {
|
||||
const unsigned int f = this->printing_region(i).config().outer_wall_filament_id.value;
|
||||
if (f > 0 && (brim_filament == 0 || f < brim_filament))
|
||||
brim_filament = f;
|
||||
}
|
||||
return brim_filament == 0 ? 1u : brim_filament;
|
||||
}
|
||||
|
||||
bool PrintObject::belt_brim_instances_compatible() const
|
||||
{
|
||||
// One set of bands is shared by every instance of this object, so they must all sit at
|
||||
|
||||
Reference in New Issue
Block a user