mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-17 19:36:28 +02:00
Match slicer presets on what they declare, not what they are named (issue #2982)
The internal slicer picked PETG for a PLA plate and an A1 process for a P1S. Both come from the sidecar's bundled-profile listing, fixed in the sidecar repo; this is the consuming half plus the hardening that keeps an older sidecar degrading rather than breaking. Standard-tier presets now carry the compatible_printers the sidecar reports. That list is the only truthful account of which printer a preset belongs to, because the bundle ships no process preset named after a P1S, an X1, an X1E or an H2D Pro -- all ten of the P1S's are named "@BBL X1C" and name the P1S only in that list. Reading the printer out of the preset NAME therefore made a P1S look like it had no compatible process at all: all 198 hid behind "Show all" and the auto-pick fell through to an alphabetically-first 0.06mm Fine @BBL A1 0.2 nozzle the CLI refused. A P1S now gets 0.20mm Standard @BBL X1C and 73 filaments instead of 4. An older sidecar reports nothing here, which leaves the name matcher in place -- degraded as before, not broken. Material is now a hard partition in the filament pre-pick rather than a +10 bonus. A preset stating a different material than the plate asks for is the wrong preset, not a worse one: wrong nozzle temperature, wrong bed temperature, wrong flow. A preset stating NO material stays eligible -- unknown is not wrong, and 32 shipped profiles genuinely have none. The same rule reaches the retain path, which held a slot on printer-compatibility alone and so cemented a wrong-material pick through every re-pick. A preset the user chose themselves is exempt: printing PETG on a plate a designer labelled PLA is a legitimate thing to do, and this rule exists to correct the auto-pick, not to overrule the user. Two more, both found while tracing this and neither reported: Among process presets equally valid for the selected printer, the one nearest a 0.2mm layer height now wins. Within a tier the list is alphabetical and Bambu's naming puts the finest height first, so every slice that did not name its own process silently got 0.08mm Extra Fine on an X1 Carbon and 0.06mm Fine on an A1 mini -- correct presets, nobody's default. Ties break toward the coarser, faster height; a name with no readable height is still pickable when it is the only candidate; a process the 3MF named still wins outright. H2DP is aliased to H2D Pro, the same shape as the A1M rename in #1649 -- the bundle spells the model one way in preset names and another in the printer preset, so an H2D Pro classified all 198 processes as another printer's. Deliberately narrow: H2DP and a plain H2D are different machines and must not collapse. A dropdown the printer filter would empty now shows the unfiltered list instead. That state was reachable for four printer models and told the user nothing; a visible preset for the wrong printer can be changed, an empty dropdown cannot. Verified against live Orca 2.4.2 and BambuStudio 02.08.02.61 sidecars over the real 1156- and 1792-profile trees: every one of the eight printer models tested now auto-picks a 0.20mm process for its own printer, a PLA plate draws a PLA preset and a PETG plate a PETG one. Each change was confirmed to fail its tests when reverted.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { Cloud, CloudOff, Cog, Loader2, RefreshCw, X } from 'lucide-react';
|
||||
import { useEffect, useId, useMemo, useState } from 'react';
|
||||
import { useEffect, useId, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import {
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
pickDefault,
|
||||
pickFilamentForSlot,
|
||||
pickProcessDefault,
|
||||
statesDifferentMaterial,
|
||||
type Slot,
|
||||
} from '../utils/slicePresetPicker';
|
||||
|
||||
@@ -218,6 +219,12 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
||||
// entry per AMS slot the plate uses. Pre-pick (effect below) initialises
|
||||
// each slot from the source plate's required (type, colour).
|
||||
const [filamentPresets, setFilamentPresets] = useState<(PresetRef | null)[]>([]);
|
||||
// Slots the user chose a filament for by hand, or by applying a pipeline.
|
||||
// The pre-pick below re-picks a slot whose preset states the wrong material,
|
||||
// and without this it would do that to a deliberate choice too — printing
|
||||
// PETG on a plate a designer labelled PLA is a thing people do on purpose. A
|
||||
// ref rather than state: this must not itself re-trigger the pre-pick.
|
||||
const explicitFilamentSlots = useRef<Set<number>>(new Set());
|
||||
// Per-slot colour override, plate-slot-ordered alongside `filamentPresets`.
|
||||
// `null` means "not overridden" rather than "no colour": the slot then falls
|
||||
// through to the source plate's own colour, and — when the source has none
|
||||
@@ -579,7 +586,20 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
||||
const cur = current[i] ?? null;
|
||||
if (cur) {
|
||||
const p = findPreset(data, cur, 'filament');
|
||||
if (p && presetCompatibility(p, 'filament', selectedPrinterName, compatIndex) !== 'mismatch') {
|
||||
// Compatible with the printer is not on its own a reason to keep it:
|
||||
// a preset that states a different material than the plate asks for
|
||||
// is the wrong preset however well it fits the machine, and holding
|
||||
// onto one is how a PETG profile survived on a PLA plate through
|
||||
// every re-pick (#2982). An explicit choice is exempt — the material
|
||||
// rule exists to correct the auto-pick, not to overrule the user.
|
||||
const wrongMaterial = p
|
||||
&& !explicitFilamentSlots.current.has(i)
|
||||
&& statesDifferentMaterial(p, slot.type);
|
||||
if (
|
||||
p
|
||||
&& !wrongMaterial
|
||||
&& presetCompatibility(p, 'filament', selectedPrinterName, compatIndex) !== 'mismatch'
|
||||
) {
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
@@ -598,9 +618,14 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
||||
// onto whatever the new plate happens to call slot 2. Same slot count keeps
|
||||
// them: that is a re-pick of presets, not a different plate layout.
|
||||
useEffect(() => {
|
||||
setFilamentColours((current) =>
|
||||
current.length === filamentSlots.length ? current : filamentSlots.map(() => null),
|
||||
);
|
||||
setFilamentColours((current) => {
|
||||
if (current.length === filamentSlots.length) return current;
|
||||
// A renumbered slot list invalidates the record of which slots the user
|
||||
// chose for the same reason it invalidates the colours: slot 2 of the new
|
||||
// plate is not slot 2 of the old one.
|
||||
explicitFilamentSlots.current = new Set();
|
||||
return filamentSlots.map(() => null);
|
||||
});
|
||||
}, [filamentSlots]);
|
||||
|
||||
const enqueueMutation = useMutation({
|
||||
@@ -817,6 +842,10 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
||||
for (let i = 0; i < next.length; i++) {
|
||||
if (i < picked.filament_presets.length) {
|
||||
next[i] = picked.filament_presets[i];
|
||||
// Applying a pipeline is as deliberate as picking
|
||||
// from the dropdown, so the slots it fills are
|
||||
// exempt from the material re-pick too.
|
||||
explicitFilamentSlots.current.add(i);
|
||||
}
|
||||
}
|
||||
return next;
|
||||
@@ -1034,15 +1063,16 @@ export function SliceModal({ source, onClose }: SliceModalProps) {
|
||||
slot="filament"
|
||||
data={presetsQuery.data}
|
||||
value={filamentPresets[idx] ?? null}
|
||||
onChange={(ref) =>
|
||||
onChange={(ref) => {
|
||||
explicitFilamentSlots.current.add(idx);
|
||||
setFilamentPresets((current) => {
|
||||
const next = current.length === filamentSlots.length
|
||||
? [...current]
|
||||
: filamentSlots.map((_, i) => current[i] ?? null);
|
||||
next[idx] = ref;
|
||||
return next;
|
||||
})
|
||||
}
|
||||
});
|
||||
}}
|
||||
disabled={isEnqueuing || !isUsed || useEmbedded}
|
||||
// Shown for every filament slot now that it is editable,
|
||||
// not just multi-color ones: a single-slot STL is exactly
|
||||
@@ -1389,9 +1419,13 @@ function PresetDropdown({
|
||||
];
|
||||
const filterByPrinter = slot !== 'printer';
|
||||
const compatSections: { tierLabel: string; entries: UnifiedPreset[] }[] = [];
|
||||
// The same sections with the printer filter never applied, kept for the
|
||||
// all-filtered-out case below.
|
||||
const unfiltered: { tierLabel: string; entries: UnifiedPreset[] }[] = [];
|
||||
const other: UnifiedPreset[] = [];
|
||||
for (const { key, label: lk, fallback } of tiers) {
|
||||
const entries = (data[key] as UnifiedPresetsBySlot)[slot];
|
||||
if (entries.length > 0) unfiltered.push({ tierLabel: t(lk, fallback), entries });
|
||||
if (!filterByPrinter) {
|
||||
if (entries.length > 0) compatSections.push({ tierLabel: t(lk, fallback), entries });
|
||||
continue;
|
||||
@@ -1416,6 +1450,17 @@ function PresetDropdown({
|
||||
compatSections.push({ tierLabel: t(lk, fallback), entries: compatible });
|
||||
}
|
||||
}
|
||||
// Filtering that leaves nothing at all is a statement about our matching,
|
||||
// not about the presets: a P1S has ten usable process presets and read as
|
||||
// having none, because every one of them is named for an X1C and only says
|
||||
// "P1S" in a `compatible_printers` list an older sidecar doesn't report
|
||||
// (#2982). Hiding all of them left a dropdown holding one auto-picked entry
|
||||
// and a "Show all" link, with no hint that the list was the problem. So
|
||||
// when the filter empties the list, show it unfiltered — a visible preset
|
||||
// for the wrong printer is recoverable, an empty dropdown is not.
|
||||
if (compatSections.length === 0 && other.length > 0) {
|
||||
return { sections: unfiltered, otherEntries: [] };
|
||||
}
|
||||
return { sections: compatSections, otherEntries: other };
|
||||
}, [data, slot, t, selectedPrinterName, compatIndex]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user