feat(export): slicer-ready 3MF (plate layout, per-part colour/material, slicer sidecars, Bambu .gcode.3mf) - #788
Merged
Merged
Conversation
# Conflicts: # tests/fixtures/mcp/toolRegistry.publicContract.json
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.
Slicer-ready 3MF export: one file that opens in a desktop slicer with every part as a named object, its colour and material kept, the parts laid out on the plate, and optionally a filament slot per colour.
What the 3MF wrote before
<object>per part, with names kept.<basematerials id="1">entry per part (name = part name,displaycolorfrom.color()/.material()). Role tokens such as'tool'fell back to#CCCCCCFF.pid="1" p1="i"). Objects had nopid/pindex.What it writes now
pid/pindexat object level. There is one<base>per distinct (name, colour), and the name is the part's engineering material (arm.part(..., { material: 'pla' })) or else the part name. Role tokens resolve through the shared palette. To support this,materialNameis added to theassemblyPartrecord metadata,SceneBackendPartandWorldFramePart.options.arrange:none(default): same positions as before.plate: one build item per part, dropped to Z=0 and shelf-packed on theprinterbed with 5 mm gaps and no overlap.assembled: one multi-part (components) object for multi-colour inlays, dropped to Z=0 and centred on the bed.options.orient(plateonly): turns each part so the largest flat face it can rest on faces down. A face counts only if no vertex lies beyond it, so a cup's inner floor never wins.options.slicer:generic(default): no sidecar.bambu/orca(same format):Metadata/model_settings.configwith the name and filament slot (extruder) for each object and each part, all on plate 1.prusa:Metadata/Slic3r_PE_model.configwith one volume per part by triangle range.project_settings.config: a partial one crashes the loader in a real slicer (confirmed locally), and a full one would override the user's presets.send_to_printerwithbambu-lan: the upload is now a.gcode.3mf. The G-code goes in asMetadata/plate_1.gcode(plus an.md5), which is the path the existing MQTTproject_filecommand already names. It is packed inside the model 3MF given asmodel_3mf_path(new; alsokernelcad print send --model-3mf), or else inside a minimal core-3MF shell. The file name always ends in.3mf.Applicationmetadata is nowattributionGenerator()fromsrc/shared/links/attribution.ts(kernelCAD <v> (https://kernelcad.com)), plusDescription=Made with kernelCAD (https://kernelcad.com). A test reads both back.slicer-ready-3mfcookbook snippet.Tests
tests/unit/kernel/backends/occt/export3mfSlicer.test.ts(12): parses the XML with a namespace-aware parser and checks:plate: parts on Z=0, inside the bed, no XY overlap, also when parts overflow one rowassembledkeeps relative offsetsgenericwrites nonerunAndExporttests/unit/kernel/print/bambuPrintFile.test.ts(4) and 2 newsend_to_printercases.develop. The other 6 are regression guards and pass on both.Checked in a real slicer (only partly)
With a locally installed slicer based on the same 3MF loader, all 6 combinations (plate/assembled × generic/bambu/prusa) load as manifold objects. In
assembledmode they load as one object with N parts. Thebambusidecar round-trips per-part names, per-part filament slots (1/2/3) and the plate positions. Not tested: the actual Bambu Studio, OrcaSlicer and PrusaSlicer binaries, and a real Bambu printer.