feat(slicer): carry the printer's printable_area on UnifiedPreset - #36
Merged
Conversation
Prerequisite for maziggy#67. Both halves of that epic need the selected printer's bed size and Bambuddy could not obtain it anywhere: the `Printer` model has no dimensions, `PRINTER_MODEL_MAP` has none, and `printerPresetAxes.ts` states outright that the preset NAME is the only carrier. The only bed geometry Bambuddy reads today comes out of a 3MF, which is why an STL and a bedless 3MF both render on a hardcoded 256x256 plate. `UnifiedPreset` now carries `printable_area` for the printer slot: the bed outline as the slicer's own polygon of `"<x>x<y>"` corner points in bed millimetres, e.g. `["0x0","350x0","350x320","0x320"]` for an H2D. Populated per tier — standard from the sidecar (jappyjan/orca-slicer-api#4, resolved through the bundled profile's `inherits:` chain), orca_cloud and local from the profile content each already holds, cloud across the same name bridge `filament_vendor` rides (maziggy#58). Carried raw rather than reduced to width/height: 8 profiles in OrcaSlicer's vendor tree declare 72-point round delta beds, 3 declare 6 points and 3 declare 239, and flattening any of those would hand the viewport a rectangle that lies about where a model may be placed. Consumers reduce it themselves. Only the container shape is normalised — a polygon written as one comma-joined string is split and stray whitespace inside a point is trimmed, both of which occur verbatim in the bundled trees. No hardcoded model->bed map: one existed and was deliberately removed in favour of reading `printable_area` (CHANGELOG:1173). There is consequently no name-parse fallback for this field the way there is for `filament_vendor` — a bed cannot be guessed from a string, so an unresolvable one stays `None`. **Degrades to `None` on every sidecar that has not been rebuilt**, which is the state each deployment is in today. That is the normal state, not an error state, and it is tested explicitly: the old `{name, base_id}` response still yields a complete listing with the bed simply absent. `None` stays distinguishable from a bed of size zero throughout — fewer than three usable points reads as "no bed known", never as a zero-area one, because the first means "fall back to the default plate" and the second means "place everything at the origin". Consuming the value is #69's job; nothing in the viewport is touched here. Refs #68, maziggy#67 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Prerequisite for maziggy#67 (bed.1). Both halves of that epic need the selected printer's bed size, and Bambuddy could not obtain it anywhere — the
Printermodel has no dimensions,PRINTER_MODEL_MAPhas none, andprinterPresetAxes.tssays outright that the preset NAME is the only carrier. The only bed geometry Bambuddy reads today comes out of a 3MF, which is why an STL and a bedless 3MF both render on a hardcoded 256×256 plate.UnifiedPresetnow carriesprintable_areafor the printer slot.Pairs with jappyjan/orca-slicer-api#4, which makes the sidecar emit it. This PR is safe to merge before that image is rebuilt — see the degrade section.
Shape
The slicer's own polygon of
"<x>x<y>"corner points in bed millimetres:{"id": "Bambu Lab H2D 0.4 nozzle", "name": "…", "source": "standard", "printable_area": ["0x0", "350x0", "350x320", "0x320"]}Not a width/height pair, and deliberately not reduced here. Across OrcaSlicer's full vendor tree, 597 profiles declare the key: 582 with 4 points, 8 with 72 (round delta beds), 3 with 6, 3 with 239. Flattening any of those would hand the viewport a rectangle that lies about where a model may be placed. Consumers reduce it themselves.
Only the container shape is normalised, never the geometry — both of these occur verbatim in the bundled trees:
"0x256 "onBambu Lab X2D 0.4 nozzle)Per tier
printable_area, resolved through the bundled profile'sinherits:chainLocalPreset.setting, the stored resolved blobThe bridge is the same one
filament_vendorrides (maziggy#58), and it matters more here: only the standard tier resolves a real bed, so without it the bed a printer reports would depend on which tier happened to win dedup — invisible to the user and not something they chose. It only ever fills a gap; an entry that states its own bed keeps it.No hardcoded model→bed map. One existed and was deliberately removed in favour of reading
printable_area(CHANGELOG:1173). There is consequently no name-parse fallback the way there is forfilament_vendor— a bed cannot be guessed from a string, so an unresolvable one staysNone.Degrades to
Noneon every sidecar that has not been rebuiltThat is the state each deployment is in today, and it is the normal state rather than an error state. Tested explicitly: the old
{name, base_id}response still yields a complete listing with the bed simply absent — no raise, no dropped printer, no invented bed.Nonestays distinguishable from a bed of size zero throughout. Fewer than three usable points reads as "no bed known", never as a zero-area one, because a consumer treats the first as "fall back to the default plate" and the second as "place everything at the origin".Scope
Nothing in the viewport is touched — no
ModelViewer, noPlateStage, noSlicerPage. Consuming this value is #69. The one frontend change is the additive optional field on theUnifiedPresetinterface inapi/client.ts, so #69 does not need a type change to read it.Tests
22 new cases in
backend/tests/unit/test_slicer_presets.py: the normaliser (rectangle, hexagon, origin-offset bed, comma-joined string, whitespace, junk, sub-polygon), each tier, the old-sidecar degrade, the name bridge, and serialisation throughUnifiedPresetsResponse.2 failed, 7454 passed— the two failures are the known pre-existingtest_extract_video_last_frameones (local ffmpeg missinglibx265), unrelated.214 files passed, 2888 tests passed.npx eslint .→ exit 0 andnpx tsc --noEmit -p tsconfig.app.json→ exit 0, run as separate commands with separately checked exit codes (test_frontend.shends withcd ..and exits 0 regardless).ruff checkclean.ruff formatalso normalised two pre-existing blocks in the test file — that file already failedruff format --checkone018c336, so this is 6 lines of incidental cleanup, not new churn.tests/integration/test_notifications_api.pystill fails the check and is untouched here.Mutation checks — all five bit:
>= 3point guard removedTwo things #69 should know
backend/app/api/routes/archives.py:3011-3027, turning a 3MF'sprintable_areainto abuild_volume. It independently uses the samelen(...) >= 3guard. Two caveats before reusing it: it parses coordinates withint(), so a fractional coordinate is silently skipped; and it takes onlymax_x/max_ywith nomin, so an origin-offset bed loses its offset.printable_heightis not in this PR. #68 scoped toprintable_area, but that same reducer usesprintable_heightfor the z axis, and today's fallback is a hardcodedz: 256. It resolves from the identical sidecar walk at zero extra cost (44/44 and 56/56). If it is wanted, it should be decided before the sidecar image is rebuilt — adding it afterwards costs a second foreground rebuild.No image was rebuilt or pushed.
🤖 Generated with Claude Code