Skip to content

Latest commit

 

History

History
312 lines (240 loc) · 13.8 KB

File metadata and controls

312 lines (240 loc) · 13.8 KB

PlotterForge

Turn a photo (or a procedural drawing) into layered, pen-plotter-ready SVG, then preview it and plot it — all in one app.

PlotterForge is a browser-based studio for pen plotters, in the spirit of DrawingBotV3. You import images as movable layers, pick a path-finding style (stippling, hatching, TSP art, flow fields, tessellations, dithers, and more), arrange everything on a real page with real pens, and either export an SVG or drive a Grbl plotter directly.

PlotterForge compose workspace

A note on how this was built

This project was vibe-coded — most of the code was written with an AI assistant rather than typed by hand. That's the honest origin story, and you should factor it into your expectations: the architecture is pragmatic, not academic, and there are corners that reflect "what worked" over "what's textbook."

What it isn't, though, is untested. I use PlotterForge as my actual plotting tool. Every style, the layer system, the pen sets, and the live Grbl plotting have been run against real hardware and real drawings, repeatedly, because I needed them to work for my own art. So: AI-written, human-hammered.

What it does

  • 50 path-finding styles. Samplers (Voronoi, LBG, adaptive, Poisson-disk) combined with looks like stippling, dashes, shapes, triangulation, trees, diagrams, and TSP, plus grid halftone, spiral, hatch, sketch, streamlines, dither halftone, circle packing, differential growth, quadtree mosaic, and raster-driven tessellations. Each style's controls are generated from a typed schema, so nothing is hidden.
  • Layer-based composition. Photos come in as non-destructive raster layers (EXIF-correct, never auto-cropped). Move, scale, rotate, fit, or fill them; the path-finding follows whatever transform you set.
  • Click-to-segment (SAM2). Click a region of your source image and run a different style on it than on the rest.
  • Real page, real pens. Set the drawing area in physical units, define a multi-pen set with per-pen width and colour, preview flat-nib calligraphy, and plot each pen as its own pass with guided pen changes.
  • Plot or export. Live plot preview with a time estimate, direct Grbl serial plotting with a safe Stop/Resume, or a multi-layer millimetre SVG for Inkscape, vpype, or your plotter's own software.
  • Versioned projects. Immutable snapshots with thumbnails, kept under ~/.plotterforge/, and a project browser that shows the whole workspace as a grid of rendered pages rather than a list of names.
  • GPU when you have one. PyTorch acceleration (CUDA on Windows, MPS on Apple Silicon) for the sampling-heavy styles, with a CPU fallback that just works.
  • Cavalry bridges. Stream SVG frames live from Cavalry, or bake a Cavalry composition into a reusable tone-responsive tessellation style.

Full feature list: FEATURES.md.

Pick a style

Every style ships with a preview so you can eyeball the look before committing. A few, to give you the range:

Voronoi stippling Hatch Flow-field streamlines
Voronoi stippling Hatch Streamlines
TSP art Circle packing Truchet tessellation
TSP art Circle packing Tessellation

The full picker, with live previews, is built into the app:

Style picker

For non-programmers

You don't need to touch a terminal after the one-time setup, and you never need Node or a build step — the app's interface is pre-built and committed.

  1. Install the two prerequisites below (uv and Node.js — both have normal installers).
  2. Run the setup script for your OS once.
  3. Run the start script and open http://localhost:7438 in your browser.

That's it. Everything after that happens in the browser: import a photo, choose a style, arrange it on the page, hit plot or export. A full illustrated manual is built into the app and opens from inside it.

Layer and composition view

