Skip to content

Latest commit

 

History

History
876 lines (661 loc) · 40.1 KB

File metadata and controls

876 lines (661 loc) · 40.1 KB

web_slicer_core

Web-based SLA slicing application powered by PrusaSlicer CLI (headless). Features a React frontend with 3D preview, configurable slicing parameters, and support mesh visualization.

Features

  • SLA Slicing: Slice STL models into layer images using PrusaSlicer engine
  • 3D Preview: Interactive Three.js viewer with orbit controls (Z-up coordinate system)
  • Support Generation: Auto-generate supports with configurable parameters
  • Support Mesh Export: Download generated supports as STL for external use
  • Hollow Interior Generation: Generate hollow interior mesh for visualization (NEW)
  • Configurable Parameters: Layer height, exposure times, support settings, pad options, hollowing
  • Layer Navigation: Browse through sliced layers with slider control

Prerequisites

  • macOS (tested on macOS 15.x) or Windows (see Windows setup for TBB)
  • Python 3.9+
  • Node.js 18+
  • PrusaSlicer Fork (with --export-support-stl feature)

Windows setup

Recommended (easiest): Use Python 3.11 or 3.12 and follow Quick Start (Windows) (submodule init → build PrusaSlicer → create .venv312scripts\run_agent.bat). manifold3d has prebuilt wheels for 3.11/3.12, so no TBB or compilation is needed.

py -3.12 -m venv .venv312
scripts\run_agent.bat

You must build PrusaSlicer first (use scripts\build_prusaslicer_fork_windows.bat on Windows). The script prefers .venv312 if it exists and will install all dependencies from wheels.

Alternative (Python 3.14 or other): If you use the default .venv (e.g. Python 3.14), manifold3d builds from source and requires TBB. Use vcpkg:

  1. vcpkg install tbb:x64-windows
  2. Set VCPKG_ROOT and add %VCPKG_ROOT%\installed\x64-windows\bin to your PATH.
  3. Run scripts\run_agent.bat

See Troubleshooting below if you see "tbb was not found" or "DLL load failed".

Quick Start

After clone: init submodules so the PrusaSlicer fork source is available:

git submodule update --init --recursive

Quick Start (Windows)

After cloning (and initializing submodules), run in order:

:: 1. Init submodules (get PrusaSlicer fork source)
git submodule update --init --recursive

:: 2. Build PrusaSlicer CLI (requires CMake, Visual Studio; 16GB RAM use default, 32GB+ use full)
::    Builds PrusaSlicer_app_console + OCCTWrapper (STEP plugin). Package is OFF by default.
scripts\build_prusaslicer_fork_windows.bat

:: Optional: build then package consumer staging in one step
:: scripts\build_prusaslicer_fork_windows.bat low package
:: or: set PACKAGE_SLICER_ENGINE=1 && scripts\build_prusaslicer_fork_windows.bat low

:: 3. Python 3.12 venv (recommended so manifold3d installs from wheel)
py -3.12 -m venv .venv312

:: 4. Start backend (installs deps and runs agent)
scripts\run_agent.bat

Backend runs at https://127.0.0.1:5179. Then start the frontend (see step 3 below).

Due to de-identification, the new build output on Windows is third_party\prusaslicer_build\src\Release\slicer-engine.exe plus slicer_core.dll and OCCTWrapper.dll (the console shim loads the neutral core DLL; STEP/STP uses the OCCT plugin). Point the agent at it with set SLICER_ENGINE_BIN=%CD%\third_party\prusaslicer_build\src\Release\slicer-engine.exe. See De-identification notes.

Prerequisites for Windows: Python 3.12 (or 3.11), Node.js 18+, CMake, Visual Studio 2017+ (2019/2022/2026). Low RAM (16GB): use default; 32GB+: run scripts\build_prusaslicer_fork_windows.bat full. Args: [full|low|qa] [clean|qa|package] … — pass package or set PACKAGE_SLICER_ENGINE=1 to run D13 staging after build (default off).

1. Build PrusaSlicer Fork (macOS / Linux)

The project uses a custom PrusaSlicer fork with support mesh STL export capability.

# Build the fork (includes --export-support-stl feature)
./scripts/build_prusaslicer_fork_macos.sh

This builds the binary at third_party/prusaslicer_build/src/prusa-slicer.

Due to de-identification, the new (current) output is third_party/prusaslicer_build/src/slicer-engine on macOS (the CMake OUTPUT_NAME is now slicer-engine; branded symlinks such as prusa-slicer are removed on macOS). On Linux the binary is still named prusa-slicer. See De-identification notes.

