Skip to content

Engine calls from the UI

Reference for every path a UI element takes to the native engine. NpsAbi is the sole C# P/Invoke surface for NPSEngine.dll (src/Nps.EngineHost/NpsAbi.cs:3, DllName = "NPSEngine" at NpsAbi.cs:43). Everything above it is one of two facades:

  • EngineSession (src/Nps.EngineHost/EngineSession.cs:67) — owns the engine handle.
  • ViewportController (src/Nps.Shell/ViewportController.cs:117) — owns the G-code viewport handle.

SimulatorApp also uses MeshController (src/Nps.EngineHost/MeshController.cs:71) for mesh snapshots, and the two shell painters split by app: Slicer projects its toolpath through GcodeToolpathPainter, Simulator projects its background mesh through MeshCanvasPainter (its toolpath still paints direct — see the last section). Export names below are verified against the [DllImport] declarations in NpsAbi.cs.

flowchart TD
    UI[Button / slider / scrubber / canvas drag] --> APP[MainWindow.axaml.cs handler]
    APP --> ES[EngineSession]
    APP --> VC[ViewportController]
    APP --> MC[MeshController]
    APP --> PT[GcodeToolpathPainter / MeshCanvasPainter]
    ES --> ABI[NpsAbi P/Invoke]
    VC --> ABI
    MC --> ABI
    PT --> ABI
    ABI --> ENG[NPSEngine native]

HOW IT IS: SlicerApp never calls NpsAbi directly — rg "NpsAbi\." src/SlicerApp returns nothing; even its CLI preflight goes through EngineSession (src/SlicerApp/Program.cs:22,27,35,59). SimulatorApp has two deliberate exceptions, covered in the Simulator-only section below.

EngineSession → NpsAbi exports

Facade method NpsAbi export(s) Citations
EnsureAbiMatchesHost() GetAbiVersion (compared to ExpectedAbiVersion = 1, NpsAbi.cs:238) EngineSession.cs:104, NpsAbi.cs:241
CreateAndInitialize() / Initialize() CreateEngine, InitializeEngine EngineSession.cs:165,184,191, NpsAbi.cs:224,230
EngineVersion (property) GetEngineVersion EngineSession.cs:125, NpsAbi.cs:233
LoadModel(path) LoadModelFromFile; on failure GetLastErrorMessage; on success GetPlateCount, GetTriangleCountForPlate, GetVertexPositions (count only), GetPaletteColorCount, GetPaletteColor, GetModelUnit EngineSession.cs:202–238, NpsAbi.cs:252,265,295,307,310,319,322,298
GenerateDemoToolpath(options)demo toolpath, not a production slice (EngineSession.cs:248–250) GenerateGcode (with packed NpsGcodeGenParams), GetLastGcodeMoveCount, GetLastGcodeMoves EngineSession.cs:253–303, NpsAbi.cs:333,366,369
GetLastGeneratedGcodeText() GetLastGeneratedGcode EngineSession.cs:138, NpsAbi.cs:336
Analyze(profile) AnalyzeGcode (with packed NpsMaterialProfile) EngineSession.cs:332–361, NpsAbi.cs:443
GetFindingsSummary() GetAnalysisFindingsSummaryCount, GetAnalysisFindingsSummary EngineSession.cs:376–388, NpsAbi.cs:455,458
GetFindingDetail(id) GetAnalysisFindingsDetail EngineSession.cs:407–411, NpsAbi.cs:461
GetWarpGrid(layer) GetWarpGridInfo, GetWarpGridRisk EngineSession.cs:421–433, NpsAbi.cs:480,483
GetOverlayValue(id, layer, move) GetOverlayValue EngineSession.cs:486–489, NpsAbi.cs:464
Dispose() DestroyEngine (via SafeEngineHandle) NpsAbi.cs:227

ViewportController → NpsAbi exports

Facade method NpsAbi export(s) Citations
CreateAndInitialize() / Initialize() CreateGcodeViewport ViewportController.cs:203,222, NpsAbi.cs:389
SetMoves(ptr, count) ViewportSetGcodeMoves ViewportController.cs:240–245, NpsAbi.cs:395
SetLayerVisibility(maxLayer) ViewportSetLayerVisibility ViewportController.cs:252–256, NpsAbi.cs:398
SetCamera(state) (also SetCameraPreset) ViewportSetCamera ViewportController.cs:262–266, NpsAbi.cs:401
SetSimulationState(simulating, index) ViewportSetSimulationState ViewportController.cs:489–493, NpsAbi.cs:420
ApplyOverlay(mode, values, vmin, vmax, invert, mean?) ViewportApplyOverlay ViewportController.cs:517–547, NpsAbi.cs:432
ApplyOverlay(session, mode, invert) — session overload per move: EngineSession.GetOverlayValueGetOverlayValue; then ViewportApplyOverlay; warp-risk mode also calls ViewportSetWarpParams(true, 5.0f), any other mode ViewportSetWarpParams(false, 0) ViewportController.cs:564–639, NpsAbi.cs:464,432,424
SetWarpParams(active, scale) ViewportSetWarpParams ViewportController.cs:646–649, NpsAbi.cs:424
BuildBuffers() ViewportBuildBuffers ViewportController.cs:657–660, NpsAbi.cs:404
ComputeZoomFit(...) ProjectOrbitXY (per move, into scratch buffer) ViewportController.cs:432–456, NpsAbi.cs:500
Dispose() DestroyGcodeViewport (via SafeGcodeViewportHandle) NpsAbi.cs:392

