Skip to content

Fix/fill bed objects over plate boundary - #885

Open
muhammetfurkandemiral wants to merge 9 commits into
Snapmaker:mainfrom
muhammetfurkandemiral:fix/fill-bed-objects-over-plate-boundary
Open

muhammetfurkandemiral wants to merge 9 commits into
Snapmaker:mainfrom
muhammetfurkandemiral:fix/fill-bed-objects-over-plate-boundary

Conversation

@muhammetfurkandemiral

Copy link
Copy Markdown

Description

Screenshots/Recordings/Graphs

Tests

Right-clicking an object and picking "Fill bed with copies" left copies laid
over the plate boundary, raising "An object is laid over the plate boundaries".

After packing, libnest2d's finalAlign() re-centers the pile on the convex hull
of the packed items. That hull ignores the fixed items that filling the bed
always leaves around (other objects, the wipe tower, excluded regions), so the
shift it computes can push copies off the plate. BBS hit this and turned the
alignment off for their own vendor (STUDIO-3265), but `!is_bbl` kept it on for
everyone else.

Turn it off unconditionally. Besides removing the bad shift, Alignment::DONT_ALIGN
makes the placer's boundaryCheck a strict isInside(chull, bin) test instead of an
overfit penalty, so items are only packed when they really fit the bin.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… objects

Above 100 copies FillBedJob skips the arranger and drops the copies into the
grid cells returned by Plater::get_empty_cells(). Three things went wrong there:

- ArrangePolygon::translation positions the instance origin, but the cell is
  sized around the object's convex hull. When the origin is not the hull center
  every copy is displaced by that delta and the outer row hangs off the plate.
- The cell pitch was measured on the unrotated outline, so a copy carrying a Z
  rotation has a larger footprint than the cell it was given.
- get_empty_cells() grids the raw build volume. It knows neither the skirt/brim
  margin already applied to m_bedpts nor the objects standing on the plate, so
  copies could land over the edge or on top of an existing object.

Measure the footprint on the rotated outline, seat each copy on its cell by that
footprint, and test every cell before using it: a copy that does not fit the
shrunk bed or that overlaps an unselected item is skipped and the next cell is
tried. get_empty_cells() works in the current plate's coordinates while m_bedpts
and m_unselected are normalized to the first plate, so the plate offset is
applied before comparing, the same way finalize() does it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
"Fill bed with copies" packs copies right up to the bed edge, so the outermost
ones land inside the 3.5 mm band the plater checks for spiral lift collisions,
and "Model too close to bed boundary. Disable spiral lifting or keep at least
3.5mm gap to avoid collision." appears the moment the command finishes. Running
Arrange afterwards clears it only because arrange centers the pile and happens
to leave a margin, which is luck rather than intent.

Shrink the arranged bed by SPIRAL_LIFT_SAFETY_MARGIN whenever any filament's Z
hop performs a spiral lift, so arrange and fill bed both leave the gap on their
own. "Auto Lift" counts as a spiral lift here because GCode.cpp resolves it to
one when it writes the G-code.

The margin constant moves next to ZHopType instead of staying a local constexpr
inside the plater check, so the check and the arranger cannot drift apart.

Sequential printing is left alone: its clearance_radius model governs spacing
there, and folding the margin in would fight it.

FillBedJob's >100-copy grid fallback bypasses the arranger, but the previous
commit made it test its cells against the shrunk bed, so the margin reaches that
path as well.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…onfigured

The "Model too close to bed boundary. Disable spiral lifting or keep at least
3.5mm gap to avoid collision." warning fired for every printable model near a
rectangular plate edge, whatever Z hop the printer was set to. A user on Normal
or Slope lift got told to disable a feature they were not using, over a risk
that cannot occur: only a spiral lift traces an arc outside the object
footprint, which is the whole reason for the margin.

Gate the check on the same predicate the arranger uses, so the warning appears
exactly when the hazard does. The config is resolved once per call instead of
per volume, from the plater's cached config rather than rebuilding a full one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…of objects

Slicing a full plate of tall models with a prime tower ended in "A G-code path
goes beyond the plate boundaries", with the offending path in the tower.