2. Start the Backend

# Set the path to the forked binary
export PRUSA_SLICER_BIN=$(pwd)/third_party/prusaslicer_build/src/prusa-slicer

# Start the agent
./scripts/run_agent.sh

Due to de-identification, the new recommended variable is SLICER_ENGINE_BIN pointing at slicer-engine (macOS example: export SLICER_ENGINE_BIN=$(pwd)/third_party/prusaslicer_build/src/slicer-engine). PRUSA_SLICER_BIN above is kept only as a local legacy fallback and still works during the transition — do not rely on it as the shipped default.

Backend runs at https://127.0.0.1:5179

3. Start the Frontend

cd web
npm install
npm run dev

Frontend runs at http://localhost:5174

4. Web Interfaces

Interface URL Description
React UI http://localhost:5174 Main frontend - slicing, preview, supports, hollow
Boolean Test http://localhost:5179/test/boolean Experimental boolean operations test page
API Docs http://localhost:5179/docs Swagger UI for API exploration

De-identification notes

De-identification only applies to the consumer artifact (the formal, shipped package): it renames the surfaces a user or support agent can see so no Prusa/slic3r brand fingerprint leaks. Slicing behavior and parameters are unchanged.

Dev-branch handoff (read this first)

De-identification is landed on the dev branches of both web_slicer_core and prusaslicer_fork. Pull both to latest before continuing development.

  1. Local build & run are unchanged — no extra manual steps for day-to-day work:
    • macOS: ./scripts/build_prusaslicer_fork_macos.sh (build) + ./scripts/run_agent.sh (run)
    • Windows: scripts\build_prusaslicer_fork_windows.bat (build) + scripts\run_agent.bat (run)
    • Windows packaging is opt-in (same idea as macOS): pass package or PACKAGE_SLICER_ENGINE=1 after a successful build when you need slicer-engine\ staging.
  2. Direct CLI — same native PrusaSlicer flag grammar; only the executable name is neutral (slicer-engine / slicer-engine.exe). Example: --load config.ini --export-gcode -o out.gcode model.stl (or --export-sla for SLA). Prefer SLICER_ENGINE_BIN over legacy PRUSA_SLICER_BIN.
  3. Scenarios & copy-paste commandsdocs/slicer-engine-deidentification/build-test-runbook.md (safe to feed to an AI assistant for guidance).
  4. Full R&D recordopenspec/changes/backend-slicer-engine-deidentification/ (design.md, naming-manifest.md, blacklist.md, acceptance-procedure.md, evidence, etc.).
  5. Pre-merge retest (both platforms): clean build → dental workflow smoke (one-click process + long supports + slice) → CLI slice smoke.

Build / test runbook: docs/slicer-engine-deidentification/build-test-runbook.md

Full spec: openspec/changes/backend-slicer-engine-deidentification/

One-line positioning

Context Name to use
Development / source / fork / submodule Keep PrusaSlicer (no need to rename)
Build tree third_party/prusaslicer_build/ Keep as-is (folder name not required to change)
Consumer package / install path Neutral names slicer-engine/, slicer-engine(.exe), slicer_core.dll

slicerslic3r: the neutral slicer-* names are not blacklist hits; the blacklisted token is slic3r (with the digit 3).

Development / compilation

  • For normal development, debugging, and running the CLI, just use PrusaSlicer as before — de-identification does not affect the dev workflow.
  • Output filenames become neutral right after compilation (CMake OUTPUT_NAME):
    • macOS: third_party/prusaslicer_build/src/slicer-engine (OCCT linked statically into the binary on Apple)
    • Windows: …\src\Release\slicer-engine.exe + slicer_core.dll + OCCTWrapper.dll (delay-loaded MODULE; the build script builds it explicitly after PrusaSlicer_app_console)
    • The CMake target is still internally named PrusaSlicer — that is expected, leave it.
  • PE icon on Windows is embedded at link time from SoT third_party/prusaslicer_fork/resources/icons/slicer-engine.ico (SLIC3R_APP_ICONPrusaSlicer.rc.in). Changing the .ico requires a clean rebuild of the console shim. Do not rely on Explorer list-view icons (shell cache); packaging verifies ExtractAssociatedIcon against the SoT (fail-closed).
  • Run a clean build when you touch any of these, so stale cache doesn't leave brand names behind (build_...windows.bat clean, or delete prusaslicer_build first on mac): OUTPUT_NAME, visibility flags, BUNDLE_QA_CRASH_HARNESS, exports (.def), VERSIONINFO / version.inc, SLIC3R_APP_ICON / .ico.
  • The QA crash harness (the three intentional crashes) is compiled only when flavor=qa; consumer builds default to OFF, and no runtime-triggerable crash path may be compiled into the formal package.
  • Do not touch slicing algorithms or do a wide C++ namespace rename for the sake of de-branding (Slic3r::slice:: is L3 and out of scope for this round).

