mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-17 05:52:39 +00:00
Belt printers: brim laid onto the tilted belt, with a leading apron
A belt printer slices in a rotated frame, so the belt surface is a tilted
plane rather than the Z=0 bed plane. Each slicing layer touches the belt
only along a narrow strip at its leading edge - about 0.2mm at 45 degrees -
so a part's first layer is really a first line, with almost no contact patch
to hold it down while the belt drags it forward. Brim was hard-disabled on
belt printers, leaving no remedy at all.
Generate the brim on the belt plane instead. The object's belt footprint is
the union over layers of each slice clipped to that layer's contact band; the
brim is offset from it in a "flattened" frame where the shear axis is
stretched by 1/cos(tilt), so ordinary Clipper offsets measure true on-belt
distance. It is emitted as cross-belt lines, one per layer band, anchored to
a fixed fraction of the band so every line shares a nozzle-to-belt clearance
and therefore comes out the same width; flow is matched to the resulting band
pitch, keeping the sheet uniform and gap-free.
Three new controls, all belt-only:
* Leading brim length - extends the brim ahead of the part along the belt,
on every downhill-facing edge of its contact area. This apron necessarily
prints BELOW the object's first layer, since layer 0 is the part's leading
contact, so it needs brim-only bands of its own.
* Extra brim width - widens the brim sideways across the belt only.
* Brim type "Leading edge only" - brim at the part's first belt contact and
nothing after it. Appended last in BrimType so no existing value shifts;
degrades to an outer brim off belt printers, with a warning.
The apron bands are lightweight records rather than a Layer subclass, so no
fabricated Layer::id() can leak into initial-layer temperature selection, the
spiral vase probe, cooling or gradual interpolation. They are generated in
posSupportMaterial because their print_z values must exist before ToolOrdering
is built at psWipeTower, and they are emitted from a short dedicated branch in
process_layer that runs before any layer pointer is dereferenced.
The footprint is closed before offsetting outwards: a belt contact patch is
often a broken-up strip, and the merged offset rings of two islands closer
than 2 x brim_width would otherwise fill the space between them - space that
lies under the part.
Also fixes a pre-existing bug where PrintObject::get_first_layer_bbox()
overwrote a valid bbox with an unassigned one on any belt printer with a brim
configured, because has_brim() was true while make_brim() returned early.
Belt brim is refused alongside the prime tower and spiral vase, and requires
one instance per PrintObject - translating an instance along the belt axis
changes its physical belt-floor Z. Untilted belt printers are unchanged: they
still get no brim, since the plate brim is emitted out of skirt_brim_groups(),
which _make_skirt() never builds for a belt printer.
This commit is contained in:
+118
-1
@@ -641,6 +641,24 @@ bool Print::has_brim() const
|
||||
return std::any_of(m_objects.begin(), m_objects.end(), [](PrintObject *object) { return object->has_brim(); });
|
||||
}
|
||||
|
||||
bool Print::has_tilted_belt() const
|
||||
{
|
||||
if (! m_config.belt_printer.value)
|
||||
return false;
|
||||
// A Z rotation leaves the belt floor flat (BeltTransform forces shear = 0) and no
|
||||
// rotation at all means the machine is geometrically a flat bed.
|
||||
const BeltRotationAxis axis = m_config.belt_slice_rotation.value;
|
||||
if (axis != BeltRotationAxis::X && axis != BeltRotationAxis::Y)
|
||||
return false;
|
||||
const double tilt = std::abs(m_config.belt_slice_rotation_angle.value);
|
||||
return tilt >= BELT_BRIM_MIN_TILT_DEG && tilt <= BELT_BRIM_MAX_TILT_DEG;
|
||||
}
|
||||
|
||||
bool Print::has_belt_brim() const
|
||||
{
|
||||
return std::any_of(m_objects.begin(), m_objects.end(), [](PrintObject *object) { return object->has_belt_brim(); });
|
||||
}
|
||||
|
||||
//BBS
|
||||
std::vector<size_t> Print::layers_sorted_for_object(float start, float end, std::vector<LayerPtrs> &layers_of_objects, std::vector<BoundingBox> &boundingBox_for_objects, VecOfPoints &objects_instances_shift)
|
||||
{
|
||||
@@ -1342,6 +1360,65 @@ StringObjectException Print::validate(std::vector<StringObjectException> *warnin
|
||||
}
|
||||
if (m_config.draft_shield != dsDisabled)
|
||||
return { L("Draft shield is not compatible with belt printer mode.") };
|
||||
|
||||
// Belt brim spans many layers and owns the layers below the object, which
|
||||
// neither the prime tower nor spiral vase can share.
|
||||
if (this->has_belt_brim()) {
|
||||
if (m_config.enable_prime_tower.value)
|
||||
return { L("Brim is not compatible with the prime tower on a belt printer. "
|
||||
"Disable one of them.") };
|
||||
if (m_config.spiral_mode.value)
|
||||
return { L("Brim is not compatible with spiral vase mode on a belt printer. "
|
||||
"Disable one of them.") };
|
||||
}
|
||||
|
||||
for (const PrintObject *object : m_objects) {
|
||||
const PrintObjectConfig &ocfg = object->config();
|
||||
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.);
|
||||
if (! wants_brim)
|
||||
continue;
|
||||
|
||||
if (! this->has_tilted_belt()) {
|
||||
if (std::abs(m_config.belt_slice_rotation_angle.value) > BELT_BRIM_MAX_TILT_DEG)
|
||||
warn(L("The belt is too steep for a brim, so no brim will be generated."),
|
||||
"brim_width", object->model_object());
|
||||
else
|
||||
warn(L("A brim is only generated when the belt is tilted. Set a belt tilt angle, "
|
||||
"or remove the brim setting."),
|
||||
"brim_type", object->model_object());
|
||||
}
|
||||
|
||||
if (ocfg.brim_type == btAutoBrim || ocfg.brim_type == btEar || ocfg.brim_type == btPainted)
|
||||
warn(L("Belt printers support outer and inner brim only. Auto, Mouse ear and Painted "
|
||||
"brim are printed as Outer brim only, using Brim width."),
|
||||
"brim_type", object->model_object());
|
||||
|
||||
if (ocfg.leading_brim_length.value > 0. && ocfg.brim_object_gap.value > 0.)
|
||||
warn(L("Brim-object gap separates the leading brim from the object's leading edge, "
|
||||
"which is the edge it is meant to anchor. Set the gap to 0 when using leading "
|
||||
"brim length."),
|
||||
"brim_object_gap", object->model_object());
|
||||
|
||||
if (ocfg.leading_brim_length.value > 0. && object->instances().size() > 1)
|
||||
warn(L("This object has several instances sharing one belt position, so no brim is "
|
||||
"generated for it. Arrange the copies along the belt instead."),
|
||||
"leading_brim_length", object->model_object());
|
||||
}
|
||||
if (this->has_belt_brim() && m_objects.size() > 1)
|
||||
warn(L("Leading brim length extends ahead of each object along the belt, and Arrange does "
|
||||
"not reserve that space. Leave room between objects."),
|
||||
"leading_brim_length");
|
||||
} else {
|
||||
// "Leading edge only" describes where a part meets a moving belt, so it has no
|
||||
// meaning on a fixed bed. Brim.cpp prints it as an ordinary outer brim rather
|
||||
// than silently producing nothing; say so.
|
||||
for (const PrintObject *object : m_objects)
|
||||
if (object->config().brim_type == btLeadingEdgeOnly)
|
||||
warn(L("\"Leading edge only\" brim applies to belt printers. On this printer it is "
|
||||
"printed as an ordinary outer brim."),
|
||||
"brim_type", object->model_object());
|
||||
}
|
||||
|
||||
if (nozzles < 2 && extruders.size() > 1) {
|
||||
@@ -2251,8 +2328,28 @@ BoundingBox PrintObject::get_first_layer_bbox(float& a, float& layer_height, std
|
||||
a += area(slice);
|
||||
}
|
||||
}
|
||||
if (has_brim())
|
||||
// Guard on `defined`: make_brim() can return before assigning this (it does on
|
||||
// belt printers, where has_brim() is still true but the plate brim is skipped),
|
||||
// and overwriting a valid bbox with an undefined one corrupted the first-layer
|
||||
// centre and the GUI's first-layer area readout.
|
||||
if (has_brim() && firstLayerObjectBrimBoundingBox.defined)
|
||||
bbox = firstLayerObjectBrimBoundingBox;
|
||||
// Belt brim: the apron reaches ahead of the object along the belt.
|
||||
if (has_belt_brim()) {
|
||||
const Point shift = instances().empty() ? Point(0, 0) : instances()[0].shift_without_plate_offset();
|
||||
for (const ExPolygons &areas : m_belt_brim_areas_by_layer)
|
||||
for (const ExPolygon &ex : areas) {
|
||||
BoundingBox bb = get_extents(ex.contour);
|
||||
bb.translate(shift.x(), shift.y());
|
||||
bbox.merge(bb);
|
||||
}
|
||||
for (const BeltBrimBand &band : m_belt_brim_prologue)
|
||||
for (const ExPolygon &ex : band.areas) {
|
||||
BoundingBox bb = get_extents(ex.contour);
|
||||
bb.translate(shift.x(), shift.y());
|
||||
bbox.merge(bb);
|
||||
}
|
||||
}
|
||||
return bbox;
|
||||
}
|
||||
|
||||
@@ -2804,6 +2901,26 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
}
|
||||
|
||||
|
||||
// Belt brim: bound the first-layer convex hull by the lowest apron band, so
|
||||
// bed levelling and the initial purge line account for brim that reaches
|
||||
// ahead of every object.
|
||||
if (this->has_belt_brim()) {
|
||||
for (PrintObject *object : m_objects) {
|
||||
if (! object->has_belt_brim() || object->belt_brim_prologue().empty())
|
||||
continue;
|
||||
const BeltBrimBand &lowest = object->belt_brim_prologue().front();
|
||||
for (const PrintInstance &instance : object->instances())
|
||||
for (const ExPolygon &ex : lowest.areas) {
|
||||
Polygon poly = ex.contour;
|
||||
poly.translate(instance.shift);
|
||||
append(m_first_layer_convex_hull.points, std::move(poly.points));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unchanged for belt printers: _make_skirt() already returns early for them, and
|
||||
// the belt brim does not populate m_brimMapByInstance, which is what the
|
||||
// skirt/brim grouping reads.
|
||||
if (has_skirt() || has_infinite_skirt() || has_brim()) {
|
||||
// Generate skirt/brim groups after brim so per-object and draft-shield footprints
|
||||
// include brims when grouping and offsetting skirt loops.
|
||||
|
||||
Reference in New Issue
Block a user