For programmers

  • engine/ — the conversion engine (styles, samplers, pens, drawing area, version control, GPU/CPU backends, SVG output). Pure Python, runs headless, fully testable.
  • frontend/ — a Svelte 5 single-page app. npm run build writes to web/static/app, which is committed so end users skip the JS toolchain.
  • plotter/ — the plotter side: SVG flattening, path ordering, travel estimates and the parsed-path cache. Pure geometry, no Flask, so profiling and scripts can import it.
  • web/ — the Flask application. routes/ holds one blueprint per area (projects, composition, machine, plotting); services/ holds the operations that own real orchestration and import no Flask; session.py is the mutable application state; server.py is the remaining assembly plus the Grbl driver.

Two things about web/ worth knowing before you change it. It is deliberately a single-user, single-machine server — one open project, one plotter, process-wide state — so per-request sessions and multi-lock schemes are the wrong shape here. And path finding runs synchronously in the request thread: a dense render can hold one HTTP request open for minutes. It reports progress over the event stream and blocks project switches while it runs, but it cannot be cancelled, and a proxy with a short idle timeout will give up on it. Moving it to a background job is a known, deliberate not-yet.

Viewport crop, scale, rotation and occlusion mirror the export engine through Python-generated conformance fixtures; the intentional flat-nib preview difference is documented in the viewport geometry contract.

Style controls are declared as typed schemas in engine/params.py, and the manual's parameter reference is generated from them. See Development.

Install

Requirements

  • Windows 10/11 with an NVIDIA GPU and a current driver, or macOS on Apple Silicon
  • uv — the Python project manager (handles Python 3.13 for you; no Conda)
  • Node.js with npm

One-time setup

Platform Run
Windows setup-windows.bat
macOS ./setup-macos.command (or double-click in Finder)

Setup installs the right PyTorch build for your machine, a pinned SAM2 and its default checkpoint, and the frontend dependencies. It then runs a real segmentation inference to confirm things actually work before saying "done". Rerun it after pulling dependency or frontend changes.

Launch

Platform Run
Windows start-windows.bat
macOS ./start-macos.command (or double-click in Finder)

Open http://localhost:7438. The launchers are offline and side-effect-free: they never install, sync, build, download, or kill processes.

Debug recording

When a UI problem is difficult to reproduce, start the same launcher with its optional debug flag:

Platform Run
Windows start-windows.bat --debug
macOS ./start-macos.command --debug

Debug mode writes two files under ~/.plotterforge/logs/ (on Windows, %USERPROFILE%\.plotterforge\logs\):

  • debug-actions-*.jsonl records meaningful clicks, control changes, canvas gestures, shortcuts, API outcomes/request IDs, browser errors, and compact layer-state transitions in chronological JSON Lines.
  • plotter.log records the correlated server and worker operations.

Give both files to the LLM investigating the problem. Action recording is off for a normal launch. It never stores request/response bodies, image or SVG contents, imported filenames, or text-field values; file inputs retain only media type, byte size, and extension. Debug traces can still reveal workflow structure and project/layer IDs, so review them before sharing.

Remote access and request security

The default server is local-only. It binds to 127.0.0.1, accepts only localhost and loopback Host headers, and rejects state-changing browser requests whose Origin does not match the server.

When intentionally exposing PlotterForge through a LAN address or reverse proxy, configure both the listening interface and the public names:

PLOTTER_HOST=0.0.0.0 \
PLOTTER_TRUSTED_HOSTS=plotter.example,192.168.1.25 \
PLOTTER_ALLOWED_ORIGINS=https://plotter.example \
uv run --locked --no-sync python -m web.server

PLOTTER_TRUSTED_HOSTS is a comma-separated hostname/IP allowlist; do not add untrusted wildcard names. PLOTTER_ALLOWED_ORIGINS is a comma-separated list of exact http:// or https:// origins and is only needed when the browser's public origin differs from Flask's request origin, as it can behind a TLS proxy. Originless requests remain accepted for non-browser integrations such as Cavalry and the CLI, while requests explicitly marked by a browser as cross-site are rejected.