Build / packaging

  • Plain compilation does not strip or seal PDBs; the formal package requires a separate package step (opt-in on both platforms):
    • macOS: PACKAGE_SLICER_ENGINE=1 ./scripts/build_prusaslicer_fork_macos.shthird_party/slicer-engine/bin/
    • Windows: scripts\build_prusaslicer_fork_windows.bat low package or set PACKAGE_SLICER_ENGINE=1 then build or powershell -File scripts\package_slicer_engine_windows.ps1slicer-engine\bin\
    • Packaging deletes the previous staging root then recopies (no leftover files from older packages).
  • The consumer package must not contain: .pdb (Win), .dSYM / *.unstripped (mac), the QA harness, or brand leftovers like prusa-slicer* / PrusaSlicer.dll.
  • Keep symbols internally: archive the mac .dSYM and the Win .pdb in a private symbol store (their UUID / GUID must match that build) so function names can be restored for debugging later.
  • After stripping on macOS you must re-codesign; Windows uses Authenticode. Do not strip / rename / patch after signing.

Usage (agent / CLI)

  • The agent binary path is driven by SLICER_ENGINE_BIN; PRUSA_SLICER_BIN is a local legacy fallback only — do not rely on it as the shipped default.
  • When invoking the CLI directly, the formal package calls slicer-engine(.exe); the arguments are identical to PrusaSlicer (--export-sla, --export-support-stl, --help, etc.).
  • Running slicer-engine.exe on Windows requires slicer_core.dll, OCCTWrapper.dll, and GMP/MPFR (libgmp-10.dll / libmpfr-4.dll) in the same directory; missing files cause a LoadLibrary failure (error 126).

Quick self-check before acceptance

# --help must not print PrusaSlicer / slic3r (the neutral name slicer-engine is fine)
"$SLICER_ENGINE_BIN" --help

# The formal package dir has no prusa / slic3r filenames; no .pdb on Win, no .dSYM on mac

Usage

  1. Select STL File: Click file input to select a model - 3D preview appears immediately
  2. Configure Settings (optional): Expand "Slicing Config" to adjust parameters
    • Layer height: 0.025mm, 0.05mm, or 0.1mm
    • Exposure time: 1-30 seconds
    • Initial exposure: 5-60 seconds
    • Enable/disable supports with detailed settings
    • Enable/disable pad
  3. Slice: Click "Slice" button to start processing
  4. View Results:
    • 3D Preview: Model (blue) with support mesh overlay (red) if supports enabled
    • Layer View: Navigate through individual slice images
  5. Download Support STL: When supports are generated, download button appears

SLA Configuration Parameters

Parameter Description Range Default
layer_height Height of each slice layer 0.025, 0.05, 0.1 mm 0.05 mm
exposure_time UV exposure per layer 1-30 s 10 s
initial_exposure_time First layers exposure 5-60 s 15 s
supports_enable Generate support structures on/off off
support_head_front_diameter Support tip diameter 0.2-1.0 mm 0.4 mm
support_head_penetration Tip penetration depth 0.1-0.5 mm 0.2 mm
support_pillar_diameter Support pillar width 0.5-2.0 mm 1.0 mm
support_points_density_relative Support density 50-200% 100%
pad_enable Generate base pad on/off off
hollowing_enable Enable hollowing on/off off
hollowing_min_thickness Wall thickness 0.5-10 mm 3.0 mm
hollowing_quality Voxel quality (higher = finer) 0.1-1.0 0.5
hollowing_closing_distance Smoothing distance 0-10 mm 2.0 mm

API Reference

Health Check

curl http://127.0.0.1:5179/

Create Slicing Job

# Basic (default config)
curl -X POST http://127.0.0.1:5179/api/jobs \
  -F "file=@model.stl"

# With custom config
curl -X POST http://127.0.0.1:5179/api/jobs \
  -F "file=@model.stl" \
  -F 'config={"layer_height":0.05,"supports_enable":true,"exposure_time":12}'

Response:

{"job_id": "a1b2c3d4", "status": "pending"}

Get Job Status

curl http://127.0.0.1:5179/api/jobs/{job_id}

