three-slicer-viewer 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +44 -0
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/THIRD-PARTY-NOTICES.md +19 -0
- package/TOOLPATH_SPEC.md +173 -0
- package/VIEWER.md +423 -0
- package/data/config-schema-lean.json +1 -0
- package/data/preset-keys.json +1 -0
- package/data/ui-tree-keys.json +1 -0
- package/dist/components.js +206 -0
- package/dist/gcode_parse.js +190 -0
- package/dist/index.js +6416 -0
- package/dist/make_worker.js +26 -0
- package/dist/model_loaders.js +183 -0
- package/dist/parse_3mf-CQ5rAX6P.js +299 -0
- package/dist/parse_3mf.worker.js +10 -0
- package/dist/sl1_encode.worker.js +10 -0
- package/dist/sl1_write-CvrNejbW.js +302 -0
- package/dist/sla_reconstruct-CXkYhux2.js +176 -0
- package/dist/sla_reconstruct.worker.js +24 -0
- package/dist/sla_slice.worker.js +73 -0
- package/dist/toolpath.js +408 -0
- package/package.json +83 -0
- package/src/data.js +2 -0
- package/src/settings/data.js +6 -0
- package/src/settings/index.js +2 -0
- package/src/settings/kernel_setting_keys.js +132 -0
- package/src/settings/preset_file.js +99 -0
- package/src/settings/settings_core.js +638 -0
- package/src/settings/toggle_eval.js +90 -0
- package/types/components.d.ts +39 -0
- package/types/data.d.ts +3 -0
- package/types/gcode.d.ts +29 -0
- package/types/index.d.ts +7 -0
- package/types/loaders.d.ts +33 -0
- package/types/settings-keys.d.ts +1855 -0
- package/types/settings.d.ts +173 -0
- package/types/toggle.d.ts +30 -0
- package/types/toolpath.d.ts +138 -0
- package/types/viewer.d.ts +256 -0
package/COMPONENTS.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# three-slicer/components
|
|
2
|
+
|
|
3
|
+
Reusable React components for the browser slicer. Props-driven — no global state, no React context, no router coupling (proven standalone by the tarball consumer gate `packages/pack_check.sh`).
|
|
4
|
+
|
|
5
|
+
## `<SettingsPanel/>`
|
|
6
|
+
|
|
7
|
+
An OrcaSlicer-style settings form generated from `three-slicer/data` (schema + UI tree + toggle rules): tab/page/group navigation, mode filter (all/simple/advanced/expert/develop), search across 976 options, dirty markers with per-option reset, and enable/disable rules evaluation.
|
|
8
|
+
|
|
9
|
+
The tab set follows the printer profile's technology: a settings map whose `printer_technology` says SLA swaps the FFF tabs for the SLA ones (print/material/printer builders carry the technology in their upstream builder name, which is what the filter reads). No prop needed — change the profile in the shared settings map and the panel follows.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i three-slicer react react-dom # react is an OPTIONAL peer — npm does not install it for you
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```jsx
|
|
16
|
+
import { useState } from 'react'
|
|
17
|
+
import SettingsPanel from 'three-slicer/components'
|
|
18
|
+
|
|
19
|
+
function App() {
|
|
20
|
+
const [settings, setSettings] = useState({}) // sparse map: edited keys only; missing = schema default
|
|
21
|
+
return <SettingsPanel settings={settings} setSettings={setSettings} />
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Props:
|
|
26
|
+
|
|
27
|
+
| Prop | Type | Description |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `settings` | `{ [schemaKey]: value }` | Sparse settings map — the consumer's state is the single source of truth |
|
|
30
|
+
| `setSettings` | `(updater) => void` | React setState-style updater; the only way state leaves the component |
|
|
31
|
+
| `onOptionOpen` | `(key) => void` (optional) | Label click hook for deep links/detail views. Omit → plain labels |
|
|
32
|
+
| `embedded` | `boolean` (optional) | Compact layout for embedding inside another panel |
|
|
33
|
+
| `customWidgets` | `{ [schemaKey]: Component }` (optional) | Replace schema-driven widgets per key (e.g. `{ printable_area: MyBedEditor }`) |
|
|
34
|
+
| `only` | `{ builder, page? }` (optional) | Pins the panel to one builder/page and drops the search/page/mode chrome — how `<Viewport/>`'s `motionPanel` slot is meant to be filled |
|
|
35
|
+
|
|
36
|
+
The same `settings` object feeds `deriveKernelParams()` in `three-slicer` and the `<Viewport/>` in `three-slicer/viewer` — one state connects all three.
|
|
37
|
+
|
|
38
|
+
Ships transpiled ESM (`dist/`); react is an optional peer dependency. The panel arrives fully styled: its stylesheet is inlined into the bundle and injected into a Shadow DOM root, so host CSS neither styles it nor collides with it.
|
|
39
|
+
|
|
40
|
+
The 3D viewport lives in its own package: `three-slicer/viewer`.
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
|
|
44
|
+
AGPL-3.0-or-later (derived from OrcaSlicer).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kimgh06
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# three-slicer-viewer
|
|
2
|
+
|
|
3
|
+
The 3D-printing viewer without the slicer: model loading (STL/OBJ/3MF/AMF/PLY), G-code parsing, GPU toolpath
|
|
4
|
+
rendering, the settings transforms, and the React `<Viewport/>` itself. **MIT licensed** — usable in closed-source
|
|
5
|
+
commercial products.
|
|
6
|
+
|
|
7
|
+
This is [`three-slicer`](https://www.npmjs.com/package/three-slicer) with the kernel taken out. It displays and
|
|
8
|
+
exports; it does not slice. Slicing is plugged in through two props — `slicer` (a hook driving a kernel) and
|
|
9
|
+
`catalog` (vendor presets) — which `three-slicer/viewer` supplies. Install `three-slicer` for that, and note that
|
|
10
|
+
doing so puts your application under AGPL.
|
|
11
|
+
|
|
12
|
+
## The viewer
|
|
13
|
+
|
|
14
|
+
```jsx
|
|
15
|
+
import Viewport from 'three-slicer-viewer'
|
|
16
|
+
|
|
17
|
+
// A display-only viewer: opens models and G-code, no kernel, no presets. Pass `slicer` and `catalog` to add them.
|
|
18
|
+
<Viewport settings={settings} setSettings={setSettings} gcode={gcodeText} />
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Slicing, if you bring a kernel
|
|
22
|
+
|
|
23
|
+
```jsx
|
|
24
|
+
import Viewport, { useSlicer } from 'three-slicer-viewer'
|
|
25
|
+
import { makeSlicerWorker } from 'three-slicer/client' // AGPL — the WASM kernel's worker; this is the step that changes your licence
|
|
26
|
+
import { bundledCatalog } from 'three-slicer/settings' // AGPL — OrcaSlicer's vendor presets
|
|
27
|
+
|
|
28
|
+
const slicer = (deps) => useSlicer({ ...deps, makeWorker: makeSlicerWorker }) // module-level: it is a hook
|
|
29
|
+
<Viewport slicer={slicer} catalog={bundledCatalog} … />
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`useSlicer` is this package's; it only needs a worker that speaks its protocol. `three-slicer` ships one.
|
|
33
|
+
|
|
34
|
+
## The settings form
|
|
35
|
+
|
|
36
|
+
```jsx
|
|
37
|
+
import SettingsPanel from 'three-slicer-viewer/components'
|
|
38
|
+
|
|
39
|
+
// Bare: fields labelled by key, nothing disabled. Pass `schema`, `uiTree` and `toggle` for labels, tabs and
|
|
40
|
+
// enable/disable rules — three-slicer/components does that with OrcaSlicer's data; a host may pass its own.
|
|
41
|
+
<SettingsPanel settings={settings} setSettings={setSettings} />
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Rendering without React
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
import { parseGcode } from 'three-slicer-viewer/gcode'
|
|
48
|
+
import { buildSegmentData, makeToolpath, computeColors } from 'three-slicer-viewer/toolpath'
|
|
49
|
+
import * as THREE from 'three'
|
|
50
|
+
|
|
51
|
+
const { layers, stats } = parseGcode(gcodeText) // any slicer's output
|
|
52
|
+
const data = buildSegmentData(layers, 0.4) // -> GPU attribute buffers
|
|
53
|
+
const handle = makeToolpath(THREE, data) // -> { mesh, travLines, ... }
|
|
54
|
+
scene.add(handle.mesh, handle.travLines)
|
|
55
|
+
|
|
56
|
+
handle.setLayerRange(0, 90) // cut the print open
|
|
57
|
+
handle.setColors(computeColors(data, 'speed', ctx).color) // recolour without a rebuild
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- Reads G-code from OrcaSlicer, PrusaSlicer, Cura and this kernel — roles, widths, tool changes, arcs.
|
|
61
|
+
- Draws one instanced call for the whole plate, with per-role, per-tool and heatmap colouring.
|
|
62
|
+
- `three`, `react` and `react-dom` are **optional peers**. The main entry is the React viewer; `/toolpath` and
|
|
63
|
+
`/gcode` import neither React nor three (`makeToolpath` takes the THREE namespace as an argument), so a
|
|
64
|
+
headless consumer pulls in no renderer at all.
|
|
65
|
+
|
|
66
|
+
## Licensing
|
|
67
|
+
|
|
68
|
+
MIT. It depends on nothing AGPL, which a check in this repository enforces rather than documents
|
|
69
|
+
(`packages/viewer/test_license_boundary.mjs`).
|
|
70
|
+
|
|
71
|
+
The implementation was written from [`TOOLPATH_SPEC.md`](./TOOLPATH_SPEC.md), a functional contract, rather
|
|
72
|
+
than derived from any upstream slicer. The reasoning and evidence are in
|
|
73
|
+
[`../packages/PROVENANCE.md`](../packages/PROVENANCE.md); the plan it came from is
|
|
74
|
+
[`../packages/RELICENSE.md`](../packages/RELICENSE.md).
|
|
75
|
+
|
|
76
|
+
Published as a locked pair with `three-slicer`: both carry the same version, and `three-slicer` pins this
|
|
77
|
+
package exactly, so a combination that was never built together cannot be resolved.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
`three-slicer-viewer` is licensed MIT (`LICENSE`). It contains no code from OrcaSlicer, PrusaSlicer or any other
|
|
4
|
+
slicer — the provenance audit behind that statement is `../packages/PROVENANCE.md`.
|
|
5
|
+
|
|
6
|
+
## Peer dependencies (not bundled or redistributed)
|
|
7
|
+
|
|
8
|
+
| Package | License | Used for |
|
|
9
|
+
| --- | --- | --- |
|
|
10
|
+
| three | MIT | the scene, and `three/examples/jsm/libs/fflate.module.js` for zip/unzip in the 3mf and SL1 codecs |
|
|
11
|
+
| react, react-dom | MIT | `<Viewport/>` and `<SettingsPanel/>`; the `/toolpath` and `/gcode` entries need neither |
|
|
12
|
+
|
|
13
|
+
All three are optional peers: a consumer that only parses G-code or builds toolpath geometry installs none of them.
|
|
14
|
+
|
|
15
|
+
## Data
|
|
16
|
+
|
|
17
|
+
`data/config-schema-lean.json`, `data/ui-tree-keys.json` and `data/preset-keys.json` are option-key lists, types,
|
|
18
|
+
defaults and layout flags extracted from OrcaSlicer's configuration — facts about an option set, carrying none of
|
|
19
|
+
its authored text (labels, tooltips) and none of its vendor preset values. Those stay with `three-slicer`.
|
package/TOOLPATH_SPEC.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Toolpath renderer — functional specification
|
|
2
|
+
|
|
3
|
+
What the toolpath renderer must do, stated as a contract. Written so the four files listed in
|
|
4
|
+
[../PROVENANCE.md](../PROVENANCE.md) §2 can be reimplemented **without reference to the existing code or to
|
|
5
|
+
any upstream slicer** — see [../RELICENSE.md](../RELICENSE.md) for why that matters.
|
|
6
|
+
|
|
7
|
+
Everything here was derived from the published types (`types/viewer-toolpath.d.ts`), the consumers, and a
|
|
8
|
+
recorded run of the present implementation (`test_toolpath_contract.mjs`). No upstream source was consulted
|
|
9
|
+
in writing it, and none is needed to satisfy it.
|
|
10
|
+
|
|
11
|
+
## 1. The job
|
|
12
|
+
|
|
13
|
+
A slice result is a list of layers. Each layer holds a flat array of straight extrusion segments in
|
|
14
|
+
millimetres. The renderer turns that into something a GPU can draw as solid beads of plastic — one instanced
|
|
15
|
+
draw call for the whole plate — plus the queries the UI needs about it (per-layer counts, colour ranges,
|
|
16
|
+
material shares).
|
|
17
|
+
|
|
18
|
+
It draws two things: **extrusions** (solid, oriented, coloured) and **travels** (thin lines, hidden by
|
|
19
|
+
default).
|
|
20
|
+
|
|
21
|
+
## 2. Input
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
buildSegmentData(layers, defaultLineWidth) -> SegmentData
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`layers[i] = { z, paths, widths }`
|
|
28
|
+
|
|
29
|
+
- `paths` — a Float32Array, **stride 8**, one segment per 8 floats:
|
|
30
|
+
`[x0, y0, z0, enc, x1, y1, z1, enc]`, coordinates in mm.
|
|
31
|
+
- `enc` — `role + tool * 16`. Read the role with `& 15` and the tool with `>>> 4`. **This encoding is this
|
|
32
|
+
repository's own** (the segment stream is the largest array the viewer holds, and a ninth float per vertex
|
|
33
|
+
would cost 12.5% of it for one small integer). Both endpoints of a segment carry the same `enc`.
|
|
34
|
+
- `widths` — one entry per segment, in mm. A non-positive entry means "unknown": fall back to
|
|
35
|
+
`defaultLineWidth`.
|
|
36
|
+
- `role 0` is a **travel** move: no plastic, drawn as a line, excluded from every extrusion count and from
|
|
37
|
+
length accounting.
|
|
38
|
+
|
|
39
|
+
Roles 1–11 are: 1 wall, 2 sparse infill, 3 solid infill, 4 skirt/brim, 5 support, 6 raft, 7 gap fill,
|
|
40
|
+
8 thin wall, 9 bridge, 10 ironing, 11 prime tower.
|
|
41
|
+
|
|
42
|
+
Layer height is not in the input. Derive it as the gap to the previous layer's `z`, falling back to a
|
|
43
|
+
sensible default for the first layer.
|
|
44
|
+
|
|
45
|
+
## 3. Output — `SegmentData`
|
|
46
|
+
|
|
47
|
+
Field names and types are fixed by `types/viewer-toolpath.d.ts` and by the two consumers that are **not**
|
|
48
|
+
being rewritten (`core/toolpath_views.js`, `use_move_scrub.js`). Everything else is an implementation choice.
|
|
49
|
+
|
|
50
|
+
| Field | Shape | Meaning |
|
|
51
|
+
| --- | --- | --- |
|
|
52
|
+
| `nSeg` | number | extrusion segments (travels excluded) |
|
|
53
|
+
| `nTrav` | number | travel segments |
|
|
54
|
+
| `nV` | number | vertices in the extrusion stream |
|
|
55
|
+
| `position` | Float32Array | per-vertex geometry input |
|
|
56
|
+
| `hwa` | Float32Array | per-vertex height / width / orientation |
|
|
57
|
+
| `segIndex` | Uint32Array | per-vertex segment identity |
|
|
58
|
+
| `layerSegPrefix` | Uint32Array, length `layerCount + 1` | non-decreasing; `[0] === 0`, `[layerCount] === nSeg` |
|
|
59
|
+
| `travelPos`, `travelPrefix`, `nTrav` | — | the same, for travels |
|
|
60
|
+
| `layerCount` | number | input layer count |
|
|
61
|
+
| `maxAbs` | number | bounds the coordinate magnitude |
|
|
62
|
+
| `hasNaN` | boolean | true if any non-finite value reached the stream |
|
|
63
|
+
| `bbox` | `{min:[x,y,z], max:[x,y,z]}` or `null` | null when there is not a single vertex |
|
|
64
|
+
| `typeLengths` | Float64Array(16) | total extruded length per role, mm |
|
|
65
|
+
| `meta` | see below | per-vertex channels |
|
|
66
|
+
|
|
67
|
+
`meta` — every array has length `nV`, and a shader reads past its attribute if any disagrees:
|
|
68
|
+
|
|
69
|
+
| Channel | Type | Contents |
|
|
70
|
+
| --- | --- | --- |
|
|
71
|
+
| `vType` | Uint8Array | the **decoded** role (`enc & 15`), never raw `enc` |
|
|
72
|
+
| `vTool` | Uint8Array | the decoded tool (`enc >>> 4`); untooled input decodes to 0 |
|
|
73
|
+
| `vWidth` | Float32Array | bead width, mm, always > 0 |
|
|
74
|
+
| `vHeight` | Float32Array | bead height, mm, always > 0 |
|
|
75
|
+
| `vLayer` | Int32Array | layer index, `0 .. layerCount-1` |
|
|
76
|
+
|
|
77
|
+
The present implementation uses two vertices per segment with `position` and `hwa` as vec4 attributes and
|
|
78
|
+
`segIndex` as 2 entries per vertex. **A reimplementation may choose differently**, provided `meta`, `nV` and
|
|
79
|
+
the fields above stay consistent with each other, because `computeColors` allocates `nV * 4` colours and
|
|
80
|
+
`makeToolpath` consumes what `buildSegmentData` produced.
|
|
81
|
+
|
|
82
|
+
Degenerate input must be survived, not crashed: no layers gives zero counts and `bbox === null`; a
|
|
83
|
+
zero-length segment must not produce NaN.
|
|
84
|
+
|
|
85
|
+
## 4. Geometry
|
|
86
|
+
|
|
87
|
+
Each extrusion segment is drawn as a solid bead: a cross-section swept along the segment, oriented to the
|
|
88
|
+
direction of travel, sized by that segment's width and height. One instanced draw call covers the plate.
|
|
89
|
+
|
|
90
|
+
Requirements:
|
|
91
|
+
|
|
92
|
+
- The bead is **centred on the extrusion path in z** — the toolpath's z is the nozzle position, and plastic
|
|
93
|
+
sits below it. The recorded run shows a layer at `z = 0.2` producing a bbox z of `0.1 .. 0.3` for a
|
|
94
|
+
0.2 mm layer: the bead spans half a layer height either side.
|
|
95
|
+
- Orientation follows the segment direction in the XY plane.
|
|
96
|
+
- Width and height come from `meta`, per vertex, so two segments in one draw call can differ.
|
|
97
|
+
|
|
98
|
+
`makeToolpath(THREE, data) -> ToolpathHandle` builds the scene objects. The `THREE` namespace is **passed
|
|
99
|
+
in, never imported** — that is what guarantees the consumer's own three.js instance is used, and it is why
|
|
100
|
+
this module has no dependency of its own.
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
mesh the extrusions
|
|
104
|
+
travLines the travels (THREE.LineSegments)
|
|
105
|
+
setLayerRange(lo, hi) show only layers lo..hi inclusive
|
|
106
|
+
setVisibleLayers(n) === setLayerRange(0, n - 1)
|
|
107
|
+
setTravelVisible(bool) travels start hidden
|
|
108
|
+
setColors(Float32Array) swap the per-vertex colours in place, without rebuilding geometry
|
|
109
|
+
dispose() release GPU resources
|
|
110
|
+
nSeg, layerCount mirrored from the data
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`setColors` must not rebuild the mesh: changing the view type is interactive and happens on every dropdown
|
|
114
|
+
change.
|
|
115
|
+
|
|
116
|
+
## 5. Colour
|
|
117
|
+
|
|
118
|
+
`computeColors` (in `core/toolpath_views.js`, **not** part of the rewrite) produces a `Float32Array(nV * 4)`
|
|
119
|
+
with the colour packed into the `.r` component of each vertex. The rewrite must supply the primitives it
|
|
120
|
+
uses:
|
|
121
|
+
|
|
122
|
+
| Export | Contract |
|
|
123
|
+
| --- | --- |
|
|
124
|
+
| `packColor([r,g,b])` | components 0..1 -> `r<<16 \| g<<8 \| b`; exact in float32 below 2^24, and the inverse of `hexToRgb` for any 6-digit hex |
|
|
125
|
+
| `hexToRgb('#rrggbb')` | -> `[r,g,b]` in 0..1 |
|
|
126
|
+
| `TYPE_COLOR` | role -> `[r,g,b]`; must cover every role in `TYPE_LABEL` |
|
|
127
|
+
| `TYPE_LABEL` | role -> display name (§2's list) |
|
|
128
|
+
| `TOOL_COLOR` | categorical per-extruder colours; non-empty, indexed **modulo its length** — a machine may have more extruders than entries |
|
|
129
|
+
| `DEFAULT_RANGES_COLORS` | an 11-stop heatmap for continuous views |
|
|
130
|
+
| `rangeColorAt(v, lo, hi, palette)` | value -> interpolated colour between adjacent stops |
|
|
131
|
+
|
|
132
|
+
**`rangeColorAt` must clamp both ends.** The present implementation clamps the high end and *extrapolates*
|
|
133
|
+
below the low end, returning negative components — out of gamut. It is latent today because `computeColors`
|
|
134
|
+
derives `lo`/`hi` from the data, so no value is ever below `lo`; `test_toolpath_contract.mjs` records it and
|
|
135
|
+
will report it as fixed once the rewrite lands.
|
|
136
|
+
|
|
137
|
+
The colour values themselves are a free choice. Pick a palette that reads well; there is no external
|
|
138
|
+
consumer that depends on a specific hue, only on the shape of these APIs.
|
|
139
|
+
|
|
140
|
+
## 6. Queries
|
|
141
|
+
|
|
142
|
+
`roleRatios(typeLengths)` — length share per role, as
|
|
143
|
+
`[{ type, label, pct, color }]`, **descending by `pct`**, summing to 100, one entry per role with non-zero
|
|
144
|
+
length. The kernel exposes no per-role time, so length stands in for it.
|
|
145
|
+
|
|
146
|
+
The move-scrub queries, consumed by `use_move_scrub.js` and pinned by `test_move_scrub.mjs`:
|
|
147
|
+
|
|
148
|
+
- `layerMoveCount(data, layer)` — moves in one layer
|
|
149
|
+
- `topMoveLayer(data, lo, hi)` — the topmost visible layer in a range
|
|
150
|
+
- `moveCursor(data, layer, at)` — where the nozzle is, `at` moves into `layer`, plus how much of each draw
|
|
151
|
+
list that accounts for
|
|
152
|
+
|
|
153
|
+
## 7. Shaders
|
|
154
|
+
|
|
155
|
+
A vertex and a fragment shader, GLSL ES 3.0, used through a raw shader material. The vertex shader expands
|
|
156
|
+
each instance into the oriented bead from the per-vertex attributes; the fragment shader shades it.
|
|
157
|
+
|
|
158
|
+
Constraints:
|
|
159
|
+
|
|
160
|
+
- Attribute layout is the rewrite's own choice, provided the shaders and `makeToolpath` agree.
|
|
161
|
+
- Per-vertex colour arrives packed in one float (see §5) and is unpacked in the shader.
|
|
162
|
+
- Layer-range visibility must be a **uniform**, not a geometry rebuild — `setLayerRange` is dragged.
|
|
163
|
+
|
|
164
|
+
## 8. How to know it works
|
|
165
|
+
|
|
166
|
+
1. `node packages-mit/test_toolpath_contract.mjs` — the contract, plus a printed snapshot to diff against
|
|
167
|
+
the recorded run. Exact float equality is **not** expected; counts, strides, encodings, ranges and length
|
|
168
|
+
accounting are.
|
|
169
|
+
2. `node packages-mit/test_move_scrub.mjs` — the scrub queries.
|
|
170
|
+
3. `npm run test:viewer` — everything, including the license boundary check.
|
|
171
|
+
4. **Look at it.** Build the three demos that consume this API
|
|
172
|
+
(`examples/{instant-quote,cad-embed,farm-dashboard}`) and open them. A static check cannot tell you a
|
|
173
|
+
shader is wrong; a bead facing the wrong way, z-fighting, or a black plate will only show on screen.
|