Slicer — how it should be¶
This page collects everything the repo itself says about where the Slicer is heading, and nothing more. Every item is tagged:
- HOW IT IS — verified code truth, with a
file:linecitation. - STATED — written down in a doc, bead, memory, or ADR. The citation is the claim; this page adds no interpretation.
- INFERENCE — a reading of the evidence, marked bold. Not a plan, not a promise.
There is currently no open implementation epic for the Slicer. The parity
pass nps-3a3 is closed, the nps-n1m honesty/API epic is closed, and the
only open product-facing bead is nps-0m7 (IN_PROGRESS, P2) — a docs bead
(this site). Everything below is direction recorded at close-out time, not
scheduled work.
Stated direction¶
Product statement¶
The README defines what the Slicer is for:
| Statement | Source |
|---|---|
| Slicer: load a mesh, compute scalar fields, extract layers and toolpaths, preview and export G-code for non-planar printing. | README.md:12-15 |
| Monorepo = Non-Planar Slicer + companion Simulator; shared UI kit, 3D viewport, one engine. | README.md:3 |
Note the tension this sets up: the product goal is non-planar printing, while the shipped generator is a demo toolpath with a non-planar toggle. The repo handles this by labeling, not by pretending — see the next section.
The parity pass landed¶
The Slicer's stated near-term target — feature parity with the Simulator shell, minus playback — is done:
| Claim | Source |
|---|---|
| Parity pass: drop Simulator-only playback/mesh loop; Slice params UI; full toolpath draw; overlays/findings like Simulator; no direct P/Invoke in SlicerApp. | bead nps-3a3 (CLOSED); git 20c54fa |
The window title still advertises the parity baseline: "...(f8v.6.1 parity)". |
src/SlicerApp/MainWindow.axaml:7 |
| The XAML header comment ties the shell layout to the parity bead. | src/SlicerApp/MainWindow.axaml:11 |
Playback is intentionally absent: IsPlaybackTabVisible="False"; the panel is "intentionally omitted (nps-3a3 AC1)". |
src/SlicerApp/MainWindow.axaml:20, :89 |
Parity was a one-time catch-up, not a direction. What comes after is defined by the honesty policy below.
The toolpath is a demo — and says so on four surfaces¶
GenerateGcode emits a demo toolpath (square perimeter + 1–2 diagonal
infill lines per layer, sized from real mesh bounds, extrusion modulated by a
sampled scalar field). It is not a production slice, and the repo's stated
policy is that this fact must be visible everywhere a user could look:
| Surface | What it says | Source |
|---|---|---|
| Engine header | "GenerateGcode emits a DEMO TOOLPATH … NOT a production slice." | src/Engine/engine.h:150-156 |
| Implementation comment | nps-n1m.19 honesty block: no wall loops, bridges, skin, overhang handling, or slicer-grade heuristics exist. | src/Engine/engine.cpp:1197-1216 |
| Emitted G-code header | "; demo toolpath (nps-n1m.19)" / "; NOT a production slice." |
src/Engine/engine.cpp:1314-1316 |
| Slicer status string | "Slicer parity loaded (DEMO TOOLPATH — not a production slice): …" |
src/SlicerApp/MainWindow.axaml.cs:316 |
| Simulator status string | "Loaded Benchy (DEMO TOOLPATH - not a production slice): …" |
src/SimulatorApp/MainWindow.axaml.cs:313 |
| Host facade | Method is named GenerateDemoToolpath; doc comment restates the label. |
src/Nps.EngineHost/EngineSession.cs:248-253 |
| Docs | architecture.md lists exactly these four user-visible surfaces; README's core flow labels the gen step DEMO TOOLPATH. | docs/architecture.md:378-393; README.md:84; docs/modules/session.md:28-30 |
The public symbol stays GenerateGcode for ABI stability; the honesty lives
in labels. This was the recorded Decision A of bead nps-n1m.19 (CLOSED).
The generate surface stays tiny — depth grows behind, not beside¶
Until a real geometry slicer exists, the generate contract is deliberately a deep, narrow module — four operations:
| Op | Role |
|---|---|
GenerateGcode(params) |
generate toolpath from loaded mesh + params |
GenerateGcodeToBuffer(...) |
serialize last-generated G-code into a caller buffer |
WriteGcodeToFile(params,...) |
serialize G-code straight to a file |
GetLastGeneratedGcode() |
read the engine-owned result text |
The stated rule (docs/architecture.md:395-425, bead nps-n1m.20 CLOSED):
- Depth grows behind
GenerateGcode, not beside it. A real slicer lands insideslice/behind the samegenerate(params)entry point; the public op count does not grow. - No getter-of-the-week. No new shallow generate helper may be added without a recorded use-case review.
- The only sanctioned way to widen the surface is a real geometry slice justification landing with a superseding bead.
The rationale, stated in the same section: a wide fake-slicer facade would advertise capability the engine does not have.
The real slicer has a reserved home and a reserved name¶
The target — not today's tree — is recorded in two places:
| Target | Source |
|---|---|
src/Engine/slice/ — "L0: layer/toolpath extraction (planned)"; in the class-to-folder map: "(planned; not yet a separate class)". |
docs/architecture.md:124, :138 |
Symbol: nps::Engine::Slice (or module-folder equivalent) — name TBD. GenerateGcode keeps its name. |
docs/naming.md:56 |
docs/naming.md:46-56 is explicit that these TARGET names describe a future
artifact and are "not the canonical name of anything that ships today."
The loader was built for the real slicer's needs¶
The stated rationale for the custom 3MF loader is forward-looking: it was
built to support real slicer extensions — e.g. Bambu paint_color
(multi-material painting), Filament-style streaming, and a deep engine —
rather than only today's demo path (memory
nps-l7k-13-docs-custom-3mf-loader-rationale). In other words, the input
pipeline is not the bottleneck the roadmap is waiting on; the slice/ module
is.
What the Slicer does not get¶
The shell contract draws the product line explicitly:
| Statement | Source |
|---|---|
Simulator only paints the loaded mesh via MeshController + MeshCanvasPainter; the Slicer "stays toolpath-focused and skips the background mesh." |
docs/ui-shell-and-viewport.md:112-118 |
| The parity pass deliberately dropped Simulator-only playback/mesh concerns rather than copying them over. | bead nps-3a3 (CLOSED) |
Sharing direction that touches the Slicer¶
The share-vs-split analysis (see Sharing plan) reaches Slicer-specific verdicts from today's code:
| Verdict | Source |
|---|---|
Slicer is already ahead on shell consumption: it paints via GcodeToolpathPainter and projects findings via FindingFormatter.ProjectFromSession. |
src/SlicerApp/MainWindow.axaml.cs:622-631, :400-405 |
| Slice params harvest / Apply / warp grids stay Slicer-owned — they will grow with real slicing. | src/SlicerApp/MainWindow.axaml.cs:192-246, :321-363 |
| The load/generate pipeline stays split between the apps: a forced shared pipeline would freeze both on the demo toolpath. | sharing analysis, block G verdict |
ADR 0002 (Accepted) would collapse both apps into one binary with ProductMode {Slicer, Simulator} and an IProductMode panel seam; no implementation beads exist yet. |
docs/adr/0002-one-binary-product-mode.md:69-109 |
Inference¶
Everything below is INFERENCE — a reading of the evidence, not a stated plan.
Not scheduled work
None of the following has an open bead. Treat each as context for reading the STATED section, not as a roadmap.
-
INFERENCE — the real geometry slicer is deferred policy, not scheduled work. The target folder, target symbol, and tiny-API rule are all recorded (
docs/architecture.md:111-138,:395-425;docs/naming.md:55-56), but no open bead exists for a real slice. The Slicer UI generates the demo toolpath viaGenerateDemoToolpath(src/Nps.EngineHost/EngineSession.cs:248-253). Until a justification bead appears,slice/is a reserved seat, not a project. -
INFERENCE — the non-planar product goal currently resolves to a demo toggle. The README promises G-code for non-planar printing (
README.md:14); the shipped knob isSliceParams.EnableNonPlanar, whose own doc comment reads "When true, engine generates a non-planar demo toolpath" (src/SlicerApp/SliceParams.cs:70-74). The flag is real and exercises the pipeline, but the distance between it and the product statement is exactly the distance the futureslice/module must cross. -
INFERENCE — a ProductMode collapse would reshape this app. ADR 0002 is Accepted with follow-up implementation beads promised (
docs/adr/0002-one-binary-product-mode.md:183-186), but no open child beads exist and the tree still buildsSlicerApp+SimulatorApp. If the collapse lands, the Slicer stops being an app and becomes a mode behind anIProductModeseam — which reframes every STATED item here that says "the Slicer owns." -
INFERENCE — parity is a plateau, not a trajectory. The parity pass is closed and the docs record no next Slicer-specific UI epic. The slice params sidebar is the one surface explicitly expected to grow (sharing analysis, block R verdict), and it grows only when the engine behind it stops being a demo. Slicer UI work and engine slice work are gated on the same missing bead.
Related pages¶
- Slicer — how it is — the verified current state.
- Sharing plan — which blocks should move to the shell, stay per-app, or stop being cloned.
- Simulator — how it should be — the sibling target-state page.
- Site index