3mf-print-editor
Skill12 starsChanged 50 days agoMITView on GitHub
Guideline for directly creating or editing 3D-printing project files (.3mf), verified against BambuStudio specifically: generating or modifying mesh geometry, wiring objects into the 3MF XML correctly, arranging multiple print plates in BambuStudio's shared multi-plate coordinate system, setting compatible print/project parameters, and validating the result in the target BambuStudio application. Use whenever a user asks to create, edit, cut, split, merge, reposition, rearrange, or reconfigure a BambuStudio .3mf project file programmatically instead of through the slicer GUI. Other slicers (OrcaSlicer, PrusaSlicer, etc.) use similar-looking .3mf files but are NOT verified to follow the same plate-offset math or config schema — treat this skill's multi-plate guidance as BambuStudio-specific.
- Installs packages
In this repository37 other files it gives its agents.
- aegis-protocolSkill
- ai-address-parser-debugSkill
- ai-address-parserSkill
- ai-csuiteSkill
- browser-driverSkill
- chatgpt-web-delegationSkill
- chinese-humanizerSkill
- claude-agent-sdk-authSkill
- close-loopSkill
- de-ai-englishSkill
- dev-inboxSkill
- dokploy-deploySkill
- dokploy-upgradeSkill
- ecommerce-golive-checkSkill
- fork-upstream-syncSkill
- foxcode-openclawSkill
- git-cryptSkill
- git-worktree-decisionSkill
- handoff-receiverSkill
- image-prompt-engineerSkill
- init-rulesSkill
- landing-launch-checkSkill
- linuxdo-applicationSkill
- npm-publishSkill
- omnidebug-autopilotSkill
- psychology-masterSkill
- refactor-agents-mdSkill
- research-to-wechatSkill
- right-sized-solutionSkill
- rtk-token-saverSkill
- session-handoffSkill
- shared-memorySkill
- stalwart-dokploy-resend-relaySkill
- typed-decisions-around-llmsSkill
- web-compliance-builderSkill
- wechat-compliance-checkSkill
- wordpress-vps-installSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
18 rules. About 0 are common, 5 are distinctive, and 13 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
13
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 36
Do NOT use this skill just to *view* or *slice* a file — only when the file itself needs to be programmatically modified and re-saved.
- Very specific: line 55
If pip3 and python3 resolve to different interpreters (common on macOS with multiple Homebrew Python versions), install and run with the …
- Very specific: line 68
Read references/3mf-structure.md for the full file-by-file breakdown.
- Very specific: line 80
Never assume a schema detail — when unsure, verify against the actual BambuStudio source (src/libslic3r/Format/bbs_3mf.cpp, …
- Very specific: line 106
Use scripts/mesh_tools.py (split_mesh_plane) to do steps 3–4 with the checks built in instead of re-deriving this from scratch each time.
- Distinctive: line 115
When a cut/split produces N new sub-meshes that must become N separate placeable objects:
- Very specific: line 117
Write each mesh to its own 3D/Objects/object_<id>.model file with a unique numeric object id, following the existing file's template …
- Distinctive: line 119
Keep IDs unique across the *whole package* — IDs are global, not per-file.
- Very specific: line 123
Add a <Relationship Target="/3D/Objects/object_<id>.model" .../> for every new object file to 3D/_rels/3dmodel.model.rels — a missing …
- Very specific: line 125
Update Metadata/model_settings.config: one <object id="..."> block per top-level object (with a nested <part> for the mesh + mesh_stat …
- Very specific: line 154
Read the bed size from Metadata/project_settings.config's printable_area (four "XxY" corner strings) and printable_height.
- Distinctive: line 189
When a user asks to "改打印设置" (change print settings), find the exact key(s) in this JSON, change only those values, and leave the rest of …
- Very specific: line 192
Treat configuration enum values as versioned application data, not generic slicer vocabulary.
- Distinctive: line 203
Always run these checks on the rebuilt package before handing it back:
- Very specific: line 220
Open in the target BambuStudio GUI: when it is installed and the user intends to open the file there.
- Very specific: line 224
Use scripts/mesh_tools.py's validate_package(dir) to run 1–3 automatically over an extracted tree.
- Very specific: line 228
Never overwrite the user's original file in place.
- Very specific: line 243
Forgetting to update
3D/_rels/3dmodel.model.rels: when adding new Objects/object_N.model files.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
3MF Print Editor
Guideline for hand-editing 3D-printing files — Bambu Lab's .3mf project files, as opened/sliced by BambuStudio
— when the user wants a model cut, split across plates, repositioned, merged, or reconfigured programmatically
rather than through the slicer GUI. Covers three things end to end: editing the mesh correctly,
building/arranging plates correctly, and setting print parameters correctly. Read references/3mf-structure.md
and references/plate-coordinate-system.md before editing a multi-plate project — the plate math is not obvious and
is the single most common source of silent failure (see Pitfall #1).
Scope disclaimer: this skill is verified against BambuStudio only. Everything here — the
Metadata/model_settings.configschema, the multi-plate shared-scene offset formula in §4, and theproject_settings.configkeys in §5 — was reverse-engineered from BambuStudio's own source (bambulab/BambuStudio) and confirmed against real BambuStudio behavior. OrcaSlicer, PrusaSlicer, and other tools also read/write.3mf, and OrcaSlicer's is close (it's a BambuStudio fork), but their exact plate-offset constants, config keys, or metadata schema are not guaranteed to match and have not been verified here. If the user's target application isn't BambuStudio, say so explicitly, and re-derive/verify the relevant constants against that application's own source or behavior before applying this skill's formulas — do not assume they transfer as-is.
When to use this skill
- "把这个模型从中间切开/分成两半" — cut/split a model into pieces
- "分别放到两个盘里" / "put these on separate plates" — multi-plate arrangement
- "调整模型位置/旋转/缩放" — reposition, rotate, scale an object in a project file
- "改一下打印设置" — change bed size, plate count, filament/printer profile in a project file
- "帮我重新打包这个 3mf" — merge multiple STL/3MF sources into one project
- "直接生成 3mf,我在 Bambu Studio 里打开" — create a new BambuStudio project from generated meshes
- Any task that requires opening a
.3mfas a zip and hand-editing its XML/config instead of using the slicer UI
Do NOT use this skill just to view or slice a file — only when the file itself needs to be programmatically modified and re-saved.
Environment setup
Mesh editing needs trimesh plus its optional dependencies for boolean ops, capping, and polygon repair. On macOS
with a Homebrew Python, the interpreter that actually has these packages may differ from the default python3
(check with python3 -c "import sys; print(sys.executable)" vs which -a python3.*). Install once per environment:
python3 -m pip install trimesh numpy scipy shapely networkx rtree manifold3dVerify:
python3 -c "import trimesh, numpy, scipy, shapely, networkx, rtree; print('ok', trimesh.__version__)"If pip3 and python3 resolve to different interpreters (common on macOS with multiple Homebrew Python versions),
install and run with the exact interpreter path that has the packages, not just python3.
1. Understand the file before touching it
A .3mf is a standard OPC (Open Packaging Conventions) zip. Always extract and inspect first:
mkdir -p /tmp/3mf_work && cd /tmp/3mf_work
unzip -o "/path/to/model.3mf" -d extracted
find extracted -type fRead references/3mf-structure.md for the full file-by-file breakdown. Key files:
| File | Purpose |
3D/3dmodel.model | Top-level resources (component references) + <build> list of placed instances |
3D/Objects/object_N.model | Actual mesh geometry (vertices/triangles) for object id N |
3D/_rels/3dmodel.model.rels | OPC relationships — must list every Objects/object_N.model referenced |
Metadata/model_settings.config | Bambu/Orca project metadata: object names, per-object part metadata, <plate> blocks (which objects belong to which plate), <assemble> block |
Metadata/project_settings.config | Full print/printer/filament settings as flat JSON (bed shape, height, temps, speeds, etc.) |
Metadata/cut_information.xml | Bambu's native "Cut tool" state — only relevant if you want the GUI cut-tool to recognize a cut; safe to leave empty (<objects></objects>) for programmatic edits |
Metadata/plate_N.png etc. | Thumbnails — cosmetic only, stale thumbnails do not break loading |
Never assume a schema detail — when unsure, verify against the actual BambuStudio source
(src/libslic3r/Format/bbs_3mf.cpp, src/slic3r/GUI/PartPlate.cpp in bambulab/BambuStudio on GitHub) rather than
guessing. This skill's plate-offset math (Pitfall #1) was reverse-engineered from that source after a first attempt
silently failed.
2. Editing the mesh correctly
For real geometry edits (cutting, boolean, hollowing, etc.), never hand-edit vertex/triangle XML — parse it into a proper mesh library, edit, then re-serialize.
- Parse the object's vertices/triangles from
3D/Objects/object_N.model(or the sole object file when there's no multi-part split) usingxml.etree.ElementTree, build atrimesh.Trimesh(vertices=..., faces=..., process=False). - Determine the cut/split plane from the mesh's actual bounding box — don't guess an axis. Print
mesh.boundsandmesh.extentsfirst. For "cut in half", pick the plane through the midpoint of the longest horizontal extent (or the axis the user specifies), and prefer a plane that lands on an already-symmetric midpoint (e.g. bounds like[-127, 127]) since that's almost always the model's natural centerline. - Cut with capping so each half stays a valid solid:
half_a = trimesh.intersections.slice_mesh_plane(mesh, plane_normal=[0,1,0], plane_origin=[0,0,0], cap=True) half_b = trimesh.intersections.slice_mesh_plane(mesh, plane_normal=[0,-1,0], plane_origin=[0,0,0], cap=True) - Always verify before packaging:
half_a.is_watertight and half_b.is_watertightmust both beTrue.abs(half_a.volume + half_b.volume - mesh.volume) < 1e-3(volume conservation — proves no geometry was lost or duplicated at the cut).- Bounds of each half only extend on the expected side of the cut plane.
- Use
scripts/mesh_tools.py(split_mesh_plane) to do steps 3–4 with the checks built in instead of re-deriving this from scratch each time.
See references/mesh-editing-notes.md for capping caveats, common trimesh dependency errors and fixes
(scipy/shapely/networkx/rtree ImportErrors surface one at a time — install all five up front), and how to
handle non-watertight source meshes.
3. Wiring edited objects back into the 3MF XML
When a cut/split produces N new sub-meshes that must become N separate placeable objects:
- Write each mesh to its own
3D/Objects/object_<id>.modelfile with a unique numeric objectid, following the existing file's template (<model>root with the same namespaces, one<object type="model"><mesh>...per file). Keep IDs unique across the whole package — IDs are global, not per-file. - In
3D/3dmodel.model, give each new sub-mesh a wrapping top-level<object type="model">with a<component>pointing at its.modelfile, then add one<item objectid="...">per object to<build>with the placement transform (see §4 for the coordinates that transform must use). - Add a
<Relationship Target="/3D/Objects/object_<id>.model" .../>for every new object file to3D/_rels/3dmodel.model.rels— a missing relationship can cause silent load failures in strict parsers. - Update
Metadata/model_settings.config: one<object id="...">block per top-level object (with a nested<part>for the mesh +mesh_stat face_count=...), one<model_instance>per plate assignment inside the matching<plate>block, and matching<assemble_item>entries in<assemble>using the same transform as the build item. - Reset or clear
Metadata/cut_information.xml(<objects></objects>) when you replace geometry outside the GUI's own cut tool, so stale connector/cut metadata doesn't get misapplied to the new geometry. [Content_Types].xmlnormally needs no change (it declaresDefault Extension="model"which covers any new.modelfile automatically) — only touch it if you introduce a genuinely new file extension.
4. Building/arranging plates correctly (read this before any multi-plate edit)
BambuStudio-specific. The constants and behavior below (
LOGICAL_PART_PLATE_GAP = 0.2, the shared-scene layout) are taken directly from BambuStudio's source and confirmed in BambuStudio. They are likely similar in OrcaSlicer (a BambuStudio fork) but have not been verified there, and are not guaranteed at all for PrusaSlicer or any other.3mfconsumer. Do not present this formula as universal to "3D printing" or "3MF" in general — it is a property of this specific application's renderer/loader.
This is the part that silently fails if you skip it. BambuStudio renders every plate in one shared 3D scene, not as independent origin-0 canvases. Every plate after the first is offset along X (and wraps to a new row after the configured column count) by:
stride_x = bed_width * 1.2 # LOGICAL_PART_PLATE_GAP = 0.2, i.e. a 20% gap
stride_y = bed_depth * 1.2
col = plate_index % cols # plate_index is 0-based; cols defaults such that plates fill one row unless very many
row = plate_index // cols
plate_origin = (col * stride_x, -row * stride_y)Read the bed size from Metadata/project_settings.config's printable_area (four "XxY" corner strings) and
printable_height. For the common 256×256 bed, stride_x = stride_y = 307.2.
The printable-area polygon is not the whole placement constraint. Resolve the selected printer's inherited machine
profile and read bed_exclude_area; project files may omit this key because BambuStudio supplies it from the installed
machine profile. Normalize every mesh to plate-local XY coordinates and test its actual triangle footprint against
each excluded polygon. A bounding box inside printable_area is insufficient. For the verified
Bambu Lab P1S 0.4 nozzle profile, the installed profile overrides the common value with the left-front rectangle
["0x0", "18x0", "18x28", "0x28"]. Long diagonal parts should use the diagonal/orientation that stays clear of
that rectangle, with margin, rather than merely fitting inside 256×256 mm.
Every object's build-item transform (3D/3dmodel.model <item transform="...">) and its matching
<assemble_item transform="..."> in model_settings.config must be in this shared global space: local
plate-centered position plus that plate's plate_origin offset. If you center every object at its own plate's
local origin without adding the offset, every plate's objects land on top of each other at plate 0's location — the
first plate looks "whole" (multiple pieces stacked back together) and every other plate renders empty. This exact
bug is documented with full derivation in references/plate-coordinate-system.md; read it once, then use
scripts/mesh_tools.py's plate_origin(plate_index, bed_width, bed_depth, cols=...) helper so you never
re-derive it by hand.
Assigning which plate an object belongs to (separately from its coordinates) is done via
Metadata/model_settings.config: each <plate> block has a <model_instance> child per object/instance
(object_id, instance_id, identify_id) that belongs on that plate. identify_id can be any positive integer;
it does not need to match anything else in the file (BambuStudio falls back to internal IDs when it's absent/≤0).
5. Setting print/project parameters
Metadata/project_settings.config is flat JSON (not XML) with every printer/filament/print setting as a
key → value (often the value is itself a stringified array/percentage). Common keys to look for:
printable_area,printable_height— bed shape/size (see §4, drives plate offsets)*_plate_temp,*_plate_temp_initial_layer— bed temperature by plate type (cool/eng/hot/supertack/textured)- Standard print-profile keys (layer height, speeds, supports, etc.) — treat this file as the single source of truth for "what will this print with"; do not assume defaults
When a user asks to "改打印设置" (change print settings), find the exact key(s) in this JSON, change only those values, and leave the rest of the (very large) file untouched — do not reformat or reorder it.
Treat configuration enum values as versioned application data, not generic slicer vocabulary. In the currently
verified BambuStudio workflow, "sparse_infill_pattern": "rectilinear" triggers a compatibility warning and is
silently replaced with "cubic". Write "cubic" for this profile unless the user's exact target BambuStudio
version has been positively verified to accept another requested value. Do not copy enum values blindly from an
older template just because the JSON is syntactically valid.
Metadata/slice_info.config and Metadata/filament_sequence.json are usually safe to leave as-is unless the user
is specifically changing slicing/AMS filament-sequencing behavior.
6. Validate before delivering
Always run these checks on the rebuilt package before handing it back:
- XML well-formedness on every
.model/.config/.relsXML file (xml.etree.ElementTree.parse). - Mesh integrity: re-parse each
Objects/object_N.model, confirm vertex/triangle counts are sane and (for solid parts)trimeshreportsis_watertight. - Volume/bounds sanity: for split/cut operations, confirm combined volume matches the source and bounds fall on the correct side of the cut plane for each piece.
- Plate placement math: recompute each object's expected global position from §4's formula and diff it against what you wrote into the build item — don't eyeball it.
- Printable and excluded-area geometry: normalize each object's XY triangles to its plate-local origin, confirm
all geometry lies inside
printable_area, then reject any triangle that touches or intersects the selected machine profile'sbed_exclude_area. Resolve inherited installed profiles when the project JSON omits the key. - Repack as a proper zip (
[Content_Types].xmlat minimum should be present; ordering isn't strict but keep the file complete) and re-extract it fresh to confirm the zip itself isn't corrupt. - Project-settings compatibility: parse
Metadata/project_settings.configas JSON and reject known stale or unsupported enums. In particular, rejectsparse_infill_pattern=rectilinear; usecubicfor the verified P1S profile. - Open in the target BambuStudio GUI when it is installed and the user intends to open the file there. A clean
ZIP/XML check or
--infoparse is not enough: confirm that opening the project produces no compatibility-value replacement dialog. Treat any replacement warning as a failed validation, fix the setting, rebuild, and reopen.
Use scripts/mesh_tools.py's validate_package(dir) to run 1–3 automatically over an extracted tree.
7. Non-destructive output
Never overwrite the user's original file in place. Write the result to a new file (e.g. name_edited.3mf or
name_split.3mf) alongside the original, tell the user exactly what changed, and only overwrite the original if
they explicitly ask you to.
8. Common pitfalls
- Multi-plate objects overlapping on plate 1, other plates empty — you used plate-local coordinates instead of adding the shared-scene plate offset. Fix per §4.
ModuleNotFoundErrorcascade when callingtrimesh.intersections.slice_mesh_plane(..., cap=True)— trimesh lazily importsscipy, thenshapely, thennetworkx, thenrtreeone at a time; install all four (plusmanifold3dfor booleans) up front instead of iterating on import errors.python3without the packages —pip installandpython3can point at different interpreters on multi-Python-version macOS setups; always verify with the same interpreter you'll run edits with.- Cut plane guessed instead of measured — always print
mesh.bounds/mesh.extentsfirst; don't assume which axis is "the middle" of an asymmetric or unfamiliar model. - Forgetting to update
3D/_rels/3dmodel.model.relswhen adding newObjects/object_N.modelfiles. - Leaving stale
cut_information.xmlpointing at old part/connector IDs after replacing geometry outside the GUI cut tool. - Editing
project_settings.configby reformatting the whole file — it's huge; touch only the specific keys requested. - BambuStudio says a setting came from a newer version and replaces
rectilinearwithcubic— the JSON enum is incompatible even though the package is structurally valid. Writecubic, rebuild the 3MF, and reopen it in the target GUI to verify that the warning is gone. - BambuStudio says an object is too close to the exclusion/shielded area — checking only the bed's outer bounds
missed the machine-specific
bed_exclude_area. Resolve the active printer profile (including inheritance), test the actual plate-local triangle footprint, then rotate or translate the object away from the excluded polygon.
References
references/3mf-structure.md— full file-by-file breakdown of a Bambu/Orca.3mfpackagereferences/plate-coordinate-system.md— full derivation of the multi-plate offset formula, with BambuStudio source citationsreferences/mesh-editing-notes.md— trimesh capping/watertight/dependency notesscripts/mesh_tools.py— reusable helpers: extract/repackage a 3mf, split a mesh with validation, compute a plate's origin offset, validate an extracted package
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

