forked from Cloud-CNC/cura-wasm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
236 lines (201 loc) · 5.94 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/**
* @fileoverview Cura WASM
*/
//Imports
import {BlobWorker, spawn, Thread, Transfer} from 'threads';
import {CombinedDefinition, Extruder, Printer} from 'cura-wasm-definitions/src/types';
import {EventEmitter} from 'events';
import {printers} from 'cura-wasm-definitions/src/definitions/index';
import type {FunctionThread, ModuleThread} from 'threads/dist/types/master';
import type {Metadata, override} from './types';
import {Observable} from 'observable-fns';
//@ts-ignore Import worker (Bundled with `rollup-plugin-threads`)
import WorkerText from './worker';
/**
* Default printer definition
*/
const defaultDefinition: CombinedDefinition = {
extruders: [
<Extruder>printers.fdmextruder
],
printer: <Printer>printers.fdmprinter
};
/**
* Duplicate definition of non-exported ArbitraryThreadType from threads/dist/master/spawn.d.ts
*/
declare type ArbitraryThreadType = FunctionThread<any, any> & ModuleThread<any>;
/**
* Configuration for Cura WASM
*/
interface config
{
/**
* The Cura Engine launch command (Instead of setting overrides)
*
* There's a 99% chance you want to start your command with
* `slice -j definitions/printer.def.json -l Model.stl -o Model.gcode`
* regardless of what overrides you're using.
*
* Note: You cannot use overrides with this setting!
*/
command: string | null,
/**
* The 3D printer definition to use
*
* Default: `fdmprinter`
*/
definition: CombinedDefinition,
/**
* Overrides for the specified 3D printer definition (Instead of a launch command)
*
* Note: You cannot use the command setting with this setting!
*/
overrides: override[],
/**
* Wether or not to transfer the ArrayBuffer to the worker
*
* (Prevents duplicating large amounts of memory but empties
* the ArrayBuffer on the main thread preventing other code from using
* the ArrayBuffer)
*/
transfer: boolean
/**
* Wether to enable verbose logging or not (Useful for debugging)
*/
verbose: boolean
}
/**
* @class Cura compiled to Web Assembly (WASM)
*/
export class CuraWASM extends EventEmitter
{
/**
* Consumer provided configuration for Cura WASM
*/
private config: config;
/**
* Wether the consumer has called the `load` function or not
*/
private loaded: boolean;
/**
* A number holding the previous progress to prevent duplicate progress events
*/
private oldProgress: number;
/**
* Cura WASM runs in multiple threads to avoid blocking the main thread,
* this worker is what runs Cura Engine in a separate thread
*/
//@ts-ignore: Complains about worker not being assigned in constructor
private worker: ArbitraryThreadType;
constructor(config: Partial<config>)
{
super();
//Prevent consumer specifying both launch command and overrides
if (config.command != null && config.overrides != null)
{
throw new Error('You CANNOT specify both launch arguments and overrides! (Pick one)');
}
//Store config with defaults
this.config = {
command: null,
definition: defaultDefinition,
overrides: [],
transfer: true,
verbose: false,
...config
};
this.loaded = false;
this.oldProgress = 0;
}
/**
* Load emscripten and add definitions (Internal use only)
*/
private async load(): Promise<void>
{
//Initialize worker
this.worker = await spawn(BlobWorker.fromText(WorkerText));
await this.worker.initialize(this.config.verbose);
this.log('Initialized worker!');
await this.worker.addDefinitions(this.config.definition);
this.log('Added definitions!');
//Set loaded flag
this.loaded = true;
}
/**
* Slice the provided file using the settings specified in the constructor
* @param file The raw file to slice
* @param extension The file extension (Used for determining the correct parser)
* @returns The raw, sliced GCODE and the print metadata
*/
async slice(file: ArrayBuffer, extension: string)
{
//Make sure we've loaded emscripten
if (!this.loaded)
{
await this.load();
}
//If the transfer option is true, convert the model to a ThreadJS transferable otherwise have ThreadsJS clone the arraybuffer
const bytes = this.config.transfer ? Transfer(file) : file;
//Get worker observers
const progressObserver = this.worker.observeProgress() as Observable<any>;
const metadataObserver = this.worker.observeMetadata() as Observable<any>;
//Observe the progress
progressObserver.subscribe((progress: number) =>
{
//Normalize progress
progress = Math.trunc(100 * progress);
//Filter out repeat progress updates
if (this.oldProgress != progress)
{
this.log(`Progress: ${progress}%`);
//Event event
this.emit('progress', progress);
}
});
//Observe the metadata
let metadata: Metadata | null = null;
metadataObserver.subscribe((eventMetadata: Metadata) =>
{
//Save for later
metadata = eventMetadata;
});
//Run Cura
const gcode = await this.worker.run(this.config.command, this.config.overrides, this.config.verbose, bytes, extension.toLowerCase()) as Error | ArrayBuffer;
//Handle errors
if (gcode instanceof Error)
{
throw gcode;
}
//Handle edge cases where the metadata event hasn't been fired but Cura Engine has finished slicing
if (metadata == null)
{
this.log('Metadata event has not fired yet! (This is likely a bug!)');
}
return {
gcode,
metadata
};
}
/**
* Cleanup all assets and destroy the worker
*/
async destroy(): Promise<void>
{
//Remove definitions
await this.worker.removeDefinitions();
this.log('Removed definitions!');
//Destroy the worker
await Thread.terminate(this.worker);
}
/**
* Verbose logging method (Internal use only)
* @param msg
*/
private log(msg: string): void
{
if (this.config.verbose)
{
console.log(msg);
}
}
}