HOW IT IS: the legacy split setters ViewportSetOverlayMode, ViewportSetOverlayValues, ViewportSetOverlayRange are still exported (NpsAbi.cs:422–428) but deprecated — the facade's doc comment calls them legacy in favor of the one-shot ViewportApplyOverlay (ViewportController.cs:495–501). No facade method calls them.

MeshController and the two painters

Caller NpsAbi export(s) Citations
MeshController.Reload() GetPlateCount, GetPaletteColorCount, GetPaletteColor, then per plate GetVertexPositions, GetTriangleIndices, GetTriangleColorIndices (all Marshal.Copy into managed PlateMesh arrays) MeshController.cs:131–190, NpsAbi.cs:295,319,322,310,313,316
GcodeToolpathPainter.RenderTo(...) ProjectOrbitXY (per segment), ViewportGetHeatmapColor (when an overlay is active and mode ≠ motion-type) GcodeToolpathPainter.cs:85,160–175, NpsAbi.cs:500,494
MeshCanvasPainter.RenderTo(...) ProjectOrbitXY (per triangle vertex, 3 calls) MeshCanvasPainter.cs:112,174–178, NpsAbi.cs:500

Both painters hoist scratch buffers out of the per-segment loop (GcodeToolpathPainter.cs:54, MeshCanvasPainter.cs:74). Neither owns engine state: the mesh painter renders the MeshController snapshot, the toolpath painter reads ViewportController.MovesSpan (GcodeToolpathPainter.cs:125), a span over the pointer last handed to ViewportSetGcodeMoves.

Simulator-only raw smoke calls (Program.cs)

HOW IT IS: SimulatorApp/Program.cs calls NpsAbi directly, before Avalonia starts, on a throwaway engine. The UI then creates a second engine via EngineSession.CreateAndInitialize() (SimulatorApp/MainWindow.axaml.cs:108); the self-check engine is disposed and does not seed the UI session.

Order Call Line
1 GetAbiVersion() vs ExpectedAbiVersion; mismatch → throw Program.cs:22–28
2 CreateEngine() wrapped in SafeEngineHandle Program.cs:31
3 InitializeEngine(engine) Program.cs:33
4 GetEngineVersion(engine) (log) Program.cs:34
5 LoadModelFromFile(engine, benchy) Program.cs:43
6 (success) GetPlateCount, GetTotalVertexCount, GetTotalTriangleCount, per plate GetTriangleCountForPlate + GetVertexPositions, GetPaletteColorCount + GetPaletteColor loop, GetModelUnit — console output only Program.cs:50–71
6 (failure) GetLastErrorMessage Program.cs:75
DestroyEngine (implicit, SafeEngineHandle dispose) NpsAbi.cs:227

The UI starts regardless of the self-check outcome (Program.cs:98–99). Slicer's preflight covers the same ground but stays behind the facade: EnsureAbiMatchesHost, CreateAndInitialize, LoadModel, GenerateDemoToolpath with NumLayers: 0 (src/SlicerApp/Program.cs:22,27,35,59).

HOW IT IS: Simulator's MainWindow.RenderViewport also calls NpsAbi directly at paint time — ProjectOrbitXY and ViewportGetHeatmapColor (SimulatorApp/MainWindow.axaml.cs:768,770,778) — because it has not migrated to GcodeToolpathPainter; the painter's comment notes Simulator "may pass _simMoveIndex + 1 until it also migrates" (GcodeToolpathPainter.cs:78–79). Slicer's render path already goes through the painter.

Quick index: element → call chain

UI element Chain to the engine
Load + Gen + Analyze button EngineSession.LoadModelLoadModelFromFile … → GenerateDemoToolpathGenerateGcode, GetLastGcodeMovesViewportController.SetMoves/SetLayerVisibility/SetCamera/BuildBuffersViewportSetGcodeMoves, ViewportSetLayerVisibility, ViewportSetCamera, ViewportBuildBuffersAnalyzeAnalyzeGcode
Findings panel (populate / select row) GetFindingsSummaryGetAnalysisFindingsSummaryCount + GetAnalysisFindingsSummary; GetFindingDetailGetAnalysisFindingsDetail; selection also SetSimulationStateViewportSetSimulationState
Overlay radio (thermal / warp-risk / …) ViewportController.ApplyOverlay(session, mode) → per move GetOverlayValue → one ViewportApplyOverlay → warp-risk adds ViewportSetWarpParams
Layer slider / toolbar L+/L− SetLayerVisibilityViewportSetLayerVisibility, then ViewportBuildBuffers
Play / scrub / finding jump (Simulator) SetSimulationState(true, idx)ViewportSetSimulationState, then ViewportBuildBuffers
Canvas orbit / pan / zoom-fit camera kept in CameraState; SetCameraViewportSetCamera; zoom-fit projects via ProjectOrbitXY
Every repaint painter → ProjectOrbitXY per segment/vertex; overlay colors via ViewportGetHeatmapColor
Warp grid label (Slicer only) GetWarpGrid(l) for layers 0–5 → GetWarpGridInfo + GetWarpGridRisk (SlicerApp/MainWindow.axaml.cs:334–336)

Note

Overlay mode strings are the engine's wire ids, defined once as NpsAbi.OverlayModeId constants (NpsAbi.cs:88–95): motion-type, thermal, warp-risk, bond-strength, cooling-eff. motion-type is the "no overlay" state, not a true off switch.

For where these calls sit inside each app's interaction flow, see Slicer interactions and the Simulator overview; the sharing analysis lives under Sharing.