@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
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// parse-stl.js
|
|
2
|
+
// Pure JavaScript STL parser (binary and ASCII)
|
|
3
|
+
|
|
4
|
+
// Calculate bounds from positions array
|
|
5
|
+
function calculateBounds(positions) {
|
|
6
|
+
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
7
|
+
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
|
8
|
+
|
|
9
|
+
for (let i = 0; i < positions.length; i += 3) {
|
|
10
|
+
const x = positions[i];
|
|
11
|
+
const y = positions[i + 1];
|
|
12
|
+
const z = positions[i + 2];
|
|
13
|
+
|
|
14
|
+
minX = Math.min(minX, x);
|
|
15
|
+
maxX = Math.max(maxX, x);
|
|
16
|
+
minY = Math.min(minY, y);
|
|
17
|
+
maxY = Math.max(maxY, y);
|
|
18
|
+
minZ = Math.min(minZ, z);
|
|
19
|
+
maxZ = Math.max(maxZ, z);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
min: { x: minX, y: minY, z: minZ },
|
|
24
|
+
max: { x: maxX, y: maxY, z: maxZ }
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Parse binary STL file
|
|
29
|
+
export function parseBinarySTL(buffer) {
|
|
30
|
+
const dataView = new DataView(buffer);
|
|
31
|
+
|
|
32
|
+
// Skip 80-byte header
|
|
33
|
+
const numTriangles = dataView.getUint32(80, true); // little-endian
|
|
34
|
+
|
|
35
|
+
const positions = new Float32Array(numTriangles * 9);
|
|
36
|
+
let offset = 84; // After header and triangle count
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < numTriangles; i++) {
|
|
39
|
+
// Skip normal (12 bytes)
|
|
40
|
+
offset += 12;
|
|
41
|
+
|
|
42
|
+
// Read 3 vertices (9 floats)
|
|
43
|
+
for (let j = 0; j < 9; j++) {
|
|
44
|
+
positions[i * 9 + j] = dataView.getFloat32(offset, true);
|
|
45
|
+
offset += 4;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Skip attribute byte count (2 bytes)
|
|
49
|
+
offset += 2;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const bounds = calculateBounds(positions);
|
|
53
|
+
return { positions, triangleCount: numTriangles, bounds };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Parse ASCII STL file
|
|
57
|
+
export function parseASCIISTL(text) {
|
|
58
|
+
const positions = [];
|
|
59
|
+
const lines = text.split('\n');
|
|
60
|
+
let inFacet = false;
|
|
61
|
+
const vertices = [];
|
|
62
|
+
|
|
63
|
+
for (const line of lines) {
|
|
64
|
+
const trimmed = line.trim();
|
|
65
|
+
|
|
66
|
+
if (trimmed.startsWith('facet')) {
|
|
67
|
+
inFacet = true;
|
|
68
|
+
vertices.length = 0;
|
|
69
|
+
} else if (trimmed.startsWith('vertex')) {
|
|
70
|
+
if (inFacet) {
|
|
71
|
+
const parts = trimmed.split(/\s+/);
|
|
72
|
+
vertices.push(
|
|
73
|
+
parseFloat(parts[1]),
|
|
74
|
+
parseFloat(parts[2]),
|
|
75
|
+
parseFloat(parts[3])
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
} else if (trimmed.startsWith('endfacet')) {
|
|
79
|
+
if (vertices.length === 9) {
|
|
80
|
+
positions.push(...vertices);
|
|
81
|
+
}
|
|
82
|
+
inFacet = false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const positionsArray = new Float32Array(positions);
|
|
87
|
+
const bounds = calculateBounds(positionsArray);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
positions: positionsArray,
|
|
91
|
+
triangleCount: positions.length / 9,
|
|
92
|
+
bounds
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Auto-detect and parse STL file
|
|
97
|
+
export function parseSTL(buffer) {
|
|
98
|
+
// Try to detect if binary or ASCII
|
|
99
|
+
const view = new Uint8Array(buffer);
|
|
100
|
+
const header = String.fromCharCode(...view.slice(0, 5));
|
|
101
|
+
|
|
102
|
+
if (header === 'solid') {
|
|
103
|
+
// Might be ASCII, but could also be binary with "solid" in header
|
|
104
|
+
// Try to decode as text
|
|
105
|
+
const text = new TextDecoder().decode(buffer);
|
|
106
|
+
if (text.includes('facet') && text.includes('vertex')) {
|
|
107
|
+
// Looks like ASCII
|
|
108
|
+
return parseASCIISTL(text);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Default to binary
|
|
113
|
+
return parseBinarySTL(buffer);
|
|
114
|
+
}
|