Every non-BBL printer is sliced with WipeTower2, which lays down a stabilization
cone under the tower whose radius is tan(cone_angle / 2) * tower_height - it
widens as the print gets taller. Print::first_layer_wipe_tower_corners() already
extrudes that cone, but the GUI never accounted for it:

- The code that keeps the tower on the plate before slicing bounded only the
  width x depth rectangle plus brim, so a tower parked near an edge passed that
  check and then spilled over once its cone was sliced.
- get_wipe_tower_info(), which arrange and fill bed treat as the tower, used the
  same rectangle, so copies were packed right up against where the cone lands.

Add WipeTower2::get_first_layer_footprint(), the bounding box of that first
layer (rectangle, brim and cone), and use it in both places. With a zero cone
angle - and always on BBL printers, whose WipeTower has no such cone - it is the
old rectangle-plus-brim bound, so their behaviour is unchanged.

get_wipe_tower_info() also bounded the tower position as if its box started at
the origin (clamp to [0, plate - size]), although the box already reached below
it by twice the brim. Near the far edge that shifted arrange's idea of the tower
away from where it really stood, and would have done so by the whole cone once
the box covered it. Bound the position by the box's own min and max instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ter's

"Prime Tower" was rendered as "Başbakan Kulesi" - prime as in prime minister -
in the prime tower collision warning. Use "Prime Kulesi", the term the rest of
the Turkish translation already uses for "Prime tower".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fill bed packed the copies around the prime tower wherever it had been left.
A tower standing mid-plate, or a little off an edge, splits the free area and
wastes the strip beside it, which is the opposite of what filling the plate is
for.

Before packing, the tower is now pushed into the plate corner nearest to it -
nearest, so a tower placed back-left stays back-left - with its whole first-layer
footprint (brim and stabilization cone) WIPE_TOWER_MARGIN inside the plate, and
the copies are packed around it there. If the footprint does not fit the plate
either way the tower is left for the plate clamp to deal with.

The position is written to the project config directly: the tower info carries a
plate-local position, whereas WipeTowerInfo::apply_wipe_tower() subtracts the
plate origin as if it were a world one, which would misplace the tower on every
plate but the first.

Plain Arrange still leaves the tower where the user put it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A new "Fill bed: 3 options" item in the object's right-click menu fills three
new plates with copies of the selected object, each packed with a different
trade-off between how many copies fit and how much room each gets, so they can
be compared side by side and the preferred plate printed:

- A Max       copies may be rotated, no extra gap or edge margin
- B Balanced  no rotation, 3 mm extra gap between copies, 2 mm along the edge
- C Safe      no rotation, 8 mm extra gap, 5 mm along the edge

Every gap sits on top of the brim / support aware inflation the arranger gives
each copy anyway, so support room is never traded for count, and the spiral
lift margin from update_arrange_params() still applies. Each plate is named
after its option and copy count ("A Max - 16 copies").

The source object is left where it is. On a multi-material print the new plates
get the prime tower pushed into the corner nearest to where the current plate
has it, the copies packed around its full first-layer footprint. To reserve that
spot without moving the current plate's tower, the corner snap is split from
storing the position: get_wipe_tower_corner_arrangepoly() only computes it.

All plates are created before any copy is placed, because a new plate can
re-flow the plate grid and move the origins of the plates already there. Copies
are placed at their plate's origin and registered with it. More than 100 copies
per option would make the packer crawl, so an option is capped there and its
plate is named "100+". Plates that would exceed MAX_PLATES_COUNT are skipped
with a notification, and so is an object that does not fit an empty plate.

Turkish strings for the new UI are added.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
"Fill bed: 3 options" named its plates correctly ("B Balanced - 14 copies")
but every copy stood on the same spot in the middle of the plate, raising the
collision warning.

The copies were registered with their plate through add_to_plate(), which
calls PartPlate::add_instance() with move_position set and so moves a newly
added instance to the plate centre - after its packed position had already
been applied.

Register them with notify_instance_update(..., is_new) instead: it finds the
plate the instance stands on and adds it there without moving it, as the
instances path of fill bed does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant