diff --git a/.changeset/dd017-phase1-expr-params.md b/.changeset/dd017-phase1-expr-params.md deleted file mode 100644 index c1349281..00000000 --- a/.changeset/dd017-phase1-expr-params.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -"@chestnutlabs/gcode-parser": minor ---- - -feat(parser): RS274NGC parameters + expressions (DD-017 Phase 1, #189 phase 7) - -The parser now understands the RS274NGC *programming* layer that FDM slicers never use but CAM / -LinuxCNC output routinely does: numbered/named/global parameters (`#100`, `#`, `#<_global>`), -assignment (`#n = `), and bracket expressions (`[ … ]`) usable wherever a numeric word value is -expected (`X[#1+2]`, `F[#feed]`, `Z#`). Previously these were silently dropped, so parametric -programs rendered honest-but-**empty**; they now resolve to real geometry. - -A new self-contained module (`rs274.ts`) provides a pure recursive-descent evaluator — the RS274NGC -operator + function set with correct precedence (`**` > `*/MOD` > `+-` > comparisons > logical), -**degree**-based trig, LinuxCNC `MOD`/`EQ` semantics, `FIX`/`FUP`/`ROUND`, indirect `##n` and computed -`#[expr]` references, and a read-only system-parameter allow-list (`#5420–#5422` = current position). -It is bounded and safe: a depth guard on nested brackets, **no `eval`/`Function`**, and a capped -parameter store, so a hostile program can waste only bounded CPU. Malformed input never throws out of -the parse — the offending word is dropped with a specific disclosure (`rs274-bad-expression`, -`rs274-uninitialized-param`, `rs274-unsupported-sysparam`) and parsing continues. - -Honest and additive: a new `parametricProgram` capability (`known` when executed, `unavailable` -otherwise). Engaged only on lines that use `#`/`[` — every FDM and simple-CNC line takes the untouched -lexer and is **byte-identical** (native goldens regenerated for the additive capability key only; the -detection gate adds ~2.7% to a large FDM parse, geometry unchanged). - -**Scope: Phase 1 (parameters + expressions) only.** O-word control flow (`if`/`while`/`sub`/`call`) is a -later phase and is not yet supported — such constructs are ignored (and will be disclosed) for now. diff --git a/.changeset/dd017-phase2-oword-flow.md b/.changeset/dd017-phase2-oword-flow.md deleted file mode 100644 index 57645c30..00000000 --- a/.changeset/dd017-phase2-oword-flow.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -"@chestnutlabs/gcode-parser": minor ---- - -feat(parser): RS274NGC O-word control flow (DD-017 Phase 2, #189 phase 7) - -Building on Phase 1's parameters + expressions, the parser now **executes** the RS274NGC O-word control -structures that CAM / LinuxCNC programs use to generate geometry: `if`/`elseif`/`else`/`endif`, -`while`/`endwhile`, `do`/`while`, `repeat`/`endrepeat`, and `break`/`continue`. A bolt-circle written as -a `while` loop over a computed angle, a pocket cut by a `repeat`, an `if` that selects a tool path — -these previously ran once (or not at all) because the parser is forward-only; they now resolve to the -full, correct toolpath. - -A new module (`rs274-flow.ts`) is the program-buffered interpreter of DD-017 D1: engaged **only** when a -program actually contains an O-word control line (a cheap detection scan), it buffers the program, builds -a block tree, and executes it — feeding every non-control line back through the same engine so -geometry/params/expressions stay on one code path. Loops evaluate their conditions/counts against the -same shared parameter store, so `# = [# + 1]` inside a loop body does what you expect. - -**Bounded and safe (this is now a small interpreter).** A new `maxProgramIterations` limit (default -`1_000_000`) bounds **total loop work** — charged per loop pass *and* per statement executed inside a -loop body, so a large body cannot multiply the real work past the cap — and stops a runaway `o while [1]` -with a partial result and a `rs274-iteration-limit` disclosure. Structural nesting is capped; loops are -ordinary iteration, not recursion, so an adversarial program can waste only bounded CPU — it cannot hang, -blow the stack, or OOM the worker. There is no `eval` and no I/O. - -**Honest and additive.** Clean flow keeps `parametricProgram: 'known'`; a degraded run (a hit iteration -cap, an unbalanced or unsupported O-word, or a malformed condition) reports `'approximated'` and always -emits a specific disclosure — never silently wrong. Subroutines (`sub`/`call`/`return`) are a later phase -and are explicitly disclosed as unsupported and **not** executed inline. Non-parametric input never -enters the interpreter and stays **byte-identical** (both golden suites unchanged). Streaming input, which -cannot re-run a line range from a partial stream, discloses and runs linearly. diff --git a/.changeset/dd017-phase3-subroutines.md b/.changeset/dd017-phase3-subroutines.md deleted file mode 100644 index 757e6bd0..00000000 --- a/.changeset/dd017-phase3-subroutines.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -"@chestnutlabs/gcode-parser": minor ---- - -feat(parser): RS274NGC subroutines (DD-017 Phase 3, #189 phase 7) - -Completes the RS274NGC programming layer: the parser now executes in-file O-word **subroutines** — -`o sub`/`endsub`, `o call [args]`, and `o return`. A `call` binds its arguments to `#1`..`#30` -in a fresh call frame, runs the named sub's body, and returns; `return` exits early. Subroutines may be -forward-referenced (a `call` before the `sub` that defines it) and may recurse. This is how CAM/LinuxCNC -programs factor repeated geometry (a "stamp" drilled at many positions, a parametric feature reused -across a part). - -**Correct scoping (recursion-safe).** Numbered parameters `#1`..`#30` and non-underscore named parameters -(`#`) are LOCAL to each call frame; `#31`+ and underscore-named (`#<_global>`) are shared globals — -matching LinuxCNC. A recursive sub's `#1` therefore cannot clobber its caller's, and a sub's locals do not -leak out. The main program runs in the base scope, so a program that uses no subroutines behaves exactly -as before (byte-identical; both golden suites unchanged). - -**Bounded and safe.** A new `maxCallDepth` limit (default `50`) caps subroutine recursion — an unbounded -or mutually-recursive `call` is disclosed (`rs274-call-depth`) and skipped, never a stack overflow. A -runtime execution-depth backstop additionally bounds the combined structural-nesting + call-depth JS -recursion. A sub called inside a loop still charges its body work against `maxProgramIterations`, so a huge -sub body cannot multiply work past the cap. `break`/`continue`/`return` are frame-local — they never jump -across a call boundary. - -**Honest.** Clean subroutine execution keeps `parametricProgram: 'known'`; a degraded run (unknown sub, -recursion-limit hit, duplicate definition, misplaced `return`) reports `'approximated'` with a specific -disclosure — never silently wrong. Subroutine return VALUES are not yet propagated to the caller (a -documented Phase-3 limitation; `return` acts as an early exit). External subroutine files (`M98`, -`o call` resolving to a separate `.ngc`) remain an explicit non-goal (no I/O in the parser). diff --git a/.changeset/dd030-bed-geometry.md b/.changeset/dd030-bed-geometry.md deleted file mode 100644 index 57b4a292..00000000 --- a/.changeset/dd030-bed-geometry.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -"@chestnutlabs/gcode-renderer-three": minor ---- - -feat(renderer): non-rectangular build-bed geometry (DD-030 D3) - -The build volume can now draw an **honest non-rectangular bed** — delta, round, or irregular — instead of -only a rectangle. `BuildVolumeDef` gains an optional `shape?: BedShape` -(`{kind:'rect'} | {kind:'circular', center, diameter} | {kind:'polygon', points}`): a circle is -polygonized, the outline is filled and drawn, and the floor grid is **clipped to the outline** so the -printable area reads correctly (a round bed is no longer a square with a circle floating over it). The -`mesh` escape hatch for a fully custom bed mesh is reserved for a later phase. - -`machineToVolume()` now maps a discovered `MachineGeometry.bed` of kind `circular`/`polygon` onto that -`shape`, so a **discovered** round/delta bed renders as its true outline instead of being collapsed to its -bounding rectangle (a visible improvement for delta/round printers). Callers supply the shape (from a -machine profile, a config, wherever); the renderer just draws the polygon — no profile parsing in the -library, no vendor semantics baked in. - -Additive and safe: a bed with no `shape` (or `{kind:'rect'}`) takes the original rectangular path and is -**byte-identical** — the rectangular grid, plate, cage, excluded-region outlines, and origin tripod are -unchanged. The volume cage stays a bounding box (the bed outline carries the shape). First increment of -the DD-030 renderer/viewer interoperability batch. diff --git a/.changeset/dd030-capture.md b/.changeset/dd030-capture.md deleted file mode 100644 index 866c0329..00000000 --- a/.changeset/dd030-capture.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -"@chestnutlabs/gcode-renderer-three": minor -"@chestnutlabs/gcode-model-renderer": minor -"@chestnutlabs/gcode-preview-core": minor -"@chestnutlabs/gcode-preview-element": minor ---- - -feat(renderer): interactive view capture() → Blob (DD-030 D1) - -The interactive viewer can now hand back **what is on screen right now** as an image `Blob` — for a -user-selected thumbnail, a large-file thumbnail fallback, or a screenshot. New `capture(opts?)` where -`opts` is `{ width?, height?, format?, quality?, background? }` (all optional; defaults match the live -view). - -Available on every interactive surface: `GcodePreviewControls.capture()` (so the Vue, React, Svelte, and -Web-Component adapters all inherit it — the Web Component also exposes an imperative `capture()` method), -and `ModelViewer.capture()` on the model-viewer handle. The toolpath `ToolpathRenderer` and the shared -`InteractiveStage` carry the implementation. - -**Mechanism (render-to-target).** Capture renders the current scene + active camera into an off-screen -`WebGLRenderTarget` at the requested size and reads it back, rather than flipping the interactive -context's `preserveDrawingBuffer` (which would tax every interactive frame). That gives an arbitrary -output size and an independent/transparent background **without** disturbing the live view, and reuses the -headless still path's "single render, then read pixels" recipe. The thumbnail is framed at its own aspect -so it isn't distorted; the live view is repainted afterward. The library returns the `Blob` and **never** -triggers a download — the caller owns the pixels (same contract as `renderStill`). - -**Honest.** When the renderer cannot render-to-target (the 2D renderer, a stub GL / no WebGL) or the stage -is disposed / its context is lost, `capture()` rejects with a typed `CaptureUnsupportedError` -(`code: 'E_CAPTURE_UNSUPPORTED'`) — never fabricated output. Purely additive (a new optional method on the -renderer contract; no existing signature changed). Final increment of the DD-030 renderer/viewer -interoperability batch (bed + per-plate scope + capture). diff --git a/.changeset/dd030-render-scope.md b/.changeset/dd030-render-scope.md deleted file mode 100644 index a4aeb35a..00000000 --- a/.changeset/dd030-render-scope.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -"@chestnutlabs/gcode-model-renderer": minor ---- - -feat(model-renderer): per-plate / object-subset render scope (DD-030 D2) - -A caller can now render just **one plate** (or any object/placement subset) of a multi-plate / multi-object -source, instead of always the whole project — so a single-plate variant thumbnail looks like *that* plate. - -New `RenderScope` selector — `{ plateId }`, `{ objectIds }`, or `{ instanceFilter }` — with a pure, -three-free `applyRenderScope(scene, scope)` that returns a filtered `ModelScene` (dropping non-matching -objects/placements) with `bounds` recomputed from the kept placements, so framing follows the subset. -`{ plateId }` is sugar over the placement-level `plateIds` (DD-025); it's generic (a plate is just one way -to derive the subset) and vendor-neutral. - -Wired into both surfaces: -- `renderModelStill` gains a `renderScope?` option; it also folds the scope into the still `cacheKey` so a - plate-1 and a plate-2 thumbnail of the same source key distinctly (an opaque `instanceFilter` is marked - non-cacheable). -- `createModelViewer` gains an initial `renderScope?` and a `setRenderScope(scope | null)` handle method - that rebuilds and reframes to the subset (`null` restores the whole source). - -Honest and additive: omitting `renderScope` renders the whole project exactly as before. A `{ plateId }` -that matches nothing (e.g. a source with no declared plate structure, `capabilities.plates !== 'known'`) -renders an **empty** scene by design — the honest result of selecting a plate that isn't declared; -consumers gate `{ plateId }` on `plates: 'known'`. The source's declared `plates`/`capabilities` are -preserved (only what is *rendered* is narrowed). Second increment of the DD-030 renderer/viewer interop -batch. diff --git a/README.md b/README.md index a5147165..09965583 100644 --- a/README.md +++ b/README.md @@ -395,7 +395,7 @@ row inspects the *toolpath*, the bottom row presents the *source model* (see Design rationale, boundaries, and the accepted architecture live in [`docs/design/`](docs/design); the [docs index](docs/README.md) maps the whole set. Fourteen -packages publish to npm in lockstep (currently **v0.17.0**) with npm provenance. +packages publish to npm in lockstep (currently **v0.18.0**) with npm provenance. ## Development diff --git a/RELEASE_NOTES_DRAFT.md b/RELEASE_NOTES_DRAFT.md new file mode 100644 index 00000000..5b1766f5 --- /dev/null +++ b/RELEASE_NOTES_DRAFT.md @@ -0,0 +1,37 @@ +# Release notes draft — v0.18.0 + +> Auto-generated by `tools/release/stamp-release-docs.mjs` from this release's package +> CHANGELOGs. **Review and rewrite into prose**, fold it into the two curated surfaces in +> `docs/README.md`, then DELETE this file. The release gate (`npm run docs:release-check`) +> blocks promotion to `main` until this file is gone and those surfaces name v0.18.0. + +## 1. Update the "Current state" block (docs/README.md) +Lead it with the new version, e.g.: + +`## Current state (updated — **v0.18.0 shipped to npm** (14 lockstep pkgs): . Prior: **v** …)` + +## 2. Add the release-history line (docs/README.md, the "publishing from tagged … Releases (…)" list) +Append: `` `v0.18.0` `` + +## Proposed points (deduped from this release's changelogs) +- [#418](https://github.com/ChestnutLabs/gcode-preview/pull/418) [`c950339`](https://github.com/ChestnutLabs/gcode-preview/commit/c95033908e3ebaeb07b6f7e6f2487672dcc463f0) Thanks [@sobechestnut-dev](https://github.com/sobechestnut-dev)! - feat(renderer): interactive view capture() → Blob (DD-030 D1) +- [#417](https://github.com/ChestnutLabs/gcode-preview/pull/417) [`5dc8079`](https://github.com/ChestnutLabs/gcode-preview/commit/5dc80794444742beddafe1c37322db751af50020) Thanks [@sobechestnut-dev](https://github.com/sobechestnut-dev)! - feat(model-renderer): per-plate / object-subset render scope (DD-030 D2) +- `renderModelStill` gains a `renderScope?` option; it also folds the scope into the still `cacheKey` so a +- `createModelViewer` gains an initial `renderScope?` and a `setRenderScope(scope | null)` handle method +- [#410](https://github.com/ChestnutLabs/gcode-preview/pull/410) [`5144890`](https://github.com/ChestnutLabs/gcode-preview/commit/51448906f87ca09f3ed11f1286f7b79356110a83) Thanks [@sobechestnut-dev](https://github.com/sobechestnut-dev)! - feat(parser): RS274NGC parameters + expressions (DD-017 Phase 1, [#189](https://github.com/ChestnutLabs/gcode-preview/issues/189) phase 7) +- [#413](https://github.com/ChestnutLabs/gcode-preview/pull/413) [`bcb4b78`](https://github.com/ChestnutLabs/gcode-preview/commit/bcb4b786fcfc6a2e967d8bfff1d4fd3a06731681) Thanks [@sobechestnut-dev](https://github.com/sobechestnut-dev)! - feat(parser): RS274NGC O-word control flow (DD-017 Phase 2, [#189](https://github.com/ChestnutLabs/gcode-preview/issues/189) phase 7) +- [#415](https://github.com/ChestnutLabs/gcode-preview/pull/415) [`dc89056`](https://github.com/ChestnutLabs/gcode-preview/commit/dc890568d45e59635b9994d8409ef6cdc0847f80) Thanks [@sobechestnut-dev](https://github.com/sobechestnut-dev)! - feat(parser): RS274NGC subroutines (DD-017 Phase 3, [#189](https://github.com/ChestnutLabs/gcode-preview/issues/189) phase 7) +- [#416](https://github.com/ChestnutLabs/gcode-preview/pull/416) [`0ebeadf`](https://github.com/ChestnutLabs/gcode-preview/commit/0ebeadfa839e52baf243ef07b5807f3974bbac7e) Thanks [@sobechestnut-dev](https://github.com/sobechestnut-dev)! - feat(renderer): non-rectangular build-bed geometry (DD-030 D3) + +## Packages changed this release +- @chestnutlabs/gcode-model-renderer +- @chestnutlabs/gcode-parser +- @chestnutlabs/gcode-preview-core +- @chestnutlabs/gcode-preview-element +- @chestnutlabs/gcode-renderer-three + +## Before you cut the tag — public-docs completion check (CLAUDE.md) +- [ ] Root README still accurate for any new user-visible capability? +- [ ] Package README / user guide updated where behavior changed? +- [ ] Any existing screenshot now misrepresents the software? (regenerate via tools/screenshots/) +- [ ] Quick-start / example code still shows the recommended API? diff --git a/docs/README.md b/docs/README.md index 3204c67e..03b7b6f4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -67,7 +67,7 @@ golden/native fixtures + manifest with CI validation, boundary lint, license CI consumer smoke harnesses, plus the five framework adapters (`gcode-preview-core`/`-vue`/`-react`/ `-svelte`/`-element`) and the presentation renderer (`gcode-model-renderer`). CI (`build`) is a required check on `main`/`dev`. **All fourteen `@chestnutlabs/*` packages are published to npm at -`v0.17.0`** (lockstep, npm provenance); `main` is the release branch, publishing from tagged GitHub +`v0.18.0`** (lockstep, npm provenance); `main` is the release branch, publishing from tagged GitHub Releases (`v0.1.0` E7/DD-008, `v0.2.0` E9/DD-009, `v0.3.0` E8/#188, `v0.4.0` #189 non-extrusion, `v0.5.x` hardening #279, `v0.6.0`/`v0.7.0` ModelRenderer + toolpath batch #296/#306, `v0.8.1` 3MF `paint_color` multicolor decode, `v0.9.0` interactive `createModelViewer` #296/DD-021, `v0.10.0` diff --git a/docs/manual/index.md b/docs/manual/index.md index 244037b7..56d339cc 100644 --- a/docs/manual/index.md +++ b/docs/manual/index.md @@ -67,6 +67,6 @@ viewer is reachable without switching APIs. [Live progress & motion model](concept-progress-motion.md). - **API reference** — pick a package in the sidebar (`toolpath-core`, `gcode-parser`, …). -> **Status:** published — all fourteen `@chestnutlabs/*` packages are on npm (latest `v0.17.0`, +> **Status:** published — all fourteen `@chestnutlabs/*` packages are on npm (latest `v0.18.0`, > lockstep-versioned with provenance). Governance and epic status live in the > [repository docs](https://github.com/ChestnutLabs/gcode-preview/tree/dev/docs). diff --git a/package-lock.json b/package-lock.json index a039c8b3..ad9c304f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7389,11 +7389,11 @@ }, "packages/gcode-bgcode": { "name": "@chestnutlabs/gcode-bgcode", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@chestnutlabs/gcode-containers": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-containers": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" }, "engines": { "node": ">=22" @@ -7401,10 +7401,10 @@ }, "packages/gcode-colors": { "name": "@chestnutlabs/gcode-colors", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/toolpath-core": "0.18.0" }, "engines": { "node": ">=22" @@ -7412,10 +7412,10 @@ }, "packages/gcode-containers": { "name": "@chestnutlabs/gcode-containers", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/toolpath-core": "0.18.0" }, "engines": { "node": ">=22" @@ -7423,10 +7423,10 @@ }, "packages/gcode-dialects": { "name": "@chestnutlabs/gcode-dialects", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/toolpath-core": "0.18.0" }, "engines": { "node": ">=22" @@ -7434,12 +7434,12 @@ }, "packages/gcode-model-renderer": { "name": "@chestnutlabs/gcode-model-renderer", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@chestnutlabs/gcode-containers": "0.17.0", - "@chestnutlabs/gcode-renderer-three": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-containers": "0.18.0", + "@chestnutlabs/gcode-renderer-three": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" }, "devDependencies": { "@types/three": "0.178.0" @@ -7453,13 +7453,13 @@ }, "packages/gcode-parser": { "name": "@chestnutlabs/gcode-parser", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@chestnutlabs/gcode-bgcode": "0.17.0", - "@chestnutlabs/gcode-containers": "0.17.0", - "@chestnutlabs/gcode-dialects": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-bgcode": "0.18.0", + "@chestnutlabs/gcode-containers": "0.18.0", + "@chestnutlabs/gcode-dialects": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" }, "engines": { "node": ">=22" @@ -7467,13 +7467,13 @@ }, "packages/gcode-preview-core": { "name": "@chestnutlabs/gcode-preview-core", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@chestnutlabs/gcode-parser": "0.17.0", - "@chestnutlabs/gcode-renderer-2d": "0.17.0", - "@chestnutlabs/gcode-renderer-three": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-parser": "0.18.0", + "@chestnutlabs/gcode-renderer-2d": "0.18.0", + "@chestnutlabs/gcode-renderer-three": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" }, "engines": { "node": ">=22" @@ -7481,13 +7481,13 @@ }, "packages/gcode-preview-element": { "name": "@chestnutlabs/gcode-preview-element", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@chestnutlabs/gcode-parser": "0.17.0", - "@chestnutlabs/gcode-preview-core": "0.17.0", - "@chestnutlabs/gcode-renderer-three": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-parser": "0.18.0", + "@chestnutlabs/gcode-preview-core": "0.18.0", + "@chestnutlabs/gcode-renderer-three": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" }, "engines": { "node": ">=22" @@ -7495,13 +7495,13 @@ }, "packages/gcode-preview-react": { "name": "@chestnutlabs/gcode-preview-react", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@chestnutlabs/gcode-parser": "0.17.0", - "@chestnutlabs/gcode-preview-core": "0.17.0", - "@chestnutlabs/gcode-renderer-three": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-parser": "0.18.0", + "@chestnutlabs/gcode-preview-core": "0.18.0", + "@chestnutlabs/gcode-renderer-three": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" }, "devDependencies": { "@types/react": "^18.3.0", @@ -7517,13 +7517,13 @@ }, "packages/gcode-preview-svelte": { "name": "@chestnutlabs/gcode-preview-svelte", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@chestnutlabs/gcode-parser": "0.17.0", - "@chestnutlabs/gcode-preview-core": "0.17.0", - "@chestnutlabs/gcode-renderer-three": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-parser": "0.18.0", + "@chestnutlabs/gcode-preview-core": "0.18.0", + "@chestnutlabs/gcode-renderer-three": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" }, "devDependencies": { "svelte": "^4.2.0" @@ -7537,13 +7537,13 @@ }, "packages/gcode-preview-vue": { "name": "@chestnutlabs/gcode-preview-vue", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@chestnutlabs/gcode-parser": "0.17.0", - "@chestnutlabs/gcode-preview-core": "0.17.0", - "@chestnutlabs/gcode-renderer-three": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-parser": "0.18.0", + "@chestnutlabs/gcode-preview-core": "0.18.0", + "@chestnutlabs/gcode-renderer-three": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" }, "devDependencies": { "vue": "^3.4.0" @@ -7557,11 +7557,11 @@ }, "packages/gcode-renderer-2d": { "name": "@chestnutlabs/gcode-renderer-2d", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@chestnutlabs/gcode-colors": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-colors": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" }, "engines": { "node": ">=22" @@ -7569,11 +7569,11 @@ }, "packages/gcode-renderer-three": { "name": "@chestnutlabs/gcode-renderer-three", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "dependencies": { - "@chestnutlabs/gcode-colors": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-colors": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" }, "devDependencies": { "@types/three": "0.178.0" @@ -7587,7 +7587,7 @@ }, "packages/toolpath-core": { "name": "@chestnutlabs/toolpath-core", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "engines": { "node": ">=22" diff --git a/packages/gcode-bgcode/CHANGELOG.md b/packages/gcode-bgcode/CHANGELOG.md index c9a8b027..af1c11f5 100644 --- a/packages/gcode-bgcode/CHANGELOG.md +++ b/packages/gcode-bgcode/CHANGELOG.md @@ -1,5 +1,13 @@ # @chestnutlabs/gcode-bgcode +## 0.18.0 + +### Patch Changes + +- Updated dependencies []: + - @chestnutlabs/gcode-containers@0.18.0 + - @chestnutlabs/toolpath-core@0.18.0 + ## 0.17.0 ### Patch Changes diff --git a/packages/gcode-bgcode/package.json b/packages/gcode-bgcode/package.json index fe359813..2b6dd74c 100644 --- a/packages/gcode-bgcode/package.json +++ b/packages/gcode-bgcode/package.json @@ -1,6 +1,6 @@ { "name": "@chestnutlabs/gcode-bgcode", - "version": "0.17.0", + "version": "0.18.0", "description": "Binary G-code (.bgcode) decode adapter for the Chestnut Labs G-code toolpath stack (DD-011): a license-clean, in-memory block walker that decodes Prusa .bgcode to plain G-code for the existing parser/dialect/renderer pipeline. Decode-only.", "keywords": [ "gcode", @@ -47,7 +47,7 @@ "test": "vitest run" }, "dependencies": { - "@chestnutlabs/toolpath-core": "0.17.0", - "@chestnutlabs/gcode-containers": "0.17.0" + "@chestnutlabs/toolpath-core": "0.18.0", + "@chestnutlabs/gcode-containers": "0.18.0" } } diff --git a/packages/gcode-colors/CHANGELOG.md b/packages/gcode-colors/CHANGELOG.md index 86e4aa83..fe63fb2e 100644 --- a/packages/gcode-colors/CHANGELOG.md +++ b/packages/gcode-colors/CHANGELOG.md @@ -1,5 +1,12 @@ # @chestnutlabs/gcode-colors +## 0.18.0 + +### Patch Changes + +- Updated dependencies []: + - @chestnutlabs/toolpath-core@0.18.0 + ## 0.17.0 ### Patch Changes diff --git a/packages/gcode-colors/package.json b/packages/gcode-colors/package.json index b6f13bab..aa9e8c07 100644 --- a/packages/gcode-colors/package.json +++ b/packages/gcode-colors/package.json @@ -1,6 +1,6 @@ { "name": "@chestnutlabs/gcode-colors", - "version": "0.17.0", + "version": "0.18.0", "description": "Renderer-agnostic per-segment color model for the Chestnut Labs G-code toolpath stack (DD-014 D3): the ColorMode union and honest, capability-gated segment coloring over ToolpathIR channels, shared by the 3D and 2D renderers.", "keywords": [ "gcode", @@ -47,6 +47,6 @@ "test": "vitest run" }, "dependencies": { - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/toolpath-core": "0.18.0" } } diff --git a/packages/gcode-containers/CHANGELOG.md b/packages/gcode-containers/CHANGELOG.md index 56f9ab88..2c63739e 100644 --- a/packages/gcode-containers/CHANGELOG.md +++ b/packages/gcode-containers/CHANGELOG.md @@ -1,5 +1,12 @@ # @chestnutlabs/gcode-containers +## 0.18.0 + +### Patch Changes + +- Updated dependencies []: + - @chestnutlabs/toolpath-core@0.18.0 + ## 0.17.0 ### Patch Changes diff --git a/packages/gcode-containers/package.json b/packages/gcode-containers/package.json index b6cae034..9bc84ffe 100644 --- a/packages/gcode-containers/package.json +++ b/packages/gcode-containers/package.json @@ -1,6 +1,6 @@ { "name": "@chestnutlabs/gcode-containers", - "version": "0.17.0", + "version": "0.18.0", "description": "Safe, bounded, in-memory container extraction for sliced G-code (.gcode.3mf) — DD-005 §4.4/§7. Never writes files, never fetches, zero dependencies.", "keywords": [ "gcode", @@ -42,6 +42,6 @@ "test": "vitest run" }, "dependencies": { - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/toolpath-core": "0.18.0" } } diff --git a/packages/gcode-dialects/CHANGELOG.md b/packages/gcode-dialects/CHANGELOG.md index f91b37ff..1f808aba 100644 --- a/packages/gcode-dialects/CHANGELOG.md +++ b/packages/gcode-dialects/CHANGELOG.md @@ -1,5 +1,12 @@ # @chestnutlabs/gcode-dialects +## 0.18.0 + +### Patch Changes + +- Updated dependencies []: + - @chestnutlabs/toolpath-core@0.18.0 + ## 0.17.0 ### Minor Changes diff --git a/packages/gcode-dialects/package.json b/packages/gcode-dialects/package.json index 6b399c53..1244a8c1 100644 --- a/packages/gcode-dialects/package.json +++ b/packages/gcode-dialects/package.json @@ -1,6 +1,6 @@ { "name": "@chestnutlabs/gcode-dialects", - "version": "0.17.0", + "version": "0.18.0", "description": "Slicer/firmware dialect adapters for ToolpathIR annotation (DD-005). Adapters annotate metadata and optional channels — they can never alter geometry.", "keywords": [ "gcode", @@ -45,6 +45,6 @@ "test": "vitest run" }, "dependencies": { - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/toolpath-core": "0.18.0" } } diff --git a/packages/gcode-model-renderer/CHANGELOG.md b/packages/gcode-model-renderer/CHANGELOG.md index 7bcb2fa0..653fd651 100644 --- a/packages/gcode-model-renderer/CHANGELOG.md +++ b/packages/gcode-model-renderer/CHANGELOG.md @@ -1,5 +1,67 @@ # @chestnutlabs/gcode-model-renderer +## 0.18.0 + +### Minor Changes + +- [#418](https://github.com/ChestnutLabs/gcode-preview/pull/418) [`c950339`](https://github.com/ChestnutLabs/gcode-preview/commit/c95033908e3ebaeb07b6f7e6f2487672dcc463f0) Thanks [@sobechestnut-dev](https://github.com/sobechestnut-dev)! - feat(renderer): interactive view capture() → Blob (DD-030 D1) + + The interactive viewer can now hand back **what is on screen right now** as an image `Blob` — for a + user-selected thumbnail, a large-file thumbnail fallback, or a screenshot. New `capture(opts?)` where + `opts` is `{ width?, height?, format?, quality?, background? }` (all optional; defaults match the live + view). + + Available on every interactive surface: `GcodePreviewControls.capture()` (so the Vue, React, Svelte, and + Web-Component adapters all inherit it — the Web Component also exposes an imperative `capture()` method), + and `ModelViewer.capture()` on the model-viewer handle. The toolpath `ToolpathRenderer` and the shared + `InteractiveStage` carry the implementation. + + **Mechanism (render-to-target).** Capture renders the current scene + active camera into an off-screen + `WebGLRenderTarget` at the requested size and reads it back, rather than flipping the interactive + context's `preserveDrawingBuffer` (which would tax every interactive frame). That gives an arbitrary + output size and an independent/transparent background **without** disturbing the live view, and reuses the + headless still path's "single render, then read pixels" recipe. The thumbnail is framed at its own aspect + so it isn't distorted; the live view is repainted afterward. The library returns the `Blob` and **never** + triggers a download — the caller owns the pixels (same contract as `renderStill`). + + **Honest.** When the renderer cannot render-to-target (the 2D renderer, a stub GL / no WebGL) or the stage + is disposed / its context is lost, `capture()` rejects with a typed `CaptureUnsupportedError` + (`code: 'E_CAPTURE_UNSUPPORTED'`) — never fabricated output. Purely additive (a new optional method on the + renderer contract; no existing signature changed). Final increment of the DD-030 renderer/viewer + interoperability batch (bed + per-plate scope + capture). + +- [#417](https://github.com/ChestnutLabs/gcode-preview/pull/417) [`5dc8079`](https://github.com/ChestnutLabs/gcode-preview/commit/5dc80794444742beddafe1c37322db751af50020) Thanks [@sobechestnut-dev](https://github.com/sobechestnut-dev)! - feat(model-renderer): per-plate / object-subset render scope (DD-030 D2) + + A caller can now render just **one plate** (or any object/placement subset) of a multi-plate / multi-object + source, instead of always the whole project — so a single-plate variant thumbnail looks like _that_ plate. + + New `RenderScope` selector — `{ plateId }`, `{ objectIds }`, or `{ instanceFilter }` — with a pure, + three-free `applyRenderScope(scene, scope)` that returns a filtered `ModelScene` (dropping non-matching + objects/placements) with `bounds` recomputed from the kept placements, so framing follows the subset. + `{ plateId }` is sugar over the placement-level `plateIds` (DD-025); it's generic (a plate is just one way + to derive the subset) and vendor-neutral. + + Wired into both surfaces: + - `renderModelStill` gains a `renderScope?` option; it also folds the scope into the still `cacheKey` so a + plate-1 and a plate-2 thumbnail of the same source key distinctly (an opaque `instanceFilter` is marked + non-cacheable). + - `createModelViewer` gains an initial `renderScope?` and a `setRenderScope(scope | null)` handle method + that rebuilds and reframes to the subset (`null` restores the whole source). + + Honest and additive: omitting `renderScope` renders the whole project exactly as before. A `{ plateId }` + that matches nothing (e.g. a source with no declared plate structure, `capabilities.plates !== 'known'`) + renders an **empty** scene by design — the honest result of selecting a plate that isn't declared; + consumers gate `{ plateId }` on `plates: 'known'`. The source's declared `plates`/`capabilities` are + preserved (only what is _rendered_ is narrowed). Second increment of the DD-030 renderer/viewer interop + batch. + +### Patch Changes + +- Updated dependencies [[`0ebeadf`](https://github.com/ChestnutLabs/gcode-preview/commit/0ebeadfa839e52baf243ef07b5807f3974bbac7e), [`c950339`](https://github.com/ChestnutLabs/gcode-preview/commit/c95033908e3ebaeb07b6f7e6f2487672dcc463f0)]: + - @chestnutlabs/gcode-renderer-three@0.18.0 + - @chestnutlabs/gcode-containers@0.18.0 + - @chestnutlabs/toolpath-core@0.18.0 + ## 0.17.0 ### Patch Changes diff --git a/packages/gcode-model-renderer/package.json b/packages/gcode-model-renderer/package.json index e6243b65..29911c54 100644 --- a/packages/gcode-model-renderer/package.json +++ b/packages/gcode-model-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@chestnutlabs/gcode-model-renderer", - "version": "0.17.0", + "version": "0.18.0", "description": "Three.js presentation renderer for source models (STL/3MF) — clean thumbnails/cards, distinct from toolpath inspection (DD-018).", "keywords": [ "3d-printing", @@ -51,9 +51,9 @@ "test": "vitest run" }, "dependencies": { - "@chestnutlabs/gcode-containers": "0.17.0", - "@chestnutlabs/gcode-renderer-three": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-containers": "0.18.0", + "@chestnutlabs/gcode-renderer-three": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" }, "devDependencies": { "@types/three": "0.178.0" diff --git a/packages/gcode-parser/CHANGELOG.md b/packages/gcode-parser/CHANGELOG.md index b8e912ce..fa0a7bb3 100644 --- a/packages/gcode-parser/CHANGELOG.md +++ b/packages/gcode-parser/CHANGELOG.md @@ -1,5 +1,99 @@ # @chestnutlabs/gcode-parser +## 0.18.0 + +### Minor Changes + +- [#410](https://github.com/ChestnutLabs/gcode-preview/pull/410) [`5144890`](https://github.com/ChestnutLabs/gcode-preview/commit/51448906f87ca09f3ed11f1286f7b79356110a83) Thanks [@sobechestnut-dev](https://github.com/sobechestnut-dev)! - feat(parser): RS274NGC parameters + expressions (DD-017 Phase 1, [#189](https://github.com/ChestnutLabs/gcode-preview/issues/189) phase 7) + + The parser now understands the RS274NGC _programming_ layer that FDM slicers never use but CAM / + LinuxCNC output routinely does: numbered/named/global parameters (`[#100](https://github.com/ChestnutLabs/gcode-preview/issues/100)`, `#`, `#<_global>`), + assignment (`#n = `), and bracket expressions (`[ … ]`) usable wherever a numeric word value is + expected (`X[[#1](https://github.com/ChestnutLabs/gcode-preview/issues/1)+2]`, `F[#feed]`, `Z#`). Previously these were silently dropped, so parametric + programs rendered honest-but-**empty**; they now resolve to real geometry. + + A new self-contained module (`rs274.ts`) provides a pure recursive-descent evaluator — the RS274NGC + operator + function set with correct precedence (`**` > `*/MOD` > `+-` > comparisons > logical), + **degree**-based trig, LinuxCNC `MOD`/`EQ` semantics, `FIX`/`FUP`/`ROUND`, indirect `##n` and computed + `#[expr]` references, and a read-only system-parameter allow-list (`[#5420](https://github.com/ChestnutLabs/gcode-preview/issues/5420)–[#5422](https://github.com/ChestnutLabs/gcode-preview/issues/5422)` = current position). + It is bounded and safe: a depth guard on nested brackets, **no `eval`/`Function`**, and a capped + parameter store, so a hostile program can waste only bounded CPU. Malformed input never throws out of + the parse — the offending word is dropped with a specific disclosure (`rs274-bad-expression`, + `rs274-uninitialized-param`, `rs274-unsupported-sysparam`) and parsing continues. + + Honest and additive: a new `parametricProgram` capability (`known` when executed, `unavailable` + otherwise). Engaged only on lines that use `#`/`[` — every FDM and simple-CNC line takes the untouched + lexer and is **byte-identical** (native goldens regenerated for the additive capability key only; the + detection gate adds ~2.7% to a large FDM parse, geometry unchanged). + + **Scope: Phase 1 (parameters + expressions) only.** O-word control flow (`if`/`while`/`sub`/`call`) is a + later phase and is not yet supported — such constructs are ignored (and will be disclosed) for now. + +- [#413](https://github.com/ChestnutLabs/gcode-preview/pull/413) [`bcb4b78`](https://github.com/ChestnutLabs/gcode-preview/commit/bcb4b786fcfc6a2e967d8bfff1d4fd3a06731681) Thanks [@sobechestnut-dev](https://github.com/sobechestnut-dev)! - feat(parser): RS274NGC O-word control flow (DD-017 Phase 2, [#189](https://github.com/ChestnutLabs/gcode-preview/issues/189) phase 7) + + Building on Phase 1's parameters + expressions, the parser now **executes** the RS274NGC O-word control + structures that CAM / LinuxCNC programs use to generate geometry: `if`/`elseif`/`else`/`endif`, + `while`/`endwhile`, `do`/`while`, `repeat`/`endrepeat`, and `break`/`continue`. A bolt-circle written as + a `while` loop over a computed angle, a pocket cut by a `repeat`, an `if` that selects a tool path — + these previously ran once (or not at all) because the parser is forward-only; they now resolve to the + full, correct toolpath. + + A new module (`rs274-flow.ts`) is the program-buffered interpreter of DD-017 D1: engaged **only** when a + program actually contains an O-word control line (a cheap detection scan), it buffers the program, builds + a block tree, and executes it — feeding every non-control line back through the same engine so + geometry/params/expressions stay on one code path. Loops evaluate their conditions/counts against the + same shared parameter store, so `# = [# + 1]` inside a loop body does what you expect. + + **Bounded and safe (this is now a small interpreter).** A new `maxProgramIterations` limit (default + `1_000_000`) bounds **total loop work** — charged per loop pass _and_ per statement executed inside a + loop body, so a large body cannot multiply the real work past the cap — and stops a runaway `o while [1]` + with a partial result and a `rs274-iteration-limit` disclosure. Structural nesting is capped; loops are + ordinary iteration, not recursion, so an adversarial program can waste only bounded CPU — it cannot hang, + blow the stack, or OOM the worker. There is no `eval` and no I/O. + + **Honest and additive.** Clean flow keeps `parametricProgram: 'known'`; a degraded run (a hit iteration + cap, an unbalanced or unsupported O-word, or a malformed condition) reports `'approximated'` and always + emits a specific disclosure — never silently wrong. Subroutines (`sub`/`call`/`return`) are a later phase + and are explicitly disclosed as unsupported and **not** executed inline. Non-parametric input never + enters the interpreter and stays **byte-identical** (both golden suites unchanged). Streaming input, which + cannot re-run a line range from a partial stream, discloses and runs linearly. + +- [#415](https://github.com/ChestnutLabs/gcode-preview/pull/415) [`dc89056`](https://github.com/ChestnutLabs/gcode-preview/commit/dc890568d45e59635b9994d8409ef6cdc0847f80) Thanks [@sobechestnut-dev](https://github.com/sobechestnut-dev)! - feat(parser): RS274NGC subroutines (DD-017 Phase 3, [#189](https://github.com/ChestnutLabs/gcode-preview/issues/189) phase 7) + + Completes the RS274NGC programming layer: the parser now executes in-file O-word **subroutines** — + `o sub`/`endsub`, `o call [args]`, and `o return`. A `call` binds its arguments to `[#1](https://github.com/ChestnutLabs/gcode-preview/issues/1)`..`[#30](https://github.com/ChestnutLabs/gcode-preview/issues/30)` + in a fresh call frame, runs the named sub's body, and returns; `return` exits early. Subroutines may be + forward-referenced (a `call` before the `sub` that defines it) and may recurse. This is how CAM/LinuxCNC + programs factor repeated geometry (a "stamp" drilled at many positions, a parametric feature reused + across a part). + + **Correct scoping (recursion-safe).** Numbered parameters `[#1](https://github.com/ChestnutLabs/gcode-preview/issues/1)`..`[#30](https://github.com/ChestnutLabs/gcode-preview/issues/30)` and non-underscore named parameters + (`#`) are LOCAL to each call frame; `[#31](https://github.com/ChestnutLabs/gcode-preview/issues/31)`+ and underscore-named (`#<_global>`) are shared globals — + matching LinuxCNC. A recursive sub's `[#1](https://github.com/ChestnutLabs/gcode-preview/issues/1)` therefore cannot clobber its caller's, and a sub's locals do not + leak out. The main program runs in the base scope, so a program that uses no subroutines behaves exactly + as before (byte-identical; both golden suites unchanged). + + **Bounded and safe.** A new `maxCallDepth` limit (default `50`) caps subroutine recursion — an unbounded + or mutually-recursive `call` is disclosed (`rs274-call-depth`) and skipped, never a stack overflow. A + runtime execution-depth backstop additionally bounds the combined structural-nesting + call-depth JS + recursion. A sub called inside a loop still charges its body work against `maxProgramIterations`, so a huge + sub body cannot multiply work past the cap. `break`/`continue`/`return` are frame-local — they never jump + across a call boundary. + + **Honest.** Clean subroutine execution keeps `parametricProgram: 'known'`; a degraded run (unknown sub, + recursion-limit hit, duplicate definition, misplaced `return`) reports `'approximated'` with a specific + disclosure — never silently wrong. Subroutine return VALUES are not yet propagated to the caller (a + documented Phase-3 limitation; `return` acts as an early exit). External subroutine files (`M98`, + `o call` resolving to a separate `.ngc`) remain an explicit non-goal (no I/O in the parser). + +### Patch Changes + +- Updated dependencies []: + - @chestnutlabs/gcode-bgcode@0.18.0 + - @chestnutlabs/gcode-containers@0.18.0 + - @chestnutlabs/gcode-dialects@0.18.0 + - @chestnutlabs/toolpath-core@0.18.0 + ## 0.17.0 ### Minor Changes diff --git a/packages/gcode-parser/package.json b/packages/gcode-parser/package.json index a46df773..a4c7a7ec 100644 --- a/packages/gcode-parser/package.json +++ b/packages/gcode-parser/package.json @@ -1,6 +1,6 @@ { "name": "@chestnutlabs/gcode-parser", - "version": "0.17.0", + "version": "0.18.0", "description": "Worker-safe G-code parse core producing ToolpathIR (DD-003).", "keywords": [ "gcode", @@ -49,9 +49,9 @@ "test": "vitest run" }, "dependencies": { - "@chestnutlabs/toolpath-core": "0.17.0", - "@chestnutlabs/gcode-dialects": "0.17.0", - "@chestnutlabs/gcode-containers": "0.17.0", - "@chestnutlabs/gcode-bgcode": "0.17.0" + "@chestnutlabs/toolpath-core": "0.18.0", + "@chestnutlabs/gcode-dialects": "0.18.0", + "@chestnutlabs/gcode-containers": "0.18.0", + "@chestnutlabs/gcode-bgcode": "0.18.0" } } diff --git a/packages/gcode-preview-core/CHANGELOG.md b/packages/gcode-preview-core/CHANGELOG.md index f7db19ff..59cddf00 100644 --- a/packages/gcode-preview-core/CHANGELOG.md +++ b/packages/gcode-preview-core/CHANGELOG.md @@ -1,5 +1,43 @@ # @chestnutlabs/gcode-preview-core +## 0.18.0 + +### Minor Changes + +- [#418](https://github.com/ChestnutLabs/gcode-preview/pull/418) [`c950339`](https://github.com/ChestnutLabs/gcode-preview/commit/c95033908e3ebaeb07b6f7e6f2487672dcc463f0) Thanks [@sobechestnut-dev](https://github.com/sobechestnut-dev)! - feat(renderer): interactive view capture() → Blob (DD-030 D1) + + The interactive viewer can now hand back **what is on screen right now** as an image `Blob` — for a + user-selected thumbnail, a large-file thumbnail fallback, or a screenshot. New `capture(opts?)` where + `opts` is `{ width?, height?, format?, quality?, background? }` (all optional; defaults match the live + view). + + Available on every interactive surface: `GcodePreviewControls.capture()` (so the Vue, React, Svelte, and + Web-Component adapters all inherit it — the Web Component also exposes an imperative `capture()` method), + and `ModelViewer.capture()` on the model-viewer handle. The toolpath `ToolpathRenderer` and the shared + `InteractiveStage` carry the implementation. + + **Mechanism (render-to-target).** Capture renders the current scene + active camera into an off-screen + `WebGLRenderTarget` at the requested size and reads it back, rather than flipping the interactive + context's `preserveDrawingBuffer` (which would tax every interactive frame). That gives an arbitrary + output size and an independent/transparent background **without** disturbing the live view, and reuses the + headless still path's "single render, then read pixels" recipe. The thumbnail is framed at its own aspect + so it isn't distorted; the live view is repainted afterward. The library returns the `Blob` and **never** + triggers a download — the caller owns the pixels (same contract as `renderStill`). + + **Honest.** When the renderer cannot render-to-target (the 2D renderer, a stub GL / no WebGL) or the stage + is disposed / its context is lost, `capture()` rejects with a typed `CaptureUnsupportedError` + (`code: 'E_CAPTURE_UNSUPPORTED'`) — never fabricated output. Purely additive (a new optional method on the + renderer contract; no existing signature changed). Final increment of the DD-030 renderer/viewer + interoperability batch (bed + per-plate scope + capture). + +### Patch Changes + +- Updated dependencies [[`5144890`](https://github.com/ChestnutLabs/gcode-preview/commit/51448906f87ca09f3ed11f1286f7b79356110a83), [`bcb4b78`](https://github.com/ChestnutLabs/gcode-preview/commit/bcb4b786fcfc6a2e967d8bfff1d4fd3a06731681), [`dc89056`](https://github.com/ChestnutLabs/gcode-preview/commit/dc890568d45e59635b9994d8409ef6cdc0847f80), [`0ebeadf`](https://github.com/ChestnutLabs/gcode-preview/commit/0ebeadfa839e52baf243ef07b5807f3974bbac7e), [`c950339`](https://github.com/ChestnutLabs/gcode-preview/commit/c95033908e3ebaeb07b6f7e6f2487672dcc463f0)]: + - @chestnutlabs/gcode-parser@0.18.0 + - @chestnutlabs/gcode-renderer-three@0.18.0 + - @chestnutlabs/gcode-renderer-2d@0.18.0 + - @chestnutlabs/toolpath-core@0.18.0 + ## 0.17.0 ### Minor Changes diff --git a/packages/gcode-preview-core/package.json b/packages/gcode-preview-core/package.json index 39272890..79f867b0 100644 --- a/packages/gcode-preview-core/package.json +++ b/packages/gcode-preview-core/package.json @@ -1,6 +1,6 @@ { "name": "@chestnutlabs/gcode-preview-core", - "version": "0.17.0", + "version": "0.18.0", "description": "Framework-neutral preview controller for the Chestnut Labs G-code viewer (DD-007 §4.6): the shared engine glue, state model, and TypeScript contracts beneath the Vue/React/Svelte adapters.", "keywords": [ "gcode", @@ -52,9 +52,9 @@ "test": "vitest run" }, "dependencies": { - "@chestnutlabs/gcode-parser": "0.17.0", - "@chestnutlabs/gcode-renderer-2d": "0.17.0", - "@chestnutlabs/gcode-renderer-three": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-parser": "0.18.0", + "@chestnutlabs/gcode-renderer-2d": "0.18.0", + "@chestnutlabs/gcode-renderer-three": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" } } diff --git a/packages/gcode-preview-element/CHANGELOG.md b/packages/gcode-preview-element/CHANGELOG.md index dccd4982..33ea7409 100644 --- a/packages/gcode-preview-element/CHANGELOG.md +++ b/packages/gcode-preview-element/CHANGELOG.md @@ -1,5 +1,43 @@ # @chestnutlabs/gcode-preview-element +## 0.18.0 + +### Minor Changes + +- [#418](https://github.com/ChestnutLabs/gcode-preview/pull/418) [`c950339`](https://github.com/ChestnutLabs/gcode-preview/commit/c95033908e3ebaeb07b6f7e6f2487672dcc463f0) Thanks [@sobechestnut-dev](https://github.com/sobechestnut-dev)! - feat(renderer): interactive view capture() → Blob (DD-030 D1) + + The interactive viewer can now hand back **what is on screen right now** as an image `Blob` — for a + user-selected thumbnail, a large-file thumbnail fallback, or a screenshot. New `capture(opts?)` where + `opts` is `{ width?, height?, format?, quality?, background? }` (all optional; defaults match the live + view). + + Available on every interactive surface: `GcodePreviewControls.capture()` (so the Vue, React, Svelte, and + Web-Component adapters all inherit it — the Web Component also exposes an imperative `capture()` method), + and `ModelViewer.capture()` on the model-viewer handle. The toolpath `ToolpathRenderer` and the shared + `InteractiveStage` carry the implementation. + + **Mechanism (render-to-target).** Capture renders the current scene + active camera into an off-screen + `WebGLRenderTarget` at the requested size and reads it back, rather than flipping the interactive + context's `preserveDrawingBuffer` (which would tax every interactive frame). That gives an arbitrary + output size and an independent/transparent background **without** disturbing the live view, and reuses the + headless still path's "single render, then read pixels" recipe. The thumbnail is framed at its own aspect + so it isn't distorted; the live view is repainted afterward. The library returns the `Blob` and **never** + triggers a download — the caller owns the pixels (same contract as `renderStill`). + + **Honest.** When the renderer cannot render-to-target (the 2D renderer, a stub GL / no WebGL) or the stage + is disposed / its context is lost, `capture()` rejects with a typed `CaptureUnsupportedError` + (`code: 'E_CAPTURE_UNSUPPORTED'`) — never fabricated output. Purely additive (a new optional method on the + renderer contract; no existing signature changed). Final increment of the DD-030 renderer/viewer + interoperability batch (bed + per-plate scope + capture). + +### Patch Changes + +- Updated dependencies [[`5144890`](https://github.com/ChestnutLabs/gcode-preview/commit/51448906f87ca09f3ed11f1286f7b79356110a83), [`bcb4b78`](https://github.com/ChestnutLabs/gcode-preview/commit/bcb4b786fcfc6a2e967d8bfff1d4fd3a06731681), [`dc89056`](https://github.com/ChestnutLabs/gcode-preview/commit/dc890568d45e59635b9994d8409ef6cdc0847f80), [`0ebeadf`](https://github.com/ChestnutLabs/gcode-preview/commit/0ebeadfa839e52baf243ef07b5807f3974bbac7e), [`c950339`](https://github.com/ChestnutLabs/gcode-preview/commit/c95033908e3ebaeb07b6f7e6f2487672dcc463f0)]: + - @chestnutlabs/gcode-parser@0.18.0 + - @chestnutlabs/gcode-renderer-three@0.18.0 + - @chestnutlabs/gcode-preview-core@0.18.0 + - @chestnutlabs/toolpath-core@0.18.0 + ## 0.17.0 ### Patch Changes diff --git a/packages/gcode-preview-element/package.json b/packages/gcode-preview-element/package.json index cbeb2906..5b1af563 100644 --- a/packages/gcode-preview-element/package.json +++ b/packages/gcode-preview-element/package.json @@ -1,6 +1,6 @@ { "name": "@chestnutlabs/gcode-preview-element", - "version": "0.17.0", + "version": "0.18.0", "description": "Framework-free Web Component for the Chestnut Labs G-code viewer (DD-007 D1 / DD-009 D5): a custom element bridging @chestnutlabs/gcode-preview-core, no framework peer dependency.", "keywords": [ "gcode", @@ -52,9 +52,9 @@ "test": "vitest run" }, "dependencies": { - "@chestnutlabs/gcode-parser": "0.17.0", - "@chestnutlabs/gcode-preview-core": "0.17.0", - "@chestnutlabs/gcode-renderer-three": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-parser": "0.18.0", + "@chestnutlabs/gcode-preview-core": "0.18.0", + "@chestnutlabs/gcode-renderer-three": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" } } diff --git a/packages/gcode-preview-react/CHANGELOG.md b/packages/gcode-preview-react/CHANGELOG.md index b5474797..ad5cc244 100644 --- a/packages/gcode-preview-react/CHANGELOG.md +++ b/packages/gcode-preview-react/CHANGELOG.md @@ -1,5 +1,15 @@ # @chestnutlabs/gcode-preview-react +## 0.18.0 + +### Patch Changes + +- Updated dependencies [[`5144890`](https://github.com/ChestnutLabs/gcode-preview/commit/51448906f87ca09f3ed11f1286f7b79356110a83), [`bcb4b78`](https://github.com/ChestnutLabs/gcode-preview/commit/bcb4b786fcfc6a2e967d8bfff1d4fd3a06731681), [`dc89056`](https://github.com/ChestnutLabs/gcode-preview/commit/dc890568d45e59635b9994d8409ef6cdc0847f80), [`0ebeadf`](https://github.com/ChestnutLabs/gcode-preview/commit/0ebeadfa839e52baf243ef07b5807f3974bbac7e), [`c950339`](https://github.com/ChestnutLabs/gcode-preview/commit/c95033908e3ebaeb07b6f7e6f2487672dcc463f0)]: + - @chestnutlabs/gcode-parser@0.18.0 + - @chestnutlabs/gcode-renderer-three@0.18.0 + - @chestnutlabs/gcode-preview-core@0.18.0 + - @chestnutlabs/toolpath-core@0.18.0 + ## 0.17.0 ### Patch Changes diff --git a/packages/gcode-preview-react/package.json b/packages/gcode-preview-react/package.json index cdb75633..e94aa10d 100644 --- a/packages/gcode-preview-react/package.json +++ b/packages/gcode-preview-react/package.json @@ -1,6 +1,6 @@ { "name": "@chestnutlabs/gcode-preview-react", - "version": "0.17.0", + "version": "0.18.0", "description": "Thin React integration for the Chestnut Labs G-code viewer (DD-007 D1 amendment): useGcodePreview hook + GcodePreview component as a reactivity bridge over @chestnutlabs/gcode-preview-core.", "keywords": [ "gcode", @@ -49,10 +49,10 @@ "test": "vitest run" }, "dependencies": { - "@chestnutlabs/gcode-parser": "0.17.0", - "@chestnutlabs/gcode-preview-core": "0.17.0", - "@chestnutlabs/gcode-renderer-three": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-parser": "0.18.0", + "@chestnutlabs/gcode-preview-core": "0.18.0", + "@chestnutlabs/gcode-renderer-three": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" diff --git a/packages/gcode-preview-svelte/CHANGELOG.md b/packages/gcode-preview-svelte/CHANGELOG.md index 91539685..100b9a6c 100644 --- a/packages/gcode-preview-svelte/CHANGELOG.md +++ b/packages/gcode-preview-svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # @chestnutlabs/gcode-preview-svelte +## 0.18.0 + +### Patch Changes + +- Updated dependencies [[`5144890`](https://github.com/ChestnutLabs/gcode-preview/commit/51448906f87ca09f3ed11f1286f7b79356110a83), [`bcb4b78`](https://github.com/ChestnutLabs/gcode-preview/commit/bcb4b786fcfc6a2e967d8bfff1d4fd3a06731681), [`dc89056`](https://github.com/ChestnutLabs/gcode-preview/commit/dc890568d45e59635b9994d8409ef6cdc0847f80), [`0ebeadf`](https://github.com/ChestnutLabs/gcode-preview/commit/0ebeadfa839e52baf243ef07b5807f3974bbac7e), [`c950339`](https://github.com/ChestnutLabs/gcode-preview/commit/c95033908e3ebaeb07b6f7e6f2487672dcc463f0)]: + - @chestnutlabs/gcode-parser@0.18.0 + - @chestnutlabs/gcode-renderer-three@0.18.0 + - @chestnutlabs/gcode-preview-core@0.18.0 + - @chestnutlabs/toolpath-core@0.18.0 + ## 0.17.0 ### Patch Changes diff --git a/packages/gcode-preview-svelte/package.json b/packages/gcode-preview-svelte/package.json index e06d6614..e7e45b18 100644 --- a/packages/gcode-preview-svelte/package.json +++ b/packages/gcode-preview-svelte/package.json @@ -1,6 +1,6 @@ { "name": "@chestnutlabs/gcode-preview-svelte", - "version": "0.17.0", + "version": "0.18.0", "description": "Thin Svelte integration for the Chestnut Labs G-code viewer (DD-007 D1 amendment): createGcodePreview store/action API + GcodePreview component as a reactivity bridge over @chestnutlabs/gcode-preview-core.", "keywords": [ "gcode", @@ -55,10 +55,10 @@ "test": "vitest run" }, "dependencies": { - "@chestnutlabs/gcode-parser": "0.17.0", - "@chestnutlabs/gcode-preview-core": "0.17.0", - "@chestnutlabs/gcode-renderer-three": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-parser": "0.18.0", + "@chestnutlabs/gcode-preview-core": "0.18.0", + "@chestnutlabs/gcode-renderer-three": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0" diff --git a/packages/gcode-preview-vue/CHANGELOG.md b/packages/gcode-preview-vue/CHANGELOG.md index 7cdade39..e7dfa91c 100644 --- a/packages/gcode-preview-vue/CHANGELOG.md +++ b/packages/gcode-preview-vue/CHANGELOG.md @@ -1,5 +1,15 @@ # @chestnutlabs/gcode-preview-vue +## 0.18.0 + +### Patch Changes + +- Updated dependencies [[`5144890`](https://github.com/ChestnutLabs/gcode-preview/commit/51448906f87ca09f3ed11f1286f7b79356110a83), [`bcb4b78`](https://github.com/ChestnutLabs/gcode-preview/commit/bcb4b786fcfc6a2e967d8bfff1d4fd3a06731681), [`dc89056`](https://github.com/ChestnutLabs/gcode-preview/commit/dc890568d45e59635b9994d8409ef6cdc0847f80), [`0ebeadf`](https://github.com/ChestnutLabs/gcode-preview/commit/0ebeadfa839e52baf243ef07b5807f3974bbac7e), [`c950339`](https://github.com/ChestnutLabs/gcode-preview/commit/c95033908e3ebaeb07b6f7e6f2487672dcc463f0)]: + - @chestnutlabs/gcode-parser@0.18.0 + - @chestnutlabs/gcode-renderer-three@0.18.0 + - @chestnutlabs/gcode-preview-core@0.18.0 + - @chestnutlabs/toolpath-core@0.18.0 + ## 0.17.0 ### Patch Changes diff --git a/packages/gcode-preview-vue/package.json b/packages/gcode-preview-vue/package.json index c33305fb..fe5475a4 100644 --- a/packages/gcode-preview-vue/package.json +++ b/packages/gcode-preview-vue/package.json @@ -1,6 +1,6 @@ { "name": "@chestnutlabs/gcode-preview-vue", - "version": "0.17.0", + "version": "0.18.0", "description": "Thin Vue 3 integration for the Chestnut Labs G-code viewer (DD-007): useGcodePreview composable + GcodePreview component over the framework-neutral parser/renderer/progress packages.", "keywords": [ "gcode", @@ -50,10 +50,10 @@ "test": "vitest run" }, "dependencies": { - "@chestnutlabs/gcode-parser": "0.17.0", - "@chestnutlabs/gcode-renderer-three": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0", - "@chestnutlabs/gcode-preview-core": "0.17.0" + "@chestnutlabs/gcode-parser": "0.18.0", + "@chestnutlabs/gcode-renderer-three": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0", + "@chestnutlabs/gcode-preview-core": "0.18.0" }, "peerDependencies": { "vue": "^3.4.0" diff --git a/packages/gcode-renderer-2d/CHANGELOG.md b/packages/gcode-renderer-2d/CHANGELOG.md index 007daab3..afd44607 100644 --- a/packages/gcode-renderer-2d/CHANGELOG.md +++ b/packages/gcode-renderer-2d/CHANGELOG.md @@ -1,5 +1,13 @@ # @chestnutlabs/gcode-renderer-2d +## 0.18.0 + +### Patch Changes + +- Updated dependencies []: + - @chestnutlabs/gcode-colors@0.18.0 + - @chestnutlabs/toolpath-core@0.18.0 + ## 0.17.0 ### Patch Changes diff --git a/packages/gcode-renderer-2d/package.json b/packages/gcode-renderer-2d/package.json index 1479d4f8..aff7863b 100644 --- a/packages/gcode-renderer-2d/package.json +++ b/packages/gcode-renderer-2d/package.json @@ -1,6 +1,6 @@ { "name": "@chestnutlabs/gcode-renderer-2d", - "version": "0.17.0", + "version": "0.18.0", "description": "Low-resource Canvas 2D layer renderer for the Chestnut Labs G-code viewer (DD-014 / E8): an opt-in current/adjacent-layer 2D view over the existing ToolpathIR for low-GPU/low-memory/WebGL-blocked devices. No three, no framework.", "keywords": [ "gcode", @@ -49,7 +49,7 @@ "test": "vitest run" }, "dependencies": { - "@chestnutlabs/gcode-colors": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-colors": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" } } diff --git a/packages/gcode-renderer-three/CHANGELOG.md b/packages/gcode-renderer-three/CHANGELOG.md index c9bc7c8c..8d4c1d38 100644 --- a/packages/gcode-renderer-three/CHANGELOG.md +++ b/packages/gcode-renderer-three/CHANGELOG.md @@ -1,5 +1,61 @@ # @chestnutlabs/gcode-renderer-three +## 0.18.0 + +### Minor Changes + +- [#416](https://github.com/ChestnutLabs/gcode-preview/pull/416) [`0ebeadf`](https://github.com/ChestnutLabs/gcode-preview/commit/0ebeadfa839e52baf243ef07b5807f3974bbac7e) Thanks [@sobechestnut-dev](https://github.com/sobechestnut-dev)! - feat(renderer): non-rectangular build-bed geometry (DD-030 D3) + + The build volume can now draw an **honest non-rectangular bed** — delta, round, or irregular — instead of + only a rectangle. `BuildVolumeDef` gains an optional `shape?: BedShape` + (`{kind:'rect'} | {kind:'circular', center, diameter} | {kind:'polygon', points}`): a circle is + polygonized, the outline is filled and drawn, and the floor grid is **clipped to the outline** so the + printable area reads correctly (a round bed is no longer a square with a circle floating over it). The + `mesh` escape hatch for a fully custom bed mesh is reserved for a later phase. + + `machineToVolume()` now maps a discovered `MachineGeometry.bed` of kind `circular`/`polygon` onto that + `shape`, so a **discovered** round/delta bed renders as its true outline instead of being collapsed to its + bounding rectangle (a visible improvement for delta/round printers). Callers supply the shape (from a + machine profile, a config, wherever); the renderer just draws the polygon — no profile parsing in the + library, no vendor semantics baked in. + + Additive and safe: a bed with no `shape` (or `{kind:'rect'}`) takes the original rectangular path and is + **byte-identical** — the rectangular grid, plate, cage, excluded-region outlines, and origin tripod are + unchanged. The volume cage stays a bounding box (the bed outline carries the shape). First increment of + the DD-030 renderer/viewer interoperability batch. + +- [#418](https://github.com/ChestnutLabs/gcode-preview/pull/418) [`c950339`](https://github.com/ChestnutLabs/gcode-preview/commit/c95033908e3ebaeb07b6f7e6f2487672dcc463f0) Thanks [@sobechestnut-dev](https://github.com/sobechestnut-dev)! - feat(renderer): interactive view capture() → Blob (DD-030 D1) + + The interactive viewer can now hand back **what is on screen right now** as an image `Blob` — for a + user-selected thumbnail, a large-file thumbnail fallback, or a screenshot. New `capture(opts?)` where + `opts` is `{ width?, height?, format?, quality?, background? }` (all optional; defaults match the live + view). + + Available on every interactive surface: `GcodePreviewControls.capture()` (so the Vue, React, Svelte, and + Web-Component adapters all inherit it — the Web Component also exposes an imperative `capture()` method), + and `ModelViewer.capture()` on the model-viewer handle. The toolpath `ToolpathRenderer` and the shared + `InteractiveStage` carry the implementation. + + **Mechanism (render-to-target).** Capture renders the current scene + active camera into an off-screen + `WebGLRenderTarget` at the requested size and reads it back, rather than flipping the interactive + context's `preserveDrawingBuffer` (which would tax every interactive frame). That gives an arbitrary + output size and an independent/transparent background **without** disturbing the live view, and reuses the + headless still path's "single render, then read pixels" recipe. The thumbnail is framed at its own aspect + so it isn't distorted; the live view is repainted afterward. The library returns the `Blob` and **never** + triggers a download — the caller owns the pixels (same contract as `renderStill`). + + **Honest.** When the renderer cannot render-to-target (the 2D renderer, a stub GL / no WebGL) or the stage + is disposed / its context is lost, `capture()` rejects with a typed `CaptureUnsupportedError` + (`code: 'E_CAPTURE_UNSUPPORTED'`) — never fabricated output. Purely additive (a new optional method on the + renderer contract; no existing signature changed). Final increment of the DD-030 renderer/viewer + interoperability batch (bed + per-plate scope + capture). + +### Patch Changes + +- Updated dependencies []: + - @chestnutlabs/gcode-colors@0.18.0 + - @chestnutlabs/toolpath-core@0.18.0 + ## 0.17.0 ### Minor Changes diff --git a/packages/gcode-renderer-three/package.json b/packages/gcode-renderer-three/package.json index 9b734a87..90b6a740 100644 --- a/packages/gcode-renderer-three/package.json +++ b/packages/gcode-renderer-three/package.json @@ -1,6 +1,6 @@ { "name": "@chestnutlabs/gcode-renderer-three", - "version": "0.17.0", + "version": "0.18.0", "description": "Three.js toolpath renderer consuming ToolpathIR (DD-004). Phases 1-2: geometry builders + scene/lifecycle.", "keywords": [ "gcode", @@ -49,8 +49,8 @@ "test": "vitest run" }, "dependencies": { - "@chestnutlabs/gcode-colors": "0.17.0", - "@chestnutlabs/toolpath-core": "0.17.0" + "@chestnutlabs/gcode-colors": "0.18.0", + "@chestnutlabs/toolpath-core": "0.18.0" }, "devDependencies": { "@types/three": "0.178.0" diff --git a/packages/toolpath-core/CHANGELOG.md b/packages/toolpath-core/CHANGELOG.md index 7470e41c..ebc482a1 100644 --- a/packages/toolpath-core/CHANGELOG.md +++ b/packages/toolpath-core/CHANGELOG.md @@ -1,5 +1,7 @@ # @chestnutlabs/toolpath-core +## 0.18.0 + ## 0.17.0 ### Minor Changes diff --git a/packages/toolpath-core/package.json b/packages/toolpath-core/package.json index 66011c5d..7e8414ee 100644 --- a/packages/toolpath-core/package.json +++ b/packages/toolpath-core/package.json @@ -1,6 +1,6 @@ { "name": "@chestnutlabs/toolpath-core", - "version": "0.17.0", + "version": "0.18.0", "description": "Neutral ToolpathIR and capability model for the Chestnut Labs G-code Preview toolpath stack.", "keywords": [ "gcode",