Slicer — how it is¶
Everything on this page is HOW IT IS: verified against
src/SlicerApp code, cited file:line. Where the repo states an intent
(README, parity notes), it is marked STATED. The Slicer is the lighter
of the two apps; the Simulator is the active
build.
Purpose¶
STATED (README): the Slicer should "Load a mesh, compute scalar
fields, extract layers and toolpaths, preview and export G-code for
non-planar printing" (README.md:14).
HOW IT IS today: the app loads one hard-wired fixture, generates a
demo toolpath — not a production slice — and previews it with
findings and overlays. The code says so itself, twice: a comment
// Demo toolpath, not a production slice. (MainWindow.axaml.cs:260)
and the final status string
"Slicer parity loaded (DEMO TOOLPATH — not a production slice): …"
(MainWindow.axaml.cs:316). The window title is
"NPS Slicer - G-code Preview + Findings + Overlays (f8v.6.1 parity)"
(MainWindow.axaml:7) — preview, findings, overlays. Export is not
implemented.
Shell regions it fills¶
The window is the shared MainWindowShell with Slicer-owned content
plugged into its regions (MainWindow.axaml; layout confirmed by the
AXAML comment at MainWindow.axaml:11–14):
Window (SlicerApp.MainWindow)
└─ MainWindowShell [IsPlaybackTabVisible=False]
├─ LeftRegion: layers slider + slice params + Load/Apply + mesh/warp labels
├─ ViewportRegion: Canvas 600x600 + ViewportToolbar
├─ InspectorRegion: OverlayPanel + FindingsPanel (Playback omitted)
└─ StatusRegion: StatusBar bound to Shell.StatusText
Two things define the Slicer's silhouette:
- A thick left bar. The left region carries the layer slider, all
five slice parameters, both action buttons, and the mesh/warp info
labels (
MainWindow.axaml:23–69). It is bound to the params view model vialeft.DataContext = _sliceParams(MainWindow.axaml.cs:68–72). - No playback tab.
IsPlaybackTabVisible="False"(MainWindow.axaml:20); the shell hidesTabPlayback(MainWindowShell.axaml.cs:180) and the Slicer omits the panel entirely — comment: "Playback panel intentionally omitted" (MainWindow.axaml:89–90). The playback keys are stubbed, not removed (see limitations).
Slice params model¶
SliceParams (src/SlicerApp/SliceParams.cs) is the only view model
in either app — the Simulator has no equivalent. Defaults match the
pre-nps-3a3 hard-coded generate call, so a first Load and an untouched
Apply produce the same toolpath (stated intent: SliceParams.cs:16–17,
34–36):
| Property | Default | File:line |
|---|---|---|
LayerHeight |
0.2m |
SliceParams.cs:40 |
Feedrate |
1800m |
SliceParams.cs:41 |
VolumetricFlowScale |
1.0m |
SliceParams.cs:42 |
EnableNonPlanar |
false |
SliceParams.cs:43 |
NumLayers |
5 |
SliceParams.cs:44 |
The AXAML sets no Value= on the NumericUpDowns; values arrive through
two-way bindings, so the controls initialize from the VM:
| Control | Binding | Min | Max | Step | File:line |
|---|---|---|---|---|---|
SliceLayerHeight |
LayerHeight |
0.05 | 2 | 0.05 | MainWindow.axaml:34–37 |
SliceFeedrate |
Feedrate |
1 | 30000 | 100 | MainWindow.axaml:39–42 |
SliceFlowScale |
VolumetricFlowScale |
0.1 | 5 | 0.05 | MainWindow.axaml:44–47 |
SliceNonPlanar (CheckBox) |
EnableNonPlanar |
— | — | — | MainWindow.axaml:48–51 |
SliceNumLayers |
NumLayers |
1 | 500 | 1 | MainWindow.axaml:53–56 |
Load chain¶
Load + Gen + Analyze (Benchy primary) (OnLoadAnalyzeClick,
MainWindow.axaml.cs:155–189) is the whole app in one button. It does
not harvest the UI first — it runs with whatever is already in
_sliceParams (defaults on first click).
sequenceDiagram
participant UI as MainWindow
participant SP as SliceParams
participant ES as EngineSession
participant VP as ViewportController
UI->>ES: LoadModel(FixtureLocator.FindPrimaryBenchy3Mf())
alt load failed or fixture missing
ES-->>UI: error → Status(...) → return
end
UI->>UI: _modelLoaded = true; MeshInfoLabel.Text
UI->>SP: ToGcodeGenOptions()
UI->>ES: GenerateDemoToolpath(options)
ES-->>UI: GenerateResult (moves)
UI->>VP: SetMoves / SetLayerVisibility / SetCamera / BuildBuffers
UI->>ES: Analyze(MaterialProfileOptions("PLA", 210, 60))
UI->>UI: LoadFindings() → FindingsPanel
loop layers 0..5
UI->>ES: GetWarpGrid(l)
end
UI->>UI: WarpGridLabel.Text
UI->>UI: force _currentOverlayMode = "thermal"
UI->>VP: ApplyOverlay(session, "thermal", false)
UI->>UI: RenderViewport(); Status("Slicer parity loaded (DEMO TOOLPATH — ...)")
The full RunGenerate() sequence lives at MainWindow.axaml.cs:251–318.
Three load-time behaviors are Slicer-specific and easy to miss:
- Warp grid load — layers
0..5are sampled via_session.GetWarpGrid(l)and summarized into the sidebar label (MainWindow.axaml.cs:297, loop at334–336). - Forced thermal overlay — the mode is set to
"thermal"on both the field and the panel, then applied (MainWindow.axaml.cs:305–312). The user did not choose it; load chooses it. - Parity status string — the load ends by announcing itself as a
parity demo (
MainWindow.axaml.cs:315–317).
Apply / Regenerate flow¶
Apply / Regenerate (ApplySliceButton, OnApplySliceClick,
MainWindow.axaml.cs:192–211) is a short gate-then-regenerate:
- Session ready? Else
"Engine session not ready."(194–198). - Model loaded? Else
"Load a model first (Load + Gen + Analyze)."— the_modelLoadedgate (200–204). HarvestSliceParamsFromUi()(209, body at215–246) pulls the live control values into_sliceParams.RunGenerate()(210) — the identical chain shown above.
So the two buttons differ only in when the UI is read: Apply harvests
then generates; Load generates from defaults. That symmetry is deliberate
(STATED: defaults match the old hard-coded call, SliceParams.cs:34–36).
The parameter mapping chain, end to end:
SliceParams |
→ GcodeGenOptions |
→ ABI NpsGcodeGenParams |
|---|---|---|
EnableNonPlanar |
EnableNonPlanar |
enableNonPlanar (1/0) |
LayerHeight |
LayerHeight (float) |
layerHeight |
VolumetricFlowScale |
VolumetricFlowScale (float) |
volumetricFlowScale |
Feedrate |
Feedrate (float) |
feedrate |
NumLayers |
NumLayers |
numLayers (0 = auto from mesh) |
Mapping code: ToGcodeGenOptions() (SliceParams.cs:88–96), ABI
packing in EngineSession.GenerateDemoToolpath
(EngineSession.cs:258–269, struct at NpsAbi.cs:132–141). The app
never names ABI types itself — it is a thin shell over EngineHost
(MainWindow.axaml.cs:14–22, SlicerApp.csproj:20–24).
Warp grid label¶
The sidebar footer carries two info labels: MeshInfoLabel
(MainWindow.axaml:66) and WarpGridLabel (MainWindow.axaml:67,
initial text "WarpGrid: (analyze for full grid)"). After generation,
LoadWarpGrids() samples GetWarpGrid for layers 0..5 only and
updates the label (MainWindow.axaml.cs:334–336). The engine side
documents this as the f8v.6.1 warp grid ABI feed (EngineSession.cs:419).
There is no warp visualization — the label is the entire warp UI.
Limitations¶
All entries are HOW IT IS, each with code evidence. The share-vs-split plan discusses where these go next.
| Limitation | Evidence |
|---|---|
| Demo toolpath, not a production slice | GenerateDemoToolpath call + comment (MainWindow.axaml.cs:260–261); status string (316); engine docs (EngineSession.cs:248–250) |
| No file picker — one mandatory fixture | Only FixtureLocator.FindPrimaryBenchy3Mf(); missing file → error status (MainWindow.axaml.cs:162–167); no OpenFileDialog in SlicerApp |
| Playback tab hidden, panel omitted | IsPlaybackTabVisible="False" (MainWindow.axaml:20); omission comment (MainWindow.axaml:89–90) |
P / Left / Right are no-ops with status |
Keys route to ShellKeybindings.Handle → "Playback unavailable in Slicer." (MainWindow.axaml.cs:136–151, ShellKeybindings.cs:82–113) |
| Layer slider hard-capped at 10 | AXAML Maximum="10" (MainWindow.axaml:28); _maxLayer = 10 (MainWindow.axaml.cs:44) — not driven by mesh layer count |
| Material profile hard-coded | Analyze(new MaterialProfileOptions("PLA", 210, 60)) (MainWindow.axaml.cs:289) — no UI |
| Warp grids sampled for layers 0..5 only | for (int l = 0; l <= 5; l++) (MainWindow.axaml.cs:334–336) |
| Viewport canvas fixed 600x600 | Width="600" Height="600" (MainWindow.axaml:74) |
| Apply requires a prior Load | _modelLoaded gate (MainWindow.axaml.cs:200–204) |
CLI preflight uses NumLayers: 0, UI defaults to 5 |
Program.cs:59–64 vs SliceParams.cs:44 |
No TODO markers
src/SlicerApp contains no TODO / FIXME / HACK comments. The
honesty is in the status strings and parity comments instead
(collected in the source audit, e.g. MainWindow.axaml:11–14,
MainWindow.axaml.cs:552,556).
Where this page sits¶
- Interactions shared with the Simulator (overlay, findings, layer slider, finding selection): Simulator — how it is and the duplication audit.
- Engine call surface used above: UI → engine calls.