Response:

{
  "job_id": "a1b2c3d4",
  "status": "completed",
  "layer_count": 750,
  "error": null,
  "has_support_mesh": true
}

Get Layer Image

curl http://127.0.0.1:5179/api/jobs/{job_id}/layers/50.png --output layer50.png

Get Original Model STL

curl http://127.0.0.1:5179/api/jobs/{job_id}/model.stl --output model.stl

Get Support Mesh STL (includes pad)

curl http://127.0.0.1:5179/api/jobs/{job_id}/support.stl --output support.stl

Returns combined mesh of supports and pad (if enabled). Only available when has_support_mesh: true in job status.

Generate Hollow Interior (v2 API)

# 1. Create job with hollow config
curl -X POST http://127.0.0.1:5179/api/v2/slices \
  -H "Content-Type: application/json" \
  -d '{"config": {"hollowing_enable": true, "hollowing_min_thickness": 2.0}}'

# 2. Upload model
curl -X POST http://127.0.0.1:5179/api/v2/slices/{job_id}/upload \
  -F "file=@model.stl"

# 3. Generate hollow interior
curl -X POST http://127.0.0.1:5179/api/v2/slices/{job_id}/generate-hollow

# 4. Poll status until completed
curl http://127.0.0.1:5179/api/v2/slices/{job_id}
# Response: {"data": {"status": "completed", "hasHollowMesh": true}}

# 5. Download hollow mesh
curl http://127.0.0.1:5179/api/jobs/{job_id}/hollow.stl --output hollow.stl

Get Hollow Mesh STL

curl http://127.0.0.1:5179/api/jobs/{job_id}/hollow.stl --output hollow.stl

Returns the hollow interior mesh. Only available when has_hollow_mesh: true in job status.

Architecture

The backend supports multiple frontends through versioned API endpoints:

┌─────────────────────┐     ┌─────────────────────┐
│   DS-Online (Vue)   │     │  web_slicer_core    │
│   Dental Slicer UI  │     │  (React) Basic UI   │
│   :5173             │     │  :5174              │
└──────────┬──────────┘     └──────────┬──────────┘
           │                           │
           │  /api/v2/slices           │  /api/jobs
           │                           │
           ▼                           ▼
┌─────────────────────────────────────────────────┐
│            FastAPI Backend (:5179)              │
│  ┌─────────────────┐   ┌─────────────────────┐  │
│  │ /api/v2/slices  │   │ /api/jobs (v1)      │  │
│  │ DS-Online API   │   │ Original API        │  │
│  └────────┬────────┘   └──────────┬──────────┘  │
│           └────────────┬──────────┘             │
│                        ▼                        │
│           ┌─────────────────────┐               │
│           │    Job Manager      │               │
│           │  (shared service)   │               │
│           └──────────┬──────────┘               │
│                      ▼                          │
│           ┌─────────────────────┐               │
│           │   PrusaSlicer CLI   │               │
│           │   (Fork + support   │               │
│           │    mesh export)     │               │
│           └─────────────────────┘               │
└─────────────────────────────────────────────────┘
                       │
                       ▼
              ┌─────────────────┐
              │  Job Storage    │
              │  agent/jobs/    │
              └─────────────────┘

Layer Abstraction

