@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.
@@ -0,0 +1,92 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Raster Path</title>
7
+ <link rel="stylesheet" href="style.css">
8
+ </head>
9
+ <body>
10
+ <div id="container">
11
+ <!-- Controls Panel -->
12
+ <div class="controls">
13
+ <div class="section">
14
+ <h3>Mode</h3>
15
+ <div class="mode-toggle">
16
+ <label><input type="radio" name="mode" value="planar" checked> Planar</label>
17
+ <label><input type="radio" name="mode" value="radial"> Radial</label>
18
+ </div>
19
+ </div>
20
+
21
+ <div class="section">
22
+ <h3>Load STL</h3>
23
+ <button id="load-model" class="btn">Load Model</button>
24
+ <div id="model-status" class="status">No model loaded</div>
25
+ <button id="load-tool" class="btn">Load Tool</button>
26
+ <div id="tool-status" class="status">No tool loaded</div>
27
+ </div>
28
+
29
+ <div class="section">
30
+ <h3>Resolution</h3>
31
+ <select id="resolution">
32
+ <option value="0.100" selected>0.100mm</option>
33
+ <option value="0.050">0.050mm</option>
34
+ <option value="0.025">0.025mm</option>
35
+ <option value="0.010">0.010mm</option>
36
+ </select>
37
+ </div>
38
+
39
+ <div class="section">
40
+ <h3>Parameters</h3>
41
+ <label>
42
+ Z Floor: <input type="number" id="z-floor" value="-100" step="10" style="width: 70px;">
43
+ </label>
44
+ <label>
45
+ X Step: <input type="number" id="x-step" value="5" min="1" max="50" style="width: 60px;">
46
+ </label>
47
+ <label>
48
+ Y Step: <input type="number" id="y-step" value="5" min="1" max="50" style="width: 60px;">
49
+ </label>
50
+ <label id="angle-step-container" class="hide">
51
+ Angle Step (deg): <input type="number" id="angle-step" value="1" min="0.1" max="10" step="0.1" style="width: 60px;">
52
+ </label>
53
+ </div>
54
+
55
+ <div class="section">
56
+ <h3>Process</h3>
57
+ <button id="rasterize" class="btn" disabled>Rasterize</button>
58
+ <button id="generate-toolpath" class="btn" disabled>Generate Toolpath</button>
59
+ </div>
60
+
61
+ <div class="section">
62
+ <h3>View</h3>
63
+ <label><input type="checkbox" id="show-model" checked> Model</label>
64
+ <label><input type="checkbox" id="show-raster"> Raster</label>
65
+ <label><input type="checkbox" id="show-paths"> Toolpaths</label>
66
+ <label id="wrapped-container" class="hide">
67
+ <input type="checkbox" id="show-wrapped"> Wrapped
68
+ </label>
69
+ </div>
70
+
71
+ <div class="section">
72
+ <h3>Info</h3>
73
+ <div id="info" class="info-text">Ready</div>
74
+ </div>
75
+ </div>
76
+
77
+ <!-- 3D Canvas -->
78
+ <canvas id="canvas"></canvas>
79
+ </div>
80
+
81
+ <script type="importmap">
82
+ {
83
+ "imports": {
84
+ "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
85
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
86
+ }
87
+ }
88
+ </script>
89
+ <script type="module" src="app.js"></script>
90
+ <!-- <script type="module" src="webgpu-worker.js"></script> -->
91
+ </body>
92
+ </html>
@@ -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
+ }