Every remaining legacy filament_id outside the BBL/QD_* islands re-derives
from its product triple (filament_vendor / filament_type / family name),
completing the content-addressed id model of filament_id_plan_v3.md:
- Relocation pre-step: 14 fdm_filament_* template forks (Cubicon x3,
Prusa x6, RH3D x6 minus the pc fork already re-homed) stop declaring
ids; the 16 presets that rode Prusa's forks now declare the id they
already resolved (verified zero effective-id drift over all 5892
instantiated presets).
- --drop-redundant-ids: 6 Custom/MyToolChanger generics drop copied GF
ids and ride their OFL families.
- --remint over all 62 non-island vendors: 2647 declarations re-derived;
identical products converge cross-bundle (showcase: PolyLite PLA is now
OF5CgdDq in OrcaFilamentLibrary, Qidi, OrcaArena and Snapmaker).
- Prusament @XL completions surfaced by the relocation: filament_vendor
["Prusa Polymers"] on the 8 @XL declarers; Prusament PA-CF typed
PA11-CF (the product is Prusament PA11CF) and PC-CF typed PC-CF
(-CF family typed as base polymer); each family converges on one id.
- Succession: 331 ids retired with mode-rule successors, 355 never-shipped
v1 mints forgotten with chain splicing, 151 GF-shaped ids released to
the island space with hints, 27 curated BBL-generic -> OFL-generic
hints (GFL99 -> OFDSrzZ8 class). New --retire "OLD=NEW" maintenance
mode records lineage for OGFC99/OGFG99/OGFN99 (the shipped ids of OFL's
Generic PC/PETG/PA in released versions, whose claims migrated in v3.1
while Cubicon's inert fork declarations kept them alive).
- Retiring the P-hex system ids removes the last system ids from
check_ams_filament_valid's destructive P-gate.
- AMS-ambiguity fixes surfaced by convergence (validator -f): 4 presets
riding another family's id through inherits now declare their true
family id (Elegoo Generic ASA-CF/PETG-CF, Snapmaker PolyLite Dual PLA /
PolyLite J1 PLA); 9 Cubicon @base presets and Dremel Generic PLA, which
duplicate their per-printer variants on the same printers, split onto
the salt-1 iteration of their triple (sanctioned by the mint-conformance
check; --remint now leaves salt-conformant declarations alone).
Gates: assign_filament_ids --check 0; orca_extra_profile_check 0;
116 python unit tests; config-equivalence over all 5892 instantiated
presets (byte-identical configs except the 14 sanctioned Prusament
vendor/type corrections; setting_id and compatible_printers unchanged;
per-family convergence and ledger conservation verified); profile
validator base/-f/-r(BBL)/-r(Qidi) all green; libslic3r_tests 48610
assertions; Moonraker OFL generic map check; custom-preset fixture
archives v1.9.0-v2.4.1.
Phase v3.1 of filament_id_plan_v3.md: every OFL-declared filament_id
re-derives from its product triple; the succession ledger absorbs the old
ids. Config-equivalence verified: flattened effective configs differ ONLY
in filament_id (284 consistent old->new changes tree-wide, including every
vendor preset that rides an OFL family), zero config/compatible_printers/
setting_id drift.
Structural pre-step (id-value-neutral, verified): OFL generic family ids
move from the shared fdm_filament_* template bases onto the product-named
"Generic X @System" presets, so the v3 triple's family component is the
product name, not an internal file name (12 relocations; 2 dead duplicate
declarations on fdm_filament_pa/pet pruned; every consumer chain itemized
first - the +12 instantiated_with_id entries are this deliberate pattern,
matching the 11 generics that already declared on @System).
fdm_filament_pc keeps its declaration this phase: 7 Prusa presets (Prusa
Generic PC, Prusament PC Blend) inherit it directly and re-home in the
v3.2 Prusa worksheet; its transitional id is documented in the plan.
Re-mint: 301 declarations re-derived (e.g. Generic PLA OGFL99 ->
OFDSrzZ8 = mint("filament_product/Generic/PLA/Generic PLA")). Snapshot:
1477 ids (+300/-292). Ledger:
- 230 shipped ids retired with mode-rule successors (OGF* library ids,
OFLSBS99, DREMC/FILAR/AliZ/eSUN/... legacy strings). DREMC010 had been
shared by a PPA-CF and a TPU family - a data bug this split resolves;
its successor follows its only shipped claim (DREMC PPA-CF).
- 25 GF-shaped ids OFL had copied from the Bambu catalog (GFOT00x
Overture, GFSEP0xx) are RELEASED to the BBL island space with hints at
the re-minted families - never retired, so a future legitimate BBL
catalog addition is never blocked; plus an explicit GFOT001 ->
OFxA1p01 hint (Overture PLA Pro, still live in BBL).
- 37 never-shipped v1 branch mints dropped from lineage
(--forget-never-shipped; they exist in no release, no forwarding
needed).
- OGFC99/OGFG99/OGFL96/OGFN99/OGFSNL08 stay live (still declared by
vendor bundles); they retire in v3.2 when those declarers re-mint.
Gates: 106 unit tests OK; --check exit 0; orca_extra_profile_check exit 0;
validator -l 2 / -f tree-wide / -r BBL+Qidi exit 0; Moonraker OFL map
check resolves all 29 aliases against the re-minted presets; flatten
equivalence pre/post as above; snapshot regen idempotent.
Implements phase v3.0 of filament_id_plan_v3.md - tooling and client
prerequisites. No filament_id values change in this commit.
Tooling (scripts/assign_filament_ids.py; 106 unit tests):
- The mint key becomes "filament_product/<filament_vendor>/<filament_type>/
<family_name>", resolved loader-faithfully from the declarer's flattened
config - bundle-independent and content-addressed; identity fixes re-id
by design and are made safe by the succession ledger. --mint now takes
the triple.
- The snapshot gains "triples" and "triple_exceptions" sections, folded
into the equality gate: any vendor/type/name change surfaces as a
reviewable snapshot diff. Check 3 rewritten to triple-mint conformance
with (id, triple) grandfathering; check 5 generalized from OFL generics
to every OFL-riding vendor preset; new check 8 (triple integrity) and
check 9 (succession integrity).
- The retired ledger moves to resources/profiles/retired_filament_ids.json
so it ships with the app; schema {claims, successor} plus cross-island
"hints". The 14 v1 entries are migrated with mode-rule successors.
Vanished ids in a foreign island's space (GF*/QD_*) are RELEASED with a
hint instead of retired: the island catalog owns them and may
legitimately (re)ship them, which check 4 must never block.
- New maintenance modes: --remint VENDOR, --drop-redundant-ids VENDOR,
--add-hint "OLD=NEW", and --update-snapshot --forget-never-shipped FILE
(never-shipped ids drop from lineage; chains splice through them).
Runtime (C++):
- Succession helpers in libslic3r/Preset (pure chain-follow + lazy ledger
load from resources), consulted only on resolution miss in
get_filament_by_filament_id, both AMS sync predicates,
add_ams_filaments, setting_id_to_type, and the calibration-history
lookup; behavior is byte-identical while the ledger has no matching
entry. Catch2 coverage in tests/libslic3r.
- W1 hardening: check_ams_filament_valid no longer remote-wipes trays or
rewrites temps for P-shaped ids that a system preset carries (ten such
system ids ship today); the size==8 && [0]=='P' assert is relaxed; the
unguarded filament_list find deref in the temp-equation check returns
non-destructively on a miss.
- MoonrakerPrinterAgent: the 23 hardcoded OFL generic ids are replaced by
a runtime lookup of "Generic <family> @System" (id-equivalent on
today's shipped profiles, follows future re-mints automatically);
scripts/test_moonraker_lane_data.py derives its expectations from the
shipped profiles and gains --check-ofl-map.
Profile data (W3 - type is now a key component, so type bugs are fixed
before any re-mint):
- 17 same-name-different-type groups corrected across 50 files (OFL
Generic PETG-CF/PE-CF/PP-CF; Flashforge ASA Basic/ASA-CF/ABS-CF/HIPS/
PAHT-CF/PLA Silk; FusRock PAHT; Creality Generic PA6-CF; InfiMech PETG;
Anycubic TPU 95A / TPU for ACE; Prusa Generic PA-CF/PLA-CF) - every
value validated against MaterialType::all(); 3 Snapmaker U1 roots gain
their missing filament_vendor. Flattened-config equivalence vs the
previous commit: exactly the 54 intended diffs, zero BBL.
- doc/developer-reference/filament_id.md rewritten for the v3 rule.
Ambiguous type divergences (Prusa Generic TPU/TPU HF FLEX-vs-TPU, FusRock
S-Multi/S-PAHT) and all same-type vendor-tag divergences are deferred to
the v3.2 vendor worksheets, catalogued with analysis.
Revises the v1 mint rule after the maintainer catch that its key scoped
families to the profile bundle (printer brand), fragmenting one commercial
product into N ids (PolyLite PLA: five bundles, all filament_vendor
"Polymaker", up to five ids).
Architecture: BBL and QD_* stay frozen islands; OrcaFilamentLibrary becomes
the single declaration point for every other material family; vendor
bundles carry only same-alias specializations (no filament_id key) that
shadow the OFL preset per printer and resolve its id through the loader
walk. New mint key: uuid5 over
"filament_product/<filament_vendor>/<filament_type>/<family_name>" from the
root's flattened config — bundle-independent (hoisting families into OFL is
id-stable), and the type component keeps the four known same-name-
different-type groups apart until their data bugs are fixed.
Content-addressing replaces "ids immutable once shipped": identity edits
re-id the family, made safe by turning retired_filament_ids.json into a
shipped succession ledger (old id -> successor, chains allowed,
cross-island hints permitted) consulted on resolution miss in the AMS sync
and lookup paths. The MoonrakerPrinterAgent hardcoded generic-id map is
replaced by a runtime preset lookup, removing the code/profile lockstep.
Migration: v3.0 tooling + W1 client hardening + W3 type fixes; v3.1 OFL
re-mint with succession entries; v3.2 vendor bundles (391 unshipped OF ids
re-derive without retirement; generic tunings re-point to OFL; ~800 shipped
legacy ids re-mint with succession, incl. the 57 multi-vendor GF residue
and the 10 P-hex system ids, which also exits them from the destructive
check_ams_filament_valid P-gate); v3.3 optional id-stable consolidation
into OFL. Gate battery unchanged plus succession-resolution tests.
filament_id_plan_v2.md gets a status note: its P+md5 verdict stands; its
work items are absorbed or superseded by v3.
Introduces a plugin capability that runs Python at the seams of Print::process(),
letting a plugin read and rewrite slicing state as it is computed.
- New slicing_pipeline_plugin config option; selected plugin refs are serialized
into the print manifest.
- Print gains an injectable hook fired at each pipeline step (posSlice,
posPerimeters, posInfill, ...). It is a no-op when unset, fires only on genuine
(re)computation, and never on the use-cache path.
- orca.slicing submodule: SlicingPipelineCapabilityBase plus a trampoline and a
Step enum. Capabilities read the live graph through zero-copy int64 numpy views
(contour/holes geometry with unscaled coordinates, flattened toolpath data) and
edit it through 2D-geometry mutators with cache-invariant refresh.
- GUI dispatcher runs capabilities during slicing under the GIL, turns plugin
errors into slicing errors, honors cancellation, and adds the plugin picker.
- Ships the InsetEverySlice sample plugin and binding/hook tests.
# Description
<!--
> Please provide a summary of the changes made in this PR. Include
details such as:
> * What issue does this PR address or fix?
> * What new features or enhancements does this PR introduce?
> * Are there any breaking changes or dependencies that need to be
considered?
-->
# Screenshots/Recordings/Graphs
<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->
## Tests
<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->
<!--
> A guide for users on how to download the artifacts from this PR.
-->
[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
* fix: crash in Measure tool when a plain edge is the first selection
The SPHERE_2 gripper raycaster called get_feature_offset() on
.first.feature instead of .second.feature (copy-pasted from the SPHERE_1
block). Plain planar-border edges store no extra point, so the Edge
branch dereferenced an empty optional behind a release-stripped assert,
aborting on Flatpak and undefined behavior elsewhere.
Point the SPHERE_2 raycaster at .second.feature and fall the Edge branch
back to the edge midpoint.
Fixes#14018
Examined minting system filament_ids with CreatePresetsDialog.cpp:487's
user-custom allocator (adopt-by-base-name, else "P"+md5(name)[0:7]) for
Bambu AMS-sync compatibility. Verdict: keep the OF* mint; capture the
proposal's value through bounded work items instead of a re-mint.
- The device path never validates id shape: tray_info_idx is an opaque
string end-to-end (DeviceManager.cpp:1642; DevFilaSystem.cpp:512-514)
and firmware persists arbitrary bytes (bambulab/BambuStudio#5436).
Resolution is by value against the GF catalog plus the account's cloud
custom cache; unknown ids show "?" regardless of shape, and system
presets can never enter that cache (Preset.cpp:2071 gates upload on
is_user()).
- The only id-shape dispatch in the tree is destructive for P-shaped ids:
check_ams_filament_valid (DeviceManager.cpp:5335/5352/5395/5410) remotely
clears an AMS tray (:5345) or rewrites its temps when a P-shaped tray id
drops out of the user-root preset list — a state name-keyed minting
creates by construction. GF* and OF* ids are structurally immune.
- The real value is captured without re-minting 391 unshipped ids across
1327 files: the adopt step == curated GF adoption via the snapshot ledger
(W4); the hash belongs to user presets (W5, cf. PR #13315); client
hardening (W1) that the ten already-shipped P-hex system ids (Cubicon x8,
Ginger, Artillery) need today anyway.
Method: 6 parallel evidence agents (generator semantics, AMS/device path,
exhaustive shape-dispatch sweep, upstream + online sources, tree-wide
dry-run over 5892 presets / 1148 base names, branch change inventory) into
a 3-judge panel (keep-OF* won 23/40 aggregate); the upstream generator was
verified byte-identical to BambuStudio master, and every load-bearing
file:line and number in the document was re-verified against the tree
before commit.
With every vendor's filament_id collisions fixed (356 collision groups /
1256 printer-level ambiguity errors across 30 vendors, plus the 16
library-internal ids and the 10 live library-cross groups), the
duplicate-filament-subtype validation no longer needs the BBL-only scope:
drop -v BBL from the -f step so any new ambiguity in any vendor fails CI.
Local verification of the full workflow against this branch:
- extra JSON check exit 0; validator -l 2 exit 0 (66 vendors);
-f tree-wide exit 0 (with the library-aware extended validator, which is
strictly stricter than the released binary CI downloads); -r exit 0 for
BBL and Qidi.
- custom-preset fixture archives v1.9.0..v2.4.1 overlaid per the workflow:
six pass outright; v2.3.1/v2.3.2/v2.4.0/v2.4.1 fail locally only on a
pre-existing Windows-only validator limitation (a user preset with a
non-ASCII filename reads as empty; reproduced byte-identically on the
pre-migration base commit, and absent on CI's Linux runners where these
archives validate green).
The custom-preset fixture validation (check_profiles.yml step 5, run locally
against the v2.3.2/v2.4.0/v2.4.1 archives) caught a real regression in the
previous Cubicon fix: those archives contain user presets whose inherits
names the @base presets directly ("can not find parent Cubicon ABS @base").
A system preset with instantiation:false is not added to the preset
collection, so flipping the nine @bases was equivalent to deleting their
names - exactly the user-preset drop hazard the migration rules forbid.
Repair: restore all nine @bases byte-identically to their pre-flip state
(instantiated, setting_id, original P510cf* ids; Cubicon PC @base keeps its
minted OFnLnZQo from the copy-paste fix). The AMS ambiguity is instead
resolved Dremel-style: each family's three passthrough variants share one
fresh variant-tier mint as own keys, so on every printer the base and the
variant carry different ids. Zero preset-set change, zero config change,
user presets and fixtures resolve exactly as before the migration.
Verified: fixture archives v2.3.2/v2.4.0/v2.4.1 no longer report missing
parents; config-equivalence holds; orca_extra_profile_check.py exit 0;
assign_filament_ids.py --check exit 0; validator -l 2 exit 0; tree-wide
extended -f still exit 0.
Phase 2 completion - Volumic, Ratrig, Chuanying, Afinia, Eryone, FLSun,
Tiertime, Blocks, CONSTRUCT3D, Co Print, CoLiDo, DeltaMaker, Ginger
Additive, OrcaArena, Peopoly, Wanhao France, iQ. All are P1 copy-paste or
small structural fixes; owners keep their ids by Bambu-catalog/OFL/historic-
introducer precedence, impostor families get fresh deterministic mints,
zero effective-config change:
- Volumic 10 mints (ABS/ASA/PP, PETG/PCTG/ESD, PLA/UNIVERSAL, PA/PPS lines);
Ratrig 10 (BigNozzle/PunkFil lines off the Generic line's ids); Chuanying
8 (plus 3 redundant own-id lines removed so parents' minted ids flow to
the 0.25-nozzle variants); FLSun 4 (S1/T1 High Speed + Silk); Peopoly 4
(Lancer lines off Generic PLA's GFL99); Afinia 3 (Value PLA/ABS+/Value ABS
inside the frozen GFx##_## scheme); Co Print 3 (ABS/PETG/TPU off GFL99).
- Tiertime: Generic SBS had copy-pasted Generic PLA's ids on both printer
lines - one family mint on both variants collapses the split. Eryone: PP
copy-pasted PETG-CF's EFL43 (same introducing commit) - PP minted.
Blocks: ASA-CF copy-pasted PLA-CF's BSFI010 - ASA-CF minted. CONSTRUCT3D:
High Flow PETG minted off GFG99. CoLiDo: both claimants of the invented
GFA99 re-minted. DeltaMaker: Brand PLA minted off GFL99. OrcaArena:
Generic PLA Silk minted; the Bambu-clone Arena PLA Silk keeps GFA05
(catalog precedence). iQ: Grauts HPP4GF25 minted off Fiberthree's IQM1.
- Ginger Additive (P5): the shared fdm_filament_common template carried
P510eff9; moved onto the two pellet families (Generic PETG keeps it as
first claimant, Generic PLA minted), template key removed after verifying
every inheritor still resolves an id.
- Wanhao France (P4): YUMI PLA Bowden stops over-claiming the six
direct-drive printers covered by YUMI PLA Direct Drive (trim only, no
mints).
Verified: config-equivalence gate zero unexpected diffs; extra check exit 0;
--check exit 0; validator -l 2 exit 0; and with all 30 vendor migrations
now applied, the extended validator's -f check is clean TREE-WIDE
(0 ambiguity errors, down from 1256 baseline / 16448 with the library-aware
extension); -r exit 0 for BBL and Qidi.
{input_filename_base} is meant to be the saved project's file name. Before
#13753 a bug made it fall back to the first object's name when a project was
saved; #13753 fixed it to use the project name. Some users relied on the old
behavior to get the part name into their output file name and had no
placeholder to recover it ({model_name} is the 3mf designer metadata, blank
for plain STL imports).
Add {first_object_name} as a dedicated placeholder for the first printable
object on the current plate, populated in update_object_placeholders()
independently of {input_filename_base}.
Closes#14493
Phase 2 long-tail migrations (25 collision groups), zero effective-config
change:
Snapmaker (11 + the tree's one live library-cross group): three Benchy demo
presets coexisting via compatible_prints gating (invisible to the -f check)
get their own mints; the TPE/TPU-High-Flow lines riding shared bases get
family mints across the plain/U1/Dual/J1 variants; 'Snapmaker PLA Matte @U1'
minted; 'Snapmaker PET @Dual' unified onto its own family root id;
'Snapmaker PA-CF @U1' had its compatible_printers triple-duplicated
(self-collision) - deduplicated. The PolyTerra J1/Dual PLA presets that
re-exposed the library's PolyTerra PLA (alias rename) get family mints while
'PolyTerra PLA @0.2 nozzle' keeps the library id via inheritance. Fiberon
families keep their authentic byte-copied Bambu catalog ids (sanctioned
multi-vendor brand sharing, frozen in the snapshot).
FlyingBear (8): every preset carries an own-key Bambu generic id; owners
keep theirs, 15 impostor families (S1/Ghost7/Hyper lines) re-minted
family-atomically - '@S1' and '@Ghost7' siblings move together (20 edits).
Sovol (6): the GFL99 copy-paste epidemic. The five same-alias
'Generic * @Sovol SV08 MAX' tunings just DROP their wrong own-id lines so
the library family ids flow through inheritance (alias-shadow-verified);
Generic PLA Silk / SUNLU PETG adopt their library family ids as own keys
(their inherits point at other families, so deletion would collide); the 21
dedicated Sovol/Polymaker families get fresh mints (23 edits).
'Sovol SV07 PLA' keeps GFL99 unambiguously (sole claimant on SV07), frozen
in the snapshot.
Verified: config-equivalence gate zero unexpected diffs; extra check exit 0;
--check exit 0; validator -l 2 exit 0; extended -f -v Snapmaker/FlyingBear/
Sovol all exit 0.
Phase 2 long-tail migrations (56 collision groups across six vendors), zero
effective-config change except where stated:
Cubicon (17): 'Cubicon PC @base' verbatim-copied PA-CF's P510cfd0 at
introduction (90a6c53ad5; PC was the config-light stub) - the PC family gets
a fresh mint via its root. The other groups were all one shape: nine @base
presets instantiated on exactly the two printers their dedicated variants
cover with byte-identical passthrough configs, so no compatible_printers
trim exists that CI would accept (instantiated presets must claim a
printer). Fix: flip the nine @bases to instantiation:false and drop their
setting_ids (bases carry none by convention). Every printer keeps an
identical-config selectable preset per family - verified programmatically
before flipping; no machine default_materials references an @base name.
Dremel (3): 'Dremel Generic PLA' is deliberately selectable alongside its
per-printer variants (#6837 re-exposed it), and the variants carry real
overrides - a genuine AMS ambiguity with no config-neutral trim. The three
variants share the fresh family mint OFUYjPc4 as own keys; the root keeps
GFL99. A deliberate same-family id split (P8 shape): Dremel has no device
ecosystem consuming filament_id, and every alternative is a user-visible
regression of #6837. default_materials resolve unchanged.
Anycubic (14): copy-paste/generic-riding across three id eras; 2022-era
Anycubic Generic ABS/PETG/PLA/TPU keep the catalog ids, 13 families
re-minted atomically (72 edits incl. 48 forced same-family re-ids), retiring
the invented GFABS/GFL92/GFL93/GFPLA* space-containing ids.
Artillery (8): GFL99 umbrella + P-hex copy-pastes; Artillery Generic PLA
keeps GFL99, Artillery PLA keeps Pfcf9c4c, 17 families minted (52 edits),
P284941e retired.
InfiMech (14): pure verbatim Bambu-generic copy-paste; the six InfiMech
Generic owners keep their ids, 15 impostor families minted (34 edits).
Creality (8): HF generics copy-pasted GFL99 + id-less sub-brand lines riding
keeper families; owners keep their git-verified ids, 8 families minted (17
edits, own-key layout for members inheriting heterogeneous per-series
parents).
Verified per batch: config-equivalence gate zero unexpected diffs (the nine
Cubicon @base instantiation flips are the only preset-set changes, reviewed
above); orca_extra_profile_check.py exit 0; assign_filament_ids.py --check
exit 0; validator -l 2 exit 0; extended -f exit 0 for all six vendors
(-v Cubicon/Dremel/Anycubic/Artillery/InfiMech/Creality); 14 fully-vanished
ids appended to the retirement ledger.
Phase 2 of the filament_id cleanup (31 collision groups, all one mechanism:
material-class ids on shared fdm_filament_* templates collapsing HF/-CF/
Prusament lines onto one id, plus 3 overclaims). 18 new @base family roots
(owner generic keeps the legacy id, 12 impostor families get deterministic
mints), 72 config-neutral inherits repoints, 6 template filament_id keys
removed (only where every id-less inheritor is covered by a new root;
fdm_filament_asa/pc keep theirs for the frozen Prusament @XL riders),
3 compatible_printers trims on MINIIS printers covered by dedicated @MINIIS
variants. Frozen name-shaped and _NN per-variant ids stay byte-identical.
Verified: config-equivalence gate zero unexpected diffs; extra check exit 0;
--check exit 0; validator -l 2 exit 0; extended -f -v Prusa exit 0 (was 31
groups / 1035 printer-level errors on the pre-apply copy).
Selecting the prime tower and rotating it (PageUp/PageDown) crashed.
Selection::notify_instance_update() indexed m_model->objects with the
wipe tower's synthetic id (>= 1000), which is not a ModelObject index,
so the lookup returned garbage and dereferencing it segfaulted.
do_rotate/do_scale/do_mirror already skip the wipe tower in their own
loops but all call this shared helper, so scale and mirror hit the same
fault. Selection::drop() had the same latent bug via a direct index.
Guard both with the >= 1000 check already used throughout the file.
Fixes#14498