Skip to content

feat(export): slicer-ready 3MF (plate layout, per-part colour/material, slicer sidecars, Bambu .gcode.3mf) - #788

Merged
w1ne merged 11 commits into
developfrom
feat/slicer-ready-3mf
Sep 28, 2026
Merged

w1ne merged 11 commits into
developfrom
feat/slicer-ready-3mf

Conversation

@w1ne

@w1ne w1ne commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

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

  • One <object> per part, with names kept.
  • One <basematerials id="1"> entry per part (name = part name, displaycolor from .color()/.material()). Role tokens such as 'tool' fell back to #CCCCCCFF.
  • The colour was set on every triangle (pid="1" p1="i"). Objects had no pid/pindex.
  • Build items had no transform, so parts stayed at their raw world-frame positions: they were not on Z=0 and could overlap.
  • No slicer metadata.

What it writes now

  • Core materials: objects carry pid/pindex at 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, materialName is added to the assemblyPart record metadata, SceneBackendPart and WorldFramePart.
  • options.arrange:
    • none (default): same positions as before.
    • plate: one build item per part, dropped to Z=0 and shelf-packed on the printer bed 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 (plate only): 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.config with the name and filament slot (extruder) for each object and each part, all on plate 1.
    • prusa: Metadata/Slic3r_PE_model.config with one volume per part by triangle range.
    • Slot N is the Nth distinct (material, colour) pair.
    • On purpose, there is no 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_printer with bambu-lan: the upload is now a .gcode.3mf. The G-code goes in as Metadata/plate_1.gcode (plus an .md5), which is the path the existing MQTT project_file command already names. It is packed inside the model 3MF given as model_3mf_path (new; also kernelcad print send --model-3mf), or else inside a minimal core-3MF shell. The file name always ends in .3mf.
  • Attribution: the model's core Application metadata is now attributionGenerator() from src/shared/links/attribution.ts (kernelCAD <v> (https://kernelcad.com)), plus Description = Made with kernelCAD (https://kernelcad.com). A test reads both back.
  • Tool prose, kernelcad-mcp and kernelcad-print skills, the public-contract fixture, and a new slicer-ready-3mf cookbook snippet.

Tests

  • tests/unit/kernel/backends/occt/export3mfSlicer.test.ts (12): parses the XML with a namespace-aware parser and checks:
    • core namespace, the basematerials resource, and valid object-level pid/pindex and build refs
    • colours round-trip (hex and role token)
    • plate: parts on Z=0, inside the bed, no XY overlap, also when parts overflow one row
    • orient lays a slab flat and leaves a part that is already flat unchanged
    • assembled keeps relative offsets
    • the bambu and prusa sidecars match the model, and generic writes none
    • bad arrange/slicer values are rejected
    • the material name reaches the file end-to-end via runAndExport
  • tests/unit/kernel/print/bambuPrintFile.test.ts (4) and 2 new send_to_printer cases.
  • 12 of the new tests fail on develop. The other 6 are regression guards and pass on both.
  • The existing 3MF tests and the eval harness pass.

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 assembled mode they load as one object with N parts. The bambu sidecar 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.

@w1ne
w1ne merged commit 528012a into develop Sep 28, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant