Skip to content

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:line citation.
  • 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 inside slice/ behind the same generate(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.

  1. 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 via GenerateDemoToolpath (src/Nps.EngineHost/EngineSession.cs:248-253). Until a justification bead appears, slice/ is a reserved seat, not a project.

  2. 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 is SliceParams.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 future slice/ module must cross.

  3. 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 builds SlicerApp + SimulatorApp. If the collapse lands, the Slicer stops being an app and becomes a mode behind an IProductMode seam — which reframes every STATED item here that says "the Slicer owns."

  4. 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.