┌─────────────────────────────────────────────────────────────┐
│  UI Layer                                                   │
│  - DS-Online (Vue + Three.js + PrimeVue)                   │
│  - web_slicer_core (React + Three.js)                      │
├─────────────────────────────────────────────────────────────┤
│  API Layer (FastAPI)                                        │
│  - /api/jobs/* (v1 - original)                             │
│  - /api/v2/slices/* (v2 - DS-Online compatible)            │
├─────────────────────────────────────────────────────────────┤
│  Service Layer                                              │
│  - Job Manager (create, status, polling)                   │
│  - Config Manager (INI generation, validation)             │
├─────────────────────────────────────────────────────────────┤
│  Engine Layer                                               │
│  - PrusaSlicer CLI Adapter                                 │
│  - Support mesh export (--export-support-stl)              │
│  - Hollow interior export (--export-hollow-stl)            │
└─────────────────────────────────────────────────────────────┘

Due to de-identification, the "PrusaSlicer CLI" box in these diagrams ships as the neutral slicer-engine engine (Windows: slicer-engine.exeslicer_core.dll). The adapter, APIs, and slicing behavior are unchanged; only the consumer-facing executable/DLL names are neutral. See De-identification notes.

Directory Structure

web_slicer_core/
├── agent/
│   ├── main.py              # FastAPI application & endpoints
│   ├── config.py            # Configuration constants
│   ├── models.py            # Pydantic models (SLAConfig, JobStatus)
│   ├── jobs.py              # Job management & slicing logic
│   └── jobs/                # Job data storage (gitignored)
│       └── {job_id}/
│           ├── input/model.stl
│           ├── output/
│           │   ├── model.sl1
│           │   └── model_support.stl  # If supports enabled
│           ├── layers/{0..N}.png
│           ├── config.ini
│           └── status.json
├── web/
│   ├── src/
│   │   ├── App.tsx          # Main application component
│   │   ├── App.css          # Styles
│   │   ├── STLViewer.tsx    # Three.js 3D viewer component
│   │   └── main.tsx         # Entry point
│   ├── package.json
│   └── vite.config.ts
├── third_party/
│   ├── prusaslicer_fork/    # PrusaSlicer fork (submodule)
│   └── prusaslicer_build/   # Build output (gitignored)
├── scripts/
│   ├── run_agent.sh
│   └── build_prusaslicer_fork_macos.sh
├── requirements.txt
└── README.md

Due to de-identification, the new tree also includes:

  • third_party/slicer-engine/ — optional packaged consumer layout (bin/, symbols/, legal/, manifest), produced by the packaging scripts; gitignored.
  • prusaslicer_build/ still builds with OUTPUT_NAME=slicer-engine (slicer-engine/slicer-engine.exe + slicer_core.dll); the folder name itself is left as-is.
  • Additional scripts under scripts/: build_prusaslicer_fork_windows.bat, package_slicer_engine_macos.sh, package_slicer_engine_windows.ps1, scan_slicer_engine_macos.sh, scan_slicer_engine_windows.ps1.

PrusaSlicer Fork

This project uses a custom fork of PrusaSlicer (github.com:MaxShih147/PrusaSlicer.git) with additional CLI options:

Due to de-identification, the new shipped binary is invoked as slicer-engine (slicer-engine.exe on Windows), not prusa-slicer. The prusa-slicer commands shown in the examples below still describe the exact same CLI options and arguments — only the executable name changed for consumer artifacts. On Linux the dev binary remains prusa-slicer. See De-identification notes.

--export-support-stl

Exports the generated support and pad meshes as a combined STL file after SLA slicing.

prusa-slicer --export-sla --export-support-stl -o output.sl1 model.stl
# Creates: output.sl1 and model_support.stl

The exported STL includes:

  • Support structures (if supports_enable = true)
  • Pad/raft (if pad_enable = true)

Implementation details:

  • Added in src/libslic3r/PrintConfig.cpp (CLI option definition)
  • Export logic in src/CLI/ProcessActions.cpp
  • Uses SLAPrintObject::support_mesh() and SLAPrintObject::pad_mesh()

--export-hollow-stl

Generates and exports the hollow interior mesh as STL. This is a standalone operation that doesn't require full slicing.

prusa-slicer --export-hollow-stl \
  --hollowing-min-thickness 2 \
  --hollowing-quality 0.5 \
  --hollowing-closing-distance 1 \
  -o interior.stl model.stl
# Creates: interior.stl (hollow interior mesh only)

Parameters:

  • --hollowing-min-thickness: Wall thickness in mm (default: 2.0)
  • --hollowing-quality: Voxel quality 0.1-1.0 (default: 0.5, higher = finer detail)
  • --hollowing-closing-distance: Morphological closing distance in mm (default: 0.5)

Important notes:

  • Wall thickness must be appropriate for model size (small models need thinner walls)
  • The interior mesh has flipped normals for proper visualization
  • Uses OpenVDB for voxelization and interior generation

Implementation details:

  • CLI option defined in src/libslic3r/PrintConfig.cpp
  • Handler in src/CLI/ProcessActions.cpp
  • Uses sla::generate_interior() from libslic3r/SLA/Hollowing.hpp
  • Normals flipped via sla::swap_normals() for visualization

PrusaSlicer Default Printer Configuration

The Problem: Frontend vs Backend Parameter Disconnect

The DS-Online frontend (paramsStore.bedSize) and the PrusaSlicer backend use separate, unconnected parameter sets. When slicing via backend CLI, generate_config_ini() in agent/sla_operations.py writes only slicing parameters (layer_height, exposure, supports, hollowing) to the config INI — no printer/display parameters are included. PrusaSlicer therefore falls back to its hardcoded defaults.

This matters for any feature that needs to map between PNG pixel coordinates and real-world mm coordinates (e.g., island detection 3D overlay, cross-section alignment).

Hardcoded Defaults (from PrintConfig.cpp)

Source: third_party/prusaslicer_fork/src/libslic3r/PrintConfig.cpp (lines ~4272-4295)

Parameter Default Value Description
display_width 120.0 mm Physical display width
display_height 68.0 mm Physical display height
display_pixels_x 2560 Horizontal pixel count
display_pixels_y 1440 Vertical pixel count
display_orientation portrait Display rotation

These defaults closely match the Original Prusa SL1 printer profile.

Available SLA Printer Profiles

Profiles are stored in third_party/prusaslicer_build/resources/profiles/:

Printer display_width display_height pixels_x pixels_y orientation
Prusa SL1 120.96 mm 68.04 mm 2560 1440 portrait
Prusa SL1S SPEED 128.00 mm 81.00 mm 2560 1620 portrait
Anycubic Photon Mono (see AnycubicSLA.ini)
Anycubic Photon Mono X (see AnycubicSLA.ini)
Anycubic Photon Mono X 6K (see AnycubicSLA.ini)

PNG Pixel-to-World Coordinate Mapping

Output PNGs are 1440 x 2560 pixels (width x height). With display_orientation = portrait, the pixel layout is rotated 90 degrees from the physical display:

PNG width  (1440 px) → physical short axis → 68.0 mm (display_height)
PNG height (2560 px) → physical long axis  → 120.0 mm (display_width)

Pixel-to-mm conversion:

world_x = (px / png_width)  * display_height   // 68.0 mm
world_y = (py / png_height) * display_width     // 120.0 mm

Note: PrusaSlicer auto-centers the model on the bed and auto-drops it to Z=0. The model's position in the PNG is relative to the bed center.

CLI Config Flow

Frontend config          generate_config_ini()         PrusaSlicer CLI
(backendSlicer.js)  -->  (sla_operations.py)      -->  (--load config.ini)
                         Writes EVERY SLAConfig        Reads config.ini.
                         field, including:             Its own defaults apply
                         - layer_height                only when no --load is
                         - exposure_time               passed at all:
                         - supports_enable             - display_width    (120.0)
                         - hollowing_*                 - display_height   (68.0)
                         - display_width               - display_pixels_x (2560)
                         - display_height              - display_pixels_y (1440)
                         - display_pixels_x / _y
                         - display_orientation

display_pixels_x / display_pixels_y reaching the INI is what lets the agent derive the preview downscale ratio from the same format the engine rasterises at — see agent/preview_scale.py.

TODO: Sync Frontend Parameters with PrusaSlicer Config

  • Add printer/display config to generate_config_ini() — Done: it dumps every SLAConfig field, so display_width, display_height, display_pixels_x, display_pixels_y and display_orientation all reach the INI and PrusaSlicer uses the same bed as the frontend
  • Add printer profile selection to frontend — Let users choose a printer profile (SL1, SL1S, Anycubic, custom) or enter custom display dimensions
  • Return display config from backend API — Include display_width, display_height, display_pixels_x, display_pixels_y in slice job status response so the frontend knows the actual bed size used
  • Sync paramsStore.bedSize with PrusaSlicer display — Frontend's paramsStore.bedSize (currently [195.84, 122.4] for LS Plus) should match the PrusaSlicer printer profile, or be overridden by it
  • Read actual PNG dimensions — Instead of assuming 1440x2560, read the IHDR header from the first layer PNG to get actual pixel dimensions (supports different printer profiles)
  • Handle display_orientation — Account for portrait vs landscape orientation when mapping pixel coordinates to world coordinates
  • Add layer_height to job status response — Backend currently returns layerCount but not layer_height; needed for accurate Z coordinate mapping

Development

Backend Development

From the repository root (the scripts run the agent with HTTPS on https://127.0.0.1:5179 and require TLS cert/key; see agent/tls/ or scripts/trust_dev_tls_*.sh / scripts/trust_dev_tls_windows.ps1).

macOS / Linux

pip install -r requirements.txt
./scripts/run_agent.sh

Windows (Command Prompt or PowerShell)

pip install -r requirements.txt
scripts\run_agent.bat

Frontend Development

cd web
npm install
npm run dev  # Vite dev server with HMR

Building PrusaSlicer Fork

If you need to modify the PrusaSlicer fork:

  1. Edit source in third_party/prusaslicer_fork/
  2. Rebuild:
    cd third_party/prusaslicer_build
    make -j8
  3. Test the binary:
    ./src/prusa-slicer --help | grep export-support

Due to de-identification, on macOS the new test binary is ./src/slicer-engine (e.g. ./src/slicer-engine --help | grep export-support); Linux still produces prusa-slicer.

Troubleshooting

"PrusaSlicer CLI not found" (Windows)

Build the fork first: scripts\build_prusaslicer_fork_windows.bat. Ensure you ran git submodule update --init --recursive after clone. The binary will be at third_party\prusaslicer_build\src\Release\slicer-engine.exe (with slicer_core.dll and OCCTWrapper.dll beside it).

Due to de-identification, do not look for prusa-slicer.exe. Use slicer-engine.exe + runtime DLLs in the same folder.

"tbb was not found" when installing dependencies (Windows)

The manifold3d package needs TBB to build. Follow Windows setup: install TBB via vcpkg, set VCPKG_ROOT or CMAKE_PREFIX_PATH, then run scripts\run_agent.bat again.

"CLI not available"

Ensure PRUSA_SLICER_BIN is set correctly:

export PRUSA_SLICER_BIN=$(pwd)/third_party/prusaslicer_build/src/prusa-slicer
$PRUSA_SLICER_BIN --version

Due to de-identification, the new preferred variable and path are:

# macOS
export SLICER_ENGINE_BIN=$(pwd)/third_party/prusaslicer_build/src/slicer-engine
"$SLICER_ENGINE_BIN" --help

# Windows (cmd)
set SLICER_ENGINE_BIN=%CD%\third_party\prusaslicer_build\src\Release\slicer-engine.exe
"%SLICER_ENGINE_BIN%" --help

PRUSA_SLICER_BIN is still accepted as a local legacy fallback only.

CORS errors in browser

The backend includes CORS middleware for common localhost dev origins and https://dentalslice.onrender.com. To add more origins without editing code, set:

export CORS_ALLOWED_ORIGINS="https://your-ui.example.com,https://another.example.com"

Support mesh not appearing

  1. Ensure "Enable Supports" is checked in config panel
  2. Check backend logs for "Support mesh exported" message
  3. Verify has_support_mesh: true in job status response

Feature Status & Roadmap

✅ Implemented Features

Feature CLI Backend API Frontend Notes
SLA Slicing --export-sla /execute Full layer export
Support Generation --export-support-stl /generate-supports Includes pad mesh
Hollow Interior --export-hollow-stl /generate-hollow Interior mesh only
Layer Preview /layers/{idx}.png PNG extraction from SL1
3D Visualization - - Three.js with Z-up

🚧 TODO / Future Work

High Priority

  • Drain Holes: Add --export-drill-stl for drain hole generation
    • PrusaSlicer has DrainHole in Hollowing.hpp
    • Needs position input (click-to-place in UI)
  • Combined Hollow + Supports: Single operation for hollowed model with internal supports
  • Hollow Preview Before Apply: Show preview without generating full mesh

Medium Priority

  • Auto-Orient: Expose PrusaSlicer's auto-orient via CLI
  • Support Editing: Manual support point placement/removal
  • Infill Patterns: Support for partial hollowing with infill
  • Multi-Model Support: Handle multiple models in single job

Low Priority / Research

  • WebAssembly Port: Run hollowing in browser (OpenVDB is complex)
  • Streaming Layers: WebSocket for real-time layer streaming during slice
  • Diff Slicing: Only re-slice changed regions

⚠️ Known Issues & Limitations

  1. Wall Thickness vs Model Size

    • Small models need thinner walls (0.5-1mm)
    • Large models can use thicker walls (2-3mm)
    • Error "interior mesh is empty" means wall is too thick for model
  2. Hollow Mesh Positioning

    • Frontend must apply same transform as original model
    • Currently copies position/rotation/scale from selected model
    • If model is transformed after hollow generation, mesh will be misaligned
  3. Memory Usage

    • Hollowing uses OpenVDB which can be memory-intensive
    • High quality setting (1.0) on large models may use several GB RAM
  4. No Incremental Updates

    • Changing hollow parameters requires full regeneration
    • No caching of intermediate voxel grids
  5. Single Model Per Job

    • v2 API currently only processes first uploaded model
    • Multi-model support requires job structure changes

🏗️ Architecture Decisions

Why Export Interior Mesh Only?

Decision: Export only the hollow interior mesh, not a combined hollowed model.

Rationale:

  • Frontend already has the original mesh
  • Smaller data transfer (interior only vs full hollowed model)
  • Can toggle hollow preview on/off without re-fetching
  • Allows different materials/transparency for interior visualization
  • PrusaSlicer stores interior separately in sla::Interior

Trade-off: Frontend must combine meshes; can't directly print the exported hollow mesh.

Why Flip Normals in CLI?

Decision: Flip normals in PrusaSlicer CLI before export (sla::swap_normals()).

Rationale:

  • Interior mesh faces inward by default (for boolean subtraction)
  • Visualization requires outward-facing normals
  • Better to flip once at export than in every frontend
  • Consistent with how support mesh is exported

Why Separate /generate-hollow Endpoint?

Decision: Hollow generation is a separate endpoint, not part of /execute.

Rationale:

  • Hollow preview doesn't need full slicing
  • Faster feedback loop for parameter tuning
  • Can hollow without committing to slice
  • Matches /generate-supports pattern
  • Future: could cache hollow result for final slice

Job State Model

                    ┌─────────────┐
                    │   created   │  (in-memory, _pending_jobs)
                    └──────┬──────┘
                           │ upload model
                           ▼
                    ┌─────────────┐
         ┌─────────│   pending   │─────────┐
         │         └─────────────┘         │
         │ generate-supports    generate-hollow
         ▼                                 ▼
   ┌───────────┐                    ┌───────────┐
   │ processing│                    │ processing│
   └─────┬─────┘                    └─────┬─────┘
         │                                │
         ▼                                ▼
   ┌───────────┐                    ┌───────────┐
   │ completed │                    │ completed │
   │ +supports │                    │ +hollow   │
   └───────────┘                    └───────────┘
         │
         │ execute (full slice)
         ▼
   ┌───────────┐
   │ completed │
   │ +layers   │
   └───────────┘

Future Directions & Business Paths (Internal Checklist)

This section is a self-reminder for future product and business evolution. Not all items are meant to be pursued at once.

A. Slicing-as-a-Service (SaaS / API-first)

Idea: Expose the slicer as a headless, scalable service rather than a desktop tool.

Potential value:

  • Remove slicer maintenance burden for customers
  • Enable cloud / automation / AI pipelines
  • Natural fit for batch processing and scale

Target users:

  • Manufacturing platforms
  • Dental labs
  • Cloud manufacturing services
  • AI-generated model pipelines

Indicators to revisit:

  • Stable job-based API
  • Clear cost metrics (per job / per GB / per minute)
  • Demand for non-interactive slicing

B. Process Intelligence Layer (High-margin differentiation)

Idea: Sell decision-making instead of slicing itself.

Examples:

  • Support quality evaluation
  • Failure risk estimation
  • Auto parameter / support suggestions
  • Comparative analysis between slicing strategies

Why this matters:

  • Support = process know-how, not just geometry
  • Enables AI-driven optimization
  • Hard to copy, high long-term value

Indicators to revisit:

  • Support data is structured and comparable
  • Repeated slicing failures observed in users
  • Need for "why did this fail?" answers

C. OEM / Embedded Slicer Licensing

Idea: Provide the slicer as an embedded or white-label component for hardware vendors.

Potential value:

  • Recurring licensing revenue
  • Strong fit with device-centric workflows
  • Avoids consumer software competition

Target customers:

  • 3D printer manufacturers
  • Specialized hardware startups
  • Non-general-purpose printing systems

Indicators to revisit:

  • Requests for custom workflow / UI
  • Need for tight hardware-software integration
  • Vendor reluctance to maintain slicer teams

D. Data, Traceability & Compliance Layer

Idea: Turn slicing outputs and parameters into auditable, traceable production records.

Examples:

  • Layer-level archives
  • Parameter history
  • Reproducibility reports
  • Compliance-ready logs

Why it's valuable:

  • Required in medical / dental / industrial contexts
  • Seen as a cost of doing business, not a feature
  • High willingness to pay

Indicators to revisit:

  • Regulated customers (medical, dental, ISO)
  • Need for print reproducibility
  • QA / audit requirements

Strategic Notes

  • This project is not just a slicer; it is a platform around slicing
  • Engine choice is a means, not the product
  • Supports, layers, and parameters are data assets, not UI details
  • Monetization should prioritize process value, not feature count

License

PrusaSlicer is licensed under AGPLv3. See the fork repository for details.

Due to de-identification, the new note is: de-branding renames consumer-facing artifacts only — it does NOT change AGPLv3 obligations. The AGPL license, copyright, modification notices, and Corresponding Source offer must still ship with the consumer package (see legal/slicer-engine/ and openspec/changes/backend-slicer-engine-deidentification/).