@gridspace/raster-path 1.0.2
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/LICENSE +20 -0
- package/README.md +292 -0
- package/build/app.js +1254 -0
- package/build/index.html +92 -0
- package/build/parse-stl.js +114 -0
- package/build/raster-path.js +688 -0
- package/build/serve.json +12 -0
- package/build/style.css +158 -0
- package/build/webgpu-worker.js +3011 -0
- package/package.json +58 -0
- package/scripts/build-shaders.js +65 -0
- package/src/index.js +688 -0
- package/src/shaders/planar-rasterize.wgsl +213 -0
- package/src/shaders/planar-toolpath.wgsl +83 -0
- package/src/shaders/radial-raster-v2.wgsl +195 -0
- package/src/web/app.js +1254 -0
- package/src/web/parse-stl.js +114 -0
- package/src/web/webgpu-worker.js +2520 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2014-Infinity Stewart Allen <sa@grid.space>
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
|
4
|
+
copy of this software and associated documentation files (the "Software"),
|
|
5
|
+
to deal in the Software without restriction, including without limitation
|
|
6
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
7
|
+
and/or sell copies of the Software, and to permit persons to whom the
|
|
8
|
+
Software is furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included
|
|
11
|
+
in all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
14
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
16
|
+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
THE SOFTWARE.
|
|
20
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# Raster Path - WebGPU Toolpath Generator
|
|
2
|
+
|
|
3
|
+
Fast browser-based terrain + tool path generator using WebGPU compute shaders.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Dual Mode Rasterization**: Planar (traditional XY grid) and Radial (cylindrical unwrap) modes
|
|
8
|
+
- **CNC Toolpath Generation**: Generate toolpaths by simulating tool movement over terrain
|
|
9
|
+
- **GPU Accelerated**: 20-100× faster than CPU-based solutions
|
|
10
|
+
- **Unified API**: Clean three-method interface that works uniformly across both modes
|
|
11
|
+
- **ESM Module**: Importable package for browser applications
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### As a Module
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
import { RasterPath } from '@gridspace/raster-path';
|
|
19
|
+
|
|
20
|
+
// Initialize for planar mode
|
|
21
|
+
const raster = new RasterPath({
|
|
22
|
+
mode: 'planar',
|
|
23
|
+
resolution: 0.1 // 0.1mm grid resolution
|
|
24
|
+
});
|
|
25
|
+
await raster.init();
|
|
26
|
+
|
|
27
|
+
// 1. Load tool (from STL triangles)
|
|
28
|
+
const toolTriangles = parseSTL(toolSTLBuffer);
|
|
29
|
+
const toolData = await raster.loadTool({
|
|
30
|
+
triangles: toolTriangles
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// 2. Load terrain (rasterizes immediately in planar mode)
|
|
34
|
+
const terrainTriangles = parseSTL(terrainSTLBuffer);
|
|
35
|
+
const terrainData = await raster.loadTerrain({
|
|
36
|
+
triangles: terrainTriangles,
|
|
37
|
+
zFloor: -100
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// 3. Generate toolpaths
|
|
41
|
+
const toolpathData = await raster.generateToolpaths({
|
|
42
|
+
xStep: 5, // Sample every 5th point in X
|
|
43
|
+
yStep: 5, // Sample every 5th point in Y
|
|
44
|
+
zFloor: -100
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
console.log(`Generated ${toolpathData.pathData.length} toolpath points`);
|
|
48
|
+
|
|
49
|
+
// Cleanup
|
|
50
|
+
raster.terminate();
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Radial Mode (for cylindrical parts)
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
// Initialize for radial mode
|
|
57
|
+
const raster = new RasterPath({
|
|
58
|
+
mode: 'radial',
|
|
59
|
+
resolution: 0.1, // Radial resolution (mm)
|
|
60
|
+
rotationStep: 1.0 // 1 degree between rays
|
|
61
|
+
});
|
|
62
|
+
await raster.init();
|
|
63
|
+
|
|
64
|
+
// Load tool and terrain (same API!)
|
|
65
|
+
await raster.loadTool({ triangles: toolTriangles });
|
|
66
|
+
await raster.loadTerrain({
|
|
67
|
+
triangles: terrainTriangles,
|
|
68
|
+
zFloor: 0
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Generate toolpaths with radius offset
|
|
72
|
+
const toolpathData = await raster.generateToolpaths({
|
|
73
|
+
xStep: 5,
|
|
74
|
+
yStep: 5,
|
|
75
|
+
zFloor: 0,
|
|
76
|
+
radiusOffset: 20 // Tool offset above surface (radial mode only)
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Output is array of strips (one per rotation angle)
|
|
80
|
+
console.log(`Generated ${toolpathData.numStrips} strips, ${toolpathData.totalPoints} points`);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Demo UI
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm install
|
|
87
|
+
npm run dev
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Open http://localhost:3000 and drag STL files onto the interface.
|
|
91
|
+
|
|
92
|
+
## Algorithm
|
|
93
|
+
|
|
94
|
+
### Planar Mode (XY Grid Rasterization)
|
|
95
|
+
|
|
96
|
+
1. **Tool Rasterization**: Create XY grid at specified resolution and rasterize tool geometry (keeps min Z per grid cell)
|
|
97
|
+
2. **Terrain Rasterization**: Rasterize terrain geometry on matching XY grid (keeps max Z per grid cell)
|
|
98
|
+
3. **Toolpath Generation**:
|
|
99
|
+
- Scan tool over terrain in XY grid with configurable step sizes (xStep, yStep)
|
|
100
|
+
- At each position, calculate minimum Z-offset where tool doesn't collide with terrain
|
|
101
|
+
- Output scanline-based toolpath as array of Z-heights
|
|
102
|
+
|
|
103
|
+
### Radial Mode (Cylindrical Rasterization)
|
|
104
|
+
|
|
105
|
+
1. **Tool Rasterization**: Rasterize tool in planar mode (same as above)
|
|
106
|
+
2. **Terrain Preparation**: Center terrain in YZ plane and store triangles
|
|
107
|
+
3. **Toolpath Generation**:
|
|
108
|
+
- Cast rays from origin at specified rotation angles (e.g., every 1°)
|
|
109
|
+
- For each ray, rasterize terrain triangles along that angle
|
|
110
|
+
- Use X-bucketing optimization to partition triangles spatially
|
|
111
|
+
- Calculate tool-terrain collisions along each radial strip
|
|
112
|
+
- Output array of strips (one per angle), each containing Z-heights along X-axis
|
|
113
|
+
|
|
114
|
+
## Performance
|
|
115
|
+
|
|
116
|
+
Example (84×84×28mm model, 6,120 triangles):
|
|
117
|
+
|
|
118
|
+
| Step Size | Points | WebGPU Time | CPU Time (WASM) |
|
|
119
|
+
|-----------|---------|-------------|-----------------|
|
|
120
|
+
| 0.5mm | 48K | 0.8s | 20-80s |
|
|
121
|
+
| 0.1mm | 1.2M | 2s | 280s |
|
|
122
|
+
|
|
123
|
+
**Speedup**: 20-100× faster with WebGPU
|
|
124
|
+
|
|
125
|
+
## Project Structure
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
src/
|
|
129
|
+
index.js # Main RasterPath API (ESM export)
|
|
130
|
+
web/
|
|
131
|
+
webgpu-worker.js # WebGPU worker (GPU compute shaders)
|
|
132
|
+
app.js # Demo web application
|
|
133
|
+
index.html # Demo UI entry point
|
|
134
|
+
style.css # Demo styles
|
|
135
|
+
parse-stl.js # STL file parser utility
|
|
136
|
+
test/
|
|
137
|
+
planar-test.cjs # Planar mode regression test
|
|
138
|
+
planar-tiling-test.cjs # Planar high-resolution test
|
|
139
|
+
radial-test.cjs # Radial mode regression test
|
|
140
|
+
benchmark/
|
|
141
|
+
fixtures/ # Test STL files (terrain.stl, tool.stl)
|
|
142
|
+
build/ # Built files (generated by npm run build)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## API Reference
|
|
146
|
+
|
|
147
|
+
### `RasterPath`
|
|
148
|
+
|
|
149
|
+
Constructor: `new RasterPath(options)`
|
|
150
|
+
|
|
151
|
+
**Options**:
|
|
152
|
+
- `mode` (string): `'planar'` or `'radial'`
|
|
153
|
+
- `resolution` (number): Grid resolution in mm (e.g., 0.1)
|
|
154
|
+
- `rotationStep` (number, radial only): Degrees between rays (e.g., 1.0)
|
|
155
|
+
|
|
156
|
+
#### `async init()`
|
|
157
|
+
Initialize WebGPU worker. Must be called before other methods.
|
|
158
|
+
|
|
159
|
+
**Returns**: `Promise<void>`
|
|
160
|
+
|
|
161
|
+
**Example**:
|
|
162
|
+
```javascript
|
|
163
|
+
const raster = new RasterPath({ mode: 'planar', resolution: 0.1 });
|
|
164
|
+
await raster.init();
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
#### `async loadTool({ triangles, sparseData })`
|
|
170
|
+
Load tool geometry for toolpath generation.
|
|
171
|
+
|
|
172
|
+
**Parameters** (one required):
|
|
173
|
+
- `triangles` (Float32Array, optional): STL triangle data (9 floats per triangle: v0.xyz, v1.xyz, v2.xyz)
|
|
174
|
+
- `sparseData` (object, optional): Pre-computed raster data with `{ bounds, positions, pointCount }`
|
|
175
|
+
|
|
176
|
+
**Returns**: `Promise<object>` - Tool raster data with `{ bounds, positions, pointCount }`
|
|
177
|
+
|
|
178
|
+
**Example**:
|
|
179
|
+
```javascript
|
|
180
|
+
// From STL triangles
|
|
181
|
+
const toolData = await raster.loadTool({
|
|
182
|
+
triangles: toolTriangles
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// From pre-computed sparse data (Kiri:Moto integration)
|
|
186
|
+
const toolData = await raster.loadTool({
|
|
187
|
+
sparseData: { bounds, positions, pointCount }
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
#### `async loadTerrain({ triangles, zFloor, boundsOverride, onProgress })`
|
|
194
|
+
Load terrain geometry. Behavior depends on mode:
|
|
195
|
+
- **Planar mode**: Rasterizes immediately and returns terrain data
|
|
196
|
+
- **Radial mode**: Stores triangles for later, returns `null`
|
|
197
|
+
|
|
198
|
+
**Parameters**:
|
|
199
|
+
- `triangles` (Float32Array): STL triangle data
|
|
200
|
+
- `zFloor` (number, optional): Z floor value for out-of-bounds areas
|
|
201
|
+
- `boundsOverride` (object, optional): Override bounding box `{min: {x, y, z}, max: {x, y, z}}`
|
|
202
|
+
- `onProgress` (function, optional): Progress callback `(progress: number) => void`
|
|
203
|
+
|
|
204
|
+
**Returns**:
|
|
205
|
+
- Planar mode: `Promise<object>` - Terrain raster data with `{ bounds, positions, pointCount }`
|
|
206
|
+
- Radial mode: `Promise<null>`
|
|
207
|
+
|
|
208
|
+
**Example**:
|
|
209
|
+
```javascript
|
|
210
|
+
// Planar mode - returns terrain data immediately
|
|
211
|
+
const terrainData = await raster.loadTerrain({
|
|
212
|
+
triangles: terrainTriangles,
|
|
213
|
+
zFloor: -100
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Radial mode - stores for later
|
|
217
|
+
await raster.loadTerrain({
|
|
218
|
+
triangles: terrainTriangles,
|
|
219
|
+
zFloor: 0
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
#### `async generateToolpaths({ xStep, yStep, zFloor, radiusOffset, onProgress })`
|
|
226
|
+
Generate toolpaths from loaded tool and terrain. Must call `loadTool()` and `loadTerrain()` first.
|
|
227
|
+
|
|
228
|
+
**Parameters**:
|
|
229
|
+
- `xStep` (number): Sample every Nth point in X direction
|
|
230
|
+
- `yStep` (number): Sample every Nth point in Y direction
|
|
231
|
+
- `zFloor` (number): Z floor value for out-of-bounds areas
|
|
232
|
+
- `radiusOffset` (number, radial only): Tool offset above surface (mm)
|
|
233
|
+
- `onProgress` (function, optional): Progress callback `(progress: number) => void`
|
|
234
|
+
|
|
235
|
+
**Returns**:
|
|
236
|
+
- Planar mode: `Promise<object>` with:
|
|
237
|
+
- `pathData` (Float32Array): Z-heights in scanline order
|
|
238
|
+
- `width` (number): Points per scanline
|
|
239
|
+
- `height` (number): Number of scanlines
|
|
240
|
+
|
|
241
|
+
- Radial mode: `Promise<object>` with:
|
|
242
|
+
- `strips` (Array): Array of strip objects, each containing:
|
|
243
|
+
- `angle` (number): Rotation angle in degrees
|
|
244
|
+
- `pathData` (Float32Array): Z-heights along X-axis
|
|
245
|
+
- `numStrips` (number): Total number of strips
|
|
246
|
+
- `totalPoints` (number): Sum of all points across strips
|
|
247
|
+
|
|
248
|
+
**Example**:
|
|
249
|
+
```javascript
|
|
250
|
+
// Works for both planar and radial modes!
|
|
251
|
+
const toolpathData = await raster.generateToolpaths({
|
|
252
|
+
xStep: 5,
|
|
253
|
+
yStep: 5,
|
|
254
|
+
zFloor: -100,
|
|
255
|
+
radiusOffset: 20 // radial mode only
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
#### `terminate()`
|
|
262
|
+
Terminate WebGPU worker and cleanup resources.
|
|
263
|
+
|
|
264
|
+
**Example**:
|
|
265
|
+
```javascript
|
|
266
|
+
raster.terminate();
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Requirements
|
|
270
|
+
|
|
271
|
+
- Modern browser with WebGPU support (Chrome 113+, Edge 113+)
|
|
272
|
+
- For testing: Electron (provides headless WebGPU environment)
|
|
273
|
+
|
|
274
|
+
## Development
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
# Install dependencies
|
|
278
|
+
npm install
|
|
279
|
+
|
|
280
|
+
# Build (copies web files to build/)
|
|
281
|
+
npm run build
|
|
282
|
+
|
|
283
|
+
# Run demo
|
|
284
|
+
npm run serve
|
|
285
|
+
|
|
286
|
+
# Test
|
|
287
|
+
npm test
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## License
|
|
291
|
+
|
|
292
|
+
MIT
|