If something goes wrong

  • uv or Node.js not found — install it, then rerun the setup script.
  • "Run setup first" — the .venv is missing or stale; rerun setup.
  • Port 7438 is already in use by PID … — another instance is running. Stop that PID and relaunch (the launchers won't kill it for you).
  • CUDA/MPS unavailable — check your GPU driver (Windows) or that you're on Apple Silicon (macOS), then rerun setup.
  • setup is incomplete: missing … — SAM2, Torch, or the checkpoint is absent. Rerun setup; the server never installs anything at runtime.

The in-app Troubleshooting chapter covers first-plot calibration and the safe Stop/Resume procedure.

Plot it

Preview the toolpath, check the time estimate, and either plot over serial or export SVG.

Plot preview

Documentation

The full manual ships with the app and is served locally while it runs.

Guide For
Manual Start here — a tour of the whole workflow
Tutorials Three reproducible start-to-finish artworks
Choose a style Decision guide across all 50 styles
Parameter reference Every control, default, range, and description
Pens, paper & plotting Operators: calibration, pen changes, safety
Cavalry tessellations Bake Cavalry comps into custom styles

How it works

image / generator ──▶ engine (style) ──▶ Drawing ──▶ multi-layer mm SVG ──▶ plot / export

Projects, versions, models, and installed tessellations live under ~/.plotterforge/ (migrated automatically from the older ~/.plotter_studio/ if it's there).

Development

# Backend tests
uv run --no-sync pytest

# Frontend dev server with hot reload (proxies /api to Flask on :7438)
cd frontend && npm run dev

# Frontend build (regenerates web/static/app)
cd frontend && npm run build

# End-to-end tests (Playwright, isolated HOME + locked env)
cd frontend && npm run e2e

# Regenerate the manual's parameter reference after editing engine/params.py
uv run --no-sync python tools/build_docs_reference.py

# Check the manual for broken links and stale screenshots
uv run --no-sync python tools/check_docs.py

# Retake every manual screenshot from the live app (opt-in)
cd frontend && DOCS_CAPTURE=1 \
  E2E_BACKEND_CMD="uv run --no-sync python -m web.server" \
  SAM2_CHECKPOINT="$HOME/.plotterforge/models/sam2.1_hiera_tiny.pt" \
  npx playwright test docs-capture

docs-capture.spec.ts rebuilds each documented app state and rewrites web/static/docs/img/*.png, so the manual's screenshots always show the current UI. Rerun it after a change that alters what those states look like. Keep the two env vars: the e2e suite's default backend has no Torch, SAM2, or checkpoint, so capturing against it bakes a CPU · numpy badge and a red "setup is incomplete" error into the manual.

The deterministic CPU/MPS/CUDA/browser profiling suite is documented in docs/profiling.md; CI publishes a performance profile on every pull request. Product notes live in docs/product-roadmap.md.

Wireless plotting from Inkscape

Prefer to plot from Inkscape? The bridge/ setup exposes a plotter connected to a Raspberry Pi as a local virtual serial port over Tailscale, so the UUNA TEK / iDraw extension works wirelessly. The bridge holds the serial port while running, so stop it before plotting from PlotterForge.

Project layout

engine/        Conversion engine: styles, samplers, pens, drawing area,
               version control, GPU/CPU backend, SVG output
plotter/       Plotter side: SVG → polylines, path ordering, travel estimates,
               parsed-path cache
integrations/  Third-party adapters (SAM2 segmentation)
frontend/      Svelte 5 SPA; `npm run build` → web/static/app
web/           Flask app: routes/ blueprints, services/ operations, Grbl driver
docs/          Developer docs: Cavalry guide, profiling, roadmap, design notes
tests/         Backend test suite (pytest)
tools/         Repo-local entry points (docs generator/checker)
profiling/     Deterministic performance profiling suite
bridge/        Wireless serial bridge: plot from Mac Inkscape via a Pi
cavalry/       Cavalry UI script for live capture + tessellation baking
pyproject.toml uv manifest (engine + web deps; cuda/mps + sam2 extras)
uv.lock        locked dependency tree

License

MIT