Browse Source

Prefer the selected printer when no preset matches the plate's material (#3234)

maziggy 2 days ago
parent
commit
11013d59c9

+ 7 - 0
CHANGELOG.md

@@ -5,6 +5,13 @@ All notable changes to Bambuddy will be documented in this file.
 ## [1.2.6b1] - Unreleased
 
 ### Added
+- **The Slice dialog offers OrcaSlicer's built-in third-party filaments, and every other filament the slicer ships for Bambu printers (#3234, requested by @nvx)** — The **Standard** tier listed only part of the slicer's own presets. Two gaps, both in the slicer sidecar, which needs the updated image:
+  - **OrcaSlicer's filament library was never read.** OrcaSlicer ships filaments that suit any printer (Elegoo, PolyLite, COEX and more, named `... @System`) in a folder next to the Bambu one. With the OrcaSlicer sidecar, the 264 of them that a Bambu printer can use are now listed. As in OrcaSlicer, each is offered on every Bambu printer except those with a filament made for that printer, so `Generic PLA @System` doesn't show on an X1 Carbon, which has `Generic PLA @BBL X1C`.
+  - **Filaments in brand folders were missing.** Both slicers keep many Bambu-printer filaments in subfolders (`COEX PLA @BBL X1C`, `SUNLU PLA Matte @BBL X1C`, ...). The sidecar read only the top folder: OrcaSlicer 2.4.2 now lists 1997 Bambu filaments instead of 1156, Bambu Studio 02.08.02.61 1928 instead of 1792.
+  - **The automatic pick prefers Bambu's own presets, and real filament over support.** When several presets fit a plate equally well, the Slice dialog takes the first in the list. The sidecar now lists, within the slicer's own presets and within the library: real filaments before support materials, then Bambu Lab, then Generic, then other brands. A PLA plate on an X1 Carbon still gets `Bambu PLA Basic @BBL X1C` rather than the alphabetically first `addnorth PLA`. A PA plate now gets `Generic PA` instead of `Bambu Support For PA/PET`, which states PA and used to win alphabetically. A P1P plate gets `Bambu ABS @BBL P1P` and `Bambu PLA Basic @BBL P1P` instead of a support or special-edition preset. HIPS still gets Bambu's `Generic HIPS`.
+  - **No preset of the plate's material:** the Slice dialog's last fallback picked a preset of another material without looking at the printer, so it could choose one the slicer then refused. It now prefers one for the selected printer.
+  - **Profiles built on one now slice with its settings.** 147 Bambu filaments in OrcaSlicer take their base settings from the library, and so does an Orca Cloud preset saved from a library filament. The sidecar dropped that parent, and the slice ran on the slicer's defaults. Verified on both slicers: `Elegoo PLA @System` slices at its 210 °C, `COEX PETG @BBL X1C` at 245 °C.
+  - **Why the reporter's Orca Cloud didn't help:** OrcaSlicer uploads only presets you created yourself. Ticking a built-in filament in OrcaSlicer only shows it locally, so it never reached Orca Cloud. The wiki says so now.
 - **Users who see only their own files now see only their own folders, and admins can share a folder with everyone (#3201, requested by @Minebuddy)** — Library files had an owner but folders did not, so a user with **View own library files** saw every folder anyone had made: a school with 100 student accounts showed each student every other student's folders, most of them looking empty, and offered them all as places to upload. Folders now have an owner, the user who made them.
   - **What a user sees:** their own folders, folders shared with everyone, folders holding one of their files, and the folders above those, so they can reach them. Users with **View all library files** still see everything.
   - **Sharing:** an admin (**Update all library files**) can mark a folder **Share with everyone** in its menu. A shared folder shows a people icon, everyone sees it and can add files and subfolders to it. Sharing applies to that folder, not to the folders inside it.

+ 57 - 0
frontend/src/__tests__/utils/slicePresetPicker3234.test.ts

@@ -0,0 +1,57 @@
+import { describe, expect, it } from 'vitest';
+
+import type { UnifiedPreset, UnifiedPresetsBySlot, UnifiedPresetsResponse } from '../../api/client';
+import { pickFilamentForSlot } from '../../utils/slicePresetPicker';
+import { buildCompatibilityIndex } from '../../utils/slicerPrinterMatch';
+
+// #3234 made the sidecar list every filament the slicer ships and resolve the
+// material of all of them. Before, a few presets with no known material kept
+// the no-such-material fallback on the right printer by accident; with those
+// gone, the fallback has to look at the printer itself.
+
+const index = buildCompatibilityIndex({ 'Bambu Lab X1 Carbon': 'X1C', 'Bambu Lab A1': 'A1' });
+const X1C = 'Bambu Lab X1 Carbon 0.4 nozzle';
+const A1 = 'Bambu Lab A1 0.4 nozzle';
+
+function empty(): UnifiedPresetsBySlot {
+  return { printer: [], process: [], filament: [] };
+}
+
+function tiers(perTier: Partial<Record<'local' | 'orca_cloud' | 'cloud' | 'standard', Partial<UnifiedPreset>[]>>) {
+  const build = (source: 'local' | 'orca_cloud' | 'cloud' | 'standard') => ({
+    ...empty(),
+    filament: (perTier[source] ?? []).map((e) => ({ id: e.name as string, source, ...e })) as UnifiedPreset[],
+  });
+  return {
+    local: build('local'),
+    orca_cloud: build('orca_cloud'),
+    cloud: build('cloud'),
+    standard: build('standard'),
+    cloud_status: 'ok',
+    orca_cloud_status: 'ok',
+  } as UnifiedPresetsResponse;
+}
+
+const pick = (by: UnifiedPresetsResponse, type: string, printer = X1C, color = '') =>
+  pickFilamentForSlot(by, { type, color }, printer, index)?.id;
+
+describe('pickFilamentForSlot — no preset of the plate material (#3234)', () => {
+  it('falls back to a preset the selected printer can take', () => {
+    // Alphabetically first and scored the same, but for another printer: the
+    // slicer would refuse the slice outright.
+    const by = tiers({
+      standard: [
+        { name: 'Bambu ABS @BBL A1', filament_type: 'ABS', compatible_printers: [A1] },
+        { name: 'Bambu PLA Basic @BBL X1C', filament_type: 'PLA', compatible_printers: [X1C] },
+      ],
+    });
+    expect(pick(by, 'XYZ')).toBe('Bambu PLA Basic @BBL X1C');
+  });
+
+  it('still returns something when only other printers have presets', () => {
+    const by = tiers({
+      standard: [{ name: 'Bambu ABS @BBL A1', filament_type: 'ABS', compatible_printers: [A1] }],
+    });
+    expect(pick(by, 'XYZ')).toBe('Bambu ABS @BBL A1');
+  });
+});

+ 10 - 1
frontend/src/utils/slicePresetPicker.ts

@@ -227,6 +227,7 @@ export function pickFilamentForSlot(
   let bestCompatible: { ref: PresetRef; score: number } | null = null;
   let bestMismatch: { ref: PresetRef; score: number } | null = null;
   let bestWrongType: { ref: PresetRef; score: number } | null = null;
+  let bestWrongTypeMismatch: { ref: PresetRef; score: number } | null = null;
   for (const tier of SLICE_MODAL_TIER_ORDER) {
     for (const p of by[tier].filament) {
       let score = 0;
@@ -240,7 +241,14 @@ export function pickFilamentForSlot(
       score += TIER_BONUS[tier];
       const ref = { source: p.source, id: p.id };
       if (statesDifferentMaterial(p, reqType)) {
-        if (bestWrongType == null || score > bestWrongType.score) {
+        // Still split by printer: with no preset of the plate's material at
+        // all, a wrong-printer pick is refused by the slicer outright, so it
+        // must not beat one the selected printer can take.
+        if (presetCompatibility(p, 'filament', printerName, compatIndex) === 'mismatch') {
+          if (bestWrongTypeMismatch == null || score > bestWrongTypeMismatch.score) {
+            bestWrongTypeMismatch = { ref, score };
+          }
+        } else if (bestWrongType == null || score > bestWrongType.score) {
           bestWrongType = { ref, score };
         }
       } else if (presetCompatibility(p, 'filament', printerName, compatIndex) === 'mismatch') {
@@ -258,6 +266,7 @@ export function pickFilamentForSlot(
   // user can see and change in the dropdown than a null the modal renders as
   // an empty slot, which is what shipped before the partition existed.
   if (bestWrongType != null) return bestWrongType.ref;
+  if (bestWrongTypeMismatch != null) return bestWrongTypeMismatch.ref;
   // Final fallback when there are no filament presets at all (empty
   // registry) — pickDefault returns null in that case too, but keeping the
   // call mirrors the rest of the picker logic for shape consistency.

File diff suppressed because it is too large
+ 0 - 0
static/assets/ImagePreviewModal-DBfDeu6V.js


File diff suppressed because it is too large
+ 0 - 1
static/assets/PdfPreviewModal-DbpcXkX3.js


File diff suppressed because it is too large
+ 0 - 0
static/assets/SpreadsheetPreviewModal-DnoCutCQ.js


File diff suppressed because it is too large
+ 1 - 1
static/assets/index-yYp_wKBl.js


File diff suppressed because it is too large
+ 0 - 0
static/assets/pdf-oM5hURjx.js


+ 1 - 1
static/index.html

@@ -26,7 +26,7 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-BeGRJy9y.js"></script>
+    <script type="module" crossorigin src="/assets/index-yYp_wKBl.js"></script>
     <link rel="modulepreload" crossorigin href="/assets/chunk-aKtaBQYM.js">
     <link rel="stylesheet" crossorigin href="/assets/index-Bb4TsUSQ.css">
   </head>

Some files were not shown because too many files changed in this diff