@thi.ng/axidraw 1.1.38 → 1.1.40
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/CHANGELOG.md +1 -1
- package/README.md +1 -1
- package/api.js +9 -19
- package/axidraw.js +376 -367
- package/commands.js +52 -88
- package/control.js +26 -31
- package/dip.js +13 -48
- package/package.json +16 -14
- package/palettes.js +68 -132
- package/polyline.js +25 -35
- package/registration.js +24 -31
- package/serial.js +31 -37
package/axidraw.js
CHANGED
|
@@ -9,7 +9,9 @@ import { DIN_A3_LANDSCAPE } from "@thi.ng/units/constants/paper-sizes";
|
|
|
9
9
|
import { convert, div, Quantity } from "@thi.ng/units/unit";
|
|
10
10
|
import { inch } from "@thi.ng/units/units/length";
|
|
11
11
|
import { abs2 } from "@thi.ng/vectors/abs";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
ZERO2
|
|
14
|
+
} from "@thi.ng/vectors/api";
|
|
13
15
|
import { clamp2 } from "@thi.ng/vectors/clamp";
|
|
14
16
|
import { maddN2 } from "@thi.ng/vectors/maddn";
|
|
15
17
|
import { mag } from "@thi.ng/vectors/mag";
|
|
@@ -17,383 +19,390 @@ import { mulN2 } from "@thi.ng/vectors/muln";
|
|
|
17
19
|
import { set2 } from "@thi.ng/vectors/set";
|
|
18
20
|
import { zero } from "@thi.ng/vectors/setn";
|
|
19
21
|
import { sub2 } from "@thi.ng/vectors/sub";
|
|
20
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
AxiDrawState
|
|
24
|
+
} from "./api.js";
|
|
21
25
|
import { complete, HOME, OFF, ON, PEN, UP } from "./commands.js";
|
|
22
26
|
import { AxiDrawControl } from "./control.js";
|
|
23
27
|
import { SERIAL_PORT } from "./serial.js";
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
28
|
+
const DEFAULT_OPTS = {
|
|
29
|
+
serial: SERIAL_PORT,
|
|
30
|
+
logger: new ConsoleLogger("axidraw"),
|
|
31
|
+
control: new AxiDrawControl(),
|
|
32
|
+
refresh: 1e3,
|
|
33
|
+
bounds: DIN_A3_LANDSCAPE,
|
|
34
|
+
home: [0, 0],
|
|
35
|
+
unitsPerInch: 25.4,
|
|
36
|
+
stepsPerInch: 2032,
|
|
37
|
+
speedDown: 4e3,
|
|
38
|
+
speedUp: 4e3,
|
|
39
|
+
up: 60,
|
|
40
|
+
down: 30,
|
|
41
|
+
delayUp: 150,
|
|
42
|
+
delayDown: 150,
|
|
43
|
+
preDelay: 0,
|
|
44
|
+
start: [ON, PEN(), UP()],
|
|
45
|
+
stop: [UP(), HOME, OFF],
|
|
46
|
+
sigint: true
|
|
43
47
|
};
|
|
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
|
-
this.save();
|
|
74
|
-
}
|
|
75
|
-
reset() {
|
|
76
|
-
zero(this.pos);
|
|
77
|
-
zero(this.targetPos);
|
|
78
|
-
this.send("R\r");
|
|
79
|
-
return this;
|
|
48
|
+
class AxiDraw {
|
|
49
|
+
serial;
|
|
50
|
+
opts;
|
|
51
|
+
isConnected = false;
|
|
52
|
+
isPenDown = false;
|
|
53
|
+
penLimits;
|
|
54
|
+
penState = [];
|
|
55
|
+
pos = [0, 0];
|
|
56
|
+
targetPos = [0, 0];
|
|
57
|
+
homePos;
|
|
58
|
+
scale;
|
|
59
|
+
bounds;
|
|
60
|
+
constructor(opts = {}) {
|
|
61
|
+
this.opts = { ...DEFAULT_OPTS, ...opts };
|
|
62
|
+
this.penLimits = [this.opts.down, this.opts.up];
|
|
63
|
+
this.scale = this.opts.stepsPerInch / this.opts.unitsPerInch;
|
|
64
|
+
this.setHome(this.opts.home);
|
|
65
|
+
if (this.opts.bounds) {
|
|
66
|
+
this.bounds = this.opts.bounds instanceof Quantity ? [
|
|
67
|
+
[0, 0],
|
|
68
|
+
convert(
|
|
69
|
+
this.opts.bounds,
|
|
70
|
+
div(inch, this.opts.stepsPerInch)
|
|
71
|
+
)
|
|
72
|
+
] : [
|
|
73
|
+
mulN2([], this.opts.bounds[0], this.scale),
|
|
74
|
+
mulN2([], this.opts.bounds[1], this.scale)
|
|
75
|
+
];
|
|
80
76
|
}
|
|
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
|
-
|
|
77
|
+
this.save();
|
|
78
|
+
}
|
|
79
|
+
reset() {
|
|
80
|
+
zero(this.pos);
|
|
81
|
+
zero(this.targetPos);
|
|
82
|
+
this.send("R\r");
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Async function. Attempts to connect to the drawing machine via given
|
|
87
|
+
* (partial) serial port path/name, returns true if successful.
|
|
88
|
+
*
|
|
89
|
+
* @remarks
|
|
90
|
+
* First matching port will be used. If `path` is a sting, a port name must
|
|
91
|
+
* only start with it in order to be considered a match.
|
|
92
|
+
*
|
|
93
|
+
* An error is thrown if no matching port could be found.
|
|
94
|
+
*
|
|
95
|
+
* @param path
|
|
96
|
+
*/
|
|
97
|
+
async connect(path = "/dev/tty.usbmodem") {
|
|
98
|
+
const isStr = isString(path);
|
|
99
|
+
for (let port of await this.opts.serial.list(path.toString())) {
|
|
100
|
+
if (isStr && port.path.startsWith(path) || !isStr && path.test(port.path)) {
|
|
101
|
+
this.opts.logger.info(`using device: ${port.path}...`);
|
|
102
|
+
this.serial = this.opts.serial.ctor(port.path, 38400);
|
|
103
|
+
this.isConnected = true;
|
|
104
|
+
if (this.opts.sigint) {
|
|
105
|
+
this.opts.logger.debug("installing signal handler...");
|
|
106
|
+
process.on("SIGINT", this.onSignal.bind(this));
|
|
107
107
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
disconnect() {
|
|
111
|
-
this.serial.close();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
112
110
|
}
|
|
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
|
-
let dist;
|
|
182
|
-
switch (cmd) {
|
|
183
|
-
case "start":
|
|
184
|
-
case "stop": {
|
|
185
|
-
const metrics = await this.draw(this.opts[cmd], false, false);
|
|
186
|
-
numCommands += metrics.commands;
|
|
187
|
-
penCommands += metrics.penCommands;
|
|
188
|
-
totalDist += metrics.totalDist;
|
|
189
|
-
drawDist += metrics.drawDist;
|
|
190
|
-
break;
|
|
191
|
-
}
|
|
192
|
-
case "home":
|
|
193
|
-
[wait, dist] = this.home();
|
|
194
|
-
$recordDist(dist);
|
|
195
|
-
break;
|
|
196
|
-
case "reset":
|
|
197
|
-
this.reset();
|
|
198
|
-
break;
|
|
199
|
-
case "on":
|
|
200
|
-
this.motorsOn();
|
|
201
|
-
break;
|
|
202
|
-
case "off":
|
|
203
|
-
this.motorsOff();
|
|
204
|
-
break;
|
|
205
|
-
case "pen":
|
|
206
|
-
this.penConfig(a, b);
|
|
207
|
-
break;
|
|
208
|
-
case "u":
|
|
209
|
-
wait = this.penUp(a, b);
|
|
210
|
-
penCommands++;
|
|
211
|
-
break;
|
|
212
|
-
case "d":
|
|
213
|
-
wait = this.penDown(a, b);
|
|
214
|
-
penCommands++;
|
|
215
|
-
break;
|
|
216
|
-
case "save":
|
|
217
|
-
this.save();
|
|
218
|
-
break;
|
|
219
|
-
case "restore":
|
|
220
|
-
this.restore();
|
|
221
|
-
break;
|
|
222
|
-
case "w":
|
|
223
|
-
wait = a;
|
|
224
|
-
break;
|
|
225
|
-
case "M":
|
|
226
|
-
[wait, dist] = this.moveTo(a, b);
|
|
227
|
-
$recordDist(dist);
|
|
228
|
-
break;
|
|
229
|
-
case "m":
|
|
230
|
-
[wait, dist] = this.moveRelative(a, b);
|
|
231
|
-
$recordDist(dist);
|
|
232
|
-
break;
|
|
233
|
-
case "comment":
|
|
234
|
-
logger.info(`comment: ${a}`);
|
|
235
|
-
break;
|
|
236
|
-
default:
|
|
237
|
-
unsupported(`unknown command: ${$cmd}`);
|
|
238
|
-
}
|
|
239
|
-
if (wait > 0) {
|
|
240
|
-
wait = Math.max(0, wait - preDelay);
|
|
241
|
-
logger.debug(`waiting ${wait}ms...`);
|
|
242
|
-
await delayed(0, wait);
|
|
243
|
-
}
|
|
244
|
-
// restore one-off pen config to current state
|
|
245
|
-
if (cmd === "d" && b !== undefined) {
|
|
246
|
-
this.sendPenConfig(5, this.penLimits[0]);
|
|
247
|
-
}
|
|
248
|
-
else if (cmd === "u" && b !== undefined) {
|
|
249
|
-
this.sendPenConfig(4, this.penLimits[1]);
|
|
250
|
-
}
|
|
111
|
+
ioerror(`no matching device for ${path}`);
|
|
112
|
+
}
|
|
113
|
+
disconnect() {
|
|
114
|
+
this.serial.close();
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Async function. Converts sequence of {@link DrawCommand}s into actual EBB
|
|
118
|
+
* commands and sends them via configured serial port to the AxiDraw. If
|
|
119
|
+
* `wrap` is enabled (default), the given commands will be automatically
|
|
120
|
+
* wrapped with start/stop commands via {@link complete}. Returns object of
|
|
121
|
+
* collected {@link Metrics}. If `showMetrics` is enabled (default), the
|
|
122
|
+
* metrics will also be written to the configured logger.
|
|
123
|
+
*
|
|
124
|
+
* @remarks
|
|
125
|
+
* This function is async and if using `await` will only return once all
|
|
126
|
+
* commands have been processed or cancelled.
|
|
127
|
+
*
|
|
128
|
+
* The `control` implementation/ provided as part of {@link AxiDrawOpts} can
|
|
129
|
+
* be used to pause, resume or cancel the drawing (see
|
|
130
|
+
* {@link AxiDrawOpts.control} for details).
|
|
131
|
+
*
|
|
132
|
+
* Reference:
|
|
133
|
+
* - http://evil-mad.github.io/EggBot/ebb.html
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* // execute start sequence, draw a triangle, then exec stop sequence
|
|
138
|
+
* axi.draw([
|
|
139
|
+
* ["start"],
|
|
140
|
+
* ...axi.polyline([[50,50], [100,50], [75, 100], [50,50]]),
|
|
141
|
+
* ["stop"]
|
|
142
|
+
* ]);
|
|
143
|
+
* ```
|
|
144
|
+
*
|
|
145
|
+
* @param commands
|
|
146
|
+
* @param wrap
|
|
147
|
+
* @param showMetrics
|
|
148
|
+
*/
|
|
149
|
+
async draw(commands, wrap = true, showMetrics = true) {
|
|
150
|
+
assert(
|
|
151
|
+
this.isConnected,
|
|
152
|
+
"AxiDraw not yet connected, need to call .connect() first"
|
|
153
|
+
);
|
|
154
|
+
let t0 = Date.now();
|
|
155
|
+
let numCommands = 0;
|
|
156
|
+
let penCommands = 0;
|
|
157
|
+
let totalDist = 0;
|
|
158
|
+
let drawDist = 0;
|
|
159
|
+
const $recordDist = (dist) => {
|
|
160
|
+
totalDist += dist;
|
|
161
|
+
if (this.isPenDown)
|
|
162
|
+
drawDist += dist;
|
|
163
|
+
};
|
|
164
|
+
const { control, logger, preDelay, refresh } = this.opts;
|
|
165
|
+
for (let $cmd of wrap ? complete(commands) : commands) {
|
|
166
|
+
numCommands++;
|
|
167
|
+
if (control) {
|
|
168
|
+
let state = control.deref();
|
|
169
|
+
if (state === AxiDrawState.PAUSE) {
|
|
170
|
+
const penDown = this.isPenDown;
|
|
171
|
+
if (penDown)
|
|
172
|
+
this.penUp();
|
|
173
|
+
do {
|
|
174
|
+
await delayed(0, refresh);
|
|
175
|
+
} while ((state = control.deref()) === AxiDrawState.PAUSE);
|
|
176
|
+
if (state === AxiDrawState.CONTINUE && penDown) {
|
|
177
|
+
this.penDown();
|
|
178
|
+
}
|
|
251
179
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
logger.info(`total commands : ${numCommands}`);
|
|
256
|
-
logger.info(`pen up/downs : ${penCommands}`);
|
|
257
|
-
logger.info(`total distance : ${totalDist.toFixed(2)}`);
|
|
258
|
-
logger.info(`draw distance : ${drawDist.toFixed(2)}`);
|
|
180
|
+
if (state === AxiDrawState.CANCEL) {
|
|
181
|
+
this.penUp();
|
|
182
|
+
break;
|
|
259
183
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
motorsOn() {
|
|
278
|
-
this.send("EM,1,1\r");
|
|
279
|
-
}
|
|
280
|
-
motorsOff() {
|
|
281
|
-
this.send("EM,0,0\r");
|
|
282
|
-
}
|
|
283
|
-
save() {
|
|
284
|
-
this.opts.logger.debug("saving pen state:", this.penLimits);
|
|
285
|
-
this.penState.push(this.penLimits.slice());
|
|
286
|
-
}
|
|
287
|
-
restore() {
|
|
288
|
-
if (this.penState.length < 2) {
|
|
289
|
-
this.opts.logger.warn("stack underflow, can't restore pen state");
|
|
290
|
-
return;
|
|
184
|
+
}
|
|
185
|
+
const [cmd, a, b] = $cmd;
|
|
186
|
+
let wait = -1;
|
|
187
|
+
let dist;
|
|
188
|
+
switch (cmd) {
|
|
189
|
+
case "start":
|
|
190
|
+
case "stop": {
|
|
191
|
+
const metrics = await this.draw(
|
|
192
|
+
this.opts[cmd],
|
|
193
|
+
false,
|
|
194
|
+
false
|
|
195
|
+
);
|
|
196
|
+
numCommands += metrics.commands;
|
|
197
|
+
penCommands += metrics.penCommands;
|
|
198
|
+
totalDist += metrics.totalDist;
|
|
199
|
+
drawDist += metrics.drawDist;
|
|
200
|
+
break;
|
|
291
201
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
202
|
+
case "home":
|
|
203
|
+
[wait, dist] = this.home();
|
|
204
|
+
$recordDist(dist);
|
|
205
|
+
break;
|
|
206
|
+
case "reset":
|
|
207
|
+
this.reset();
|
|
208
|
+
break;
|
|
209
|
+
case "on":
|
|
210
|
+
this.motorsOn();
|
|
211
|
+
break;
|
|
212
|
+
case "off":
|
|
213
|
+
this.motorsOff();
|
|
214
|
+
break;
|
|
215
|
+
case "pen":
|
|
216
|
+
this.penConfig(a, b);
|
|
217
|
+
break;
|
|
218
|
+
case "u":
|
|
219
|
+
wait = this.penUp(a, b);
|
|
220
|
+
penCommands++;
|
|
221
|
+
break;
|
|
222
|
+
case "d":
|
|
223
|
+
wait = this.penDown(a, b);
|
|
224
|
+
penCommands++;
|
|
225
|
+
break;
|
|
226
|
+
case "save":
|
|
227
|
+
this.save();
|
|
228
|
+
break;
|
|
229
|
+
case "restore":
|
|
230
|
+
this.restore();
|
|
231
|
+
break;
|
|
232
|
+
case "w":
|
|
233
|
+
wait = a;
|
|
234
|
+
break;
|
|
235
|
+
case "M":
|
|
236
|
+
[wait, dist] = this.moveTo(a, b);
|
|
237
|
+
$recordDist(dist);
|
|
238
|
+
break;
|
|
239
|
+
case "m":
|
|
240
|
+
[wait, dist] = this.moveRelative(a, b);
|
|
241
|
+
$recordDist(dist);
|
|
242
|
+
break;
|
|
243
|
+
case "comment":
|
|
244
|
+
logger.info(`comment: ${a}`);
|
|
245
|
+
break;
|
|
246
|
+
default:
|
|
247
|
+
unsupported(`unknown command: ${$cmd}`);
|
|
248
|
+
}
|
|
249
|
+
if (wait > 0) {
|
|
250
|
+
wait = Math.max(0, wait - preDelay);
|
|
251
|
+
logger.debug(`waiting ${wait}ms...`);
|
|
252
|
+
await delayed(0, wait);
|
|
253
|
+
}
|
|
254
|
+
if (cmd === "d" && b !== void 0) {
|
|
255
|
+
this.sendPenConfig(5, this.penLimits[0]);
|
|
256
|
+
} else if (cmd === "u" && b !== void 0) {
|
|
257
|
+
this.sendPenConfig(4, this.penLimits[1]);
|
|
258
|
+
}
|
|
305
259
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
260
|
+
const duration = Date.now() - t0;
|
|
261
|
+
if (showMetrics) {
|
|
262
|
+
logger.info(`total duration : ${formatDuration(duration)}`);
|
|
263
|
+
logger.info(`total commands : ${numCommands}`);
|
|
264
|
+
logger.info(`pen up/downs : ${penCommands}`);
|
|
265
|
+
logger.info(`total distance : ${totalDist.toFixed(2)}`);
|
|
266
|
+
logger.info(`draw distance : ${drawDist.toFixed(2)}`);
|
|
313
267
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
* @param tempo
|
|
346
|
-
*/
|
|
347
|
-
moveRelative(delta, tempo) {
|
|
348
|
-
const { pos, scale, targetPos } = this;
|
|
349
|
-
// apply scale factor: worldspace units -> motor steps
|
|
350
|
-
maddN2(targetPos, delta, scale, pos);
|
|
351
|
-
return this.sendMove(tempo);
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* Syntax sugar for {@link AxiDraw.moveTo}([0, 0]).
|
|
355
|
-
*/
|
|
356
|
-
home() {
|
|
357
|
-
return this.moveTo(ZERO2);
|
|
358
|
-
}
|
|
359
|
-
setHome(pos) {
|
|
360
|
-
this.homePos = mulN2([], pos, this.scale);
|
|
361
|
-
this.opts.logger.debug("setting home position:", pos);
|
|
362
|
-
}
|
|
363
|
-
async onSignal() {
|
|
364
|
-
this.opts.logger.warn(`SIGNINT received, stop drawing...`);
|
|
365
|
-
this.penUp(0);
|
|
366
|
-
this.motorsOff();
|
|
367
|
-
await delayed(0, 100);
|
|
368
|
-
process.exit(1);
|
|
369
|
-
}
|
|
370
|
-
send(msg) {
|
|
371
|
-
this.opts.logger.debug(msg);
|
|
372
|
-
this.serial.write(msg);
|
|
373
|
-
}
|
|
374
|
-
sendMove(tempo = 1) {
|
|
375
|
-
const { bounds, pos, scale, targetPos, opts, isPenDown } = this;
|
|
376
|
-
if (bounds)
|
|
377
|
-
clamp2(null, targetPos, ...bounds);
|
|
378
|
-
const delta = sub2([], targetPos, pos);
|
|
379
|
-
set2(pos, targetPos);
|
|
380
|
-
const maxAxis = Math.max(...abs2([], delta));
|
|
381
|
-
const duration = (1000 * maxAxis) /
|
|
382
|
-
((isPenDown ? opts.speedDown : opts.speedUp) * tempo);
|
|
383
|
-
this.send(`XM,${duration | 0},${delta[0] | 0},${delta[1] | 0}\r`);
|
|
384
|
-
return [duration, mag(delta) / scale];
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* Sends pen up/down config
|
|
388
|
-
*
|
|
389
|
-
* @remarks
|
|
390
|
-
* Reference:
|
|
391
|
-
* - https://github.com/evil-mad/AxiDraw-Processing/blob/80d81a8c897b8a1872b0555af52a8d1b5b13cec4/AxiGen1/AxiGen1.pde#L213
|
|
392
|
-
*
|
|
393
|
-
* @param id
|
|
394
|
-
* @param x
|
|
395
|
-
*/
|
|
396
|
-
sendPenConfig(id, x) {
|
|
397
|
-
this.send(`SC,${id},${(7500 + 175 * x) | 0}\r`);
|
|
268
|
+
return {
|
|
269
|
+
duration,
|
|
270
|
+
drawDist,
|
|
271
|
+
totalDist,
|
|
272
|
+
penCommands,
|
|
273
|
+
commands: numCommands
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Syntax sugar for drawing a **single** command only, otherwise same as
|
|
278
|
+
* {@link AxiDraw.draw}.
|
|
279
|
+
*
|
|
280
|
+
* @param cmd
|
|
281
|
+
*/
|
|
282
|
+
draw1(cmd) {
|
|
283
|
+
return this.draw([cmd], false);
|
|
284
|
+
}
|
|
285
|
+
motorsOn() {
|
|
286
|
+
this.send("EM,1,1\r");
|
|
287
|
+
}
|
|
288
|
+
motorsOff() {
|
|
289
|
+
this.send("EM,0,0\r");
|
|
290
|
+
}
|
|
291
|
+
save() {
|
|
292
|
+
this.opts.logger.debug("saving pen state:", this.penLimits);
|
|
293
|
+
this.penState.push(this.penLimits.slice());
|
|
294
|
+
}
|
|
295
|
+
restore() {
|
|
296
|
+
if (this.penState.length < 2) {
|
|
297
|
+
this.opts.logger.warn("stack underflow, can't restore pen state");
|
|
298
|
+
return;
|
|
398
299
|
}
|
|
300
|
+
const [down, up] = this.penLimits = this.penState.pop();
|
|
301
|
+
this.sendPenConfig(5, down);
|
|
302
|
+
this.sendPenConfig(4, up);
|
|
303
|
+
this.opts.logger.debug("restored pen state:", this.penLimits);
|
|
304
|
+
}
|
|
305
|
+
penConfig(down, up) {
|
|
306
|
+
down = down !== void 0 ? down : this.opts.down;
|
|
307
|
+
this.sendPenConfig(5, down);
|
|
308
|
+
this.penLimits[0] = down;
|
|
309
|
+
up = up !== void 0 ? up : this.opts.up;
|
|
310
|
+
this.sendPenConfig(4, up);
|
|
311
|
+
this.penLimits[1] = up;
|
|
312
|
+
this.send(`SC,10,65535\r`);
|
|
313
|
+
}
|
|
314
|
+
penUp(delay, level) {
|
|
315
|
+
if (level !== void 0)
|
|
316
|
+
this.sendPenConfig(4, level);
|
|
317
|
+
delay = delay !== void 0 && delay >= 0 ? delay : this.opts.delayUp;
|
|
318
|
+
this.send(`SP,1,${delay}\r`);
|
|
319
|
+
this.isPenDown = false;
|
|
320
|
+
return delay;
|
|
321
|
+
}
|
|
322
|
+
penDown(delay, level) {
|
|
323
|
+
if (level !== void 0)
|
|
324
|
+
this.sendPenConfig(5, level);
|
|
325
|
+
delay = delay !== void 0 && delay >= 0 ? delay : this.opts.delayDown;
|
|
326
|
+
this.send(`SP,0,${delay}\r`);
|
|
327
|
+
this.isPenDown = true;
|
|
328
|
+
return delay;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Sends a "moveto" command (absolute coords). Returns tuple of `[duration,
|
|
332
|
+
* distance]` (distance in original/configured units)
|
|
333
|
+
*
|
|
334
|
+
* @remarks
|
|
335
|
+
* Even though this method accepts absolute coords, all AxiDraw movements
|
|
336
|
+
* are relative. Depending on pen up/down state, movement speed will be
|
|
337
|
+
* either the configured {@link AxiDrawOpts.speedDown} or
|
|
338
|
+
* {@link AxiDrawOpts.speedUp}.
|
|
339
|
+
*
|
|
340
|
+
* @param p
|
|
341
|
+
* @param tempo
|
|
342
|
+
*/
|
|
343
|
+
moveTo(p, tempo) {
|
|
344
|
+
const { homePos, scale, targetPos } = this;
|
|
345
|
+
maddN2(targetPos, p, scale, homePos);
|
|
346
|
+
return this.sendMove(tempo);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Similar to {@link AxiDraw.moveTo}, but using **relative** coordinates.
|
|
350
|
+
*
|
|
351
|
+
* @param delta
|
|
352
|
+
* @param tempo
|
|
353
|
+
*/
|
|
354
|
+
moveRelative(delta, tempo) {
|
|
355
|
+
const { pos, scale, targetPos } = this;
|
|
356
|
+
maddN2(targetPos, delta, scale, pos);
|
|
357
|
+
return this.sendMove(tempo);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Syntax sugar for {@link AxiDraw.moveTo}([0, 0]).
|
|
361
|
+
*/
|
|
362
|
+
home() {
|
|
363
|
+
return this.moveTo(ZERO2);
|
|
364
|
+
}
|
|
365
|
+
setHome(pos) {
|
|
366
|
+
this.homePos = mulN2([], pos, this.scale);
|
|
367
|
+
this.opts.logger.debug("setting home position:", pos);
|
|
368
|
+
}
|
|
369
|
+
async onSignal() {
|
|
370
|
+
this.opts.logger.warn(`SIGNINT received, stop drawing...`);
|
|
371
|
+
this.penUp(0);
|
|
372
|
+
this.motorsOff();
|
|
373
|
+
await delayed(0, 100);
|
|
374
|
+
process.exit(1);
|
|
375
|
+
}
|
|
376
|
+
send(msg) {
|
|
377
|
+
this.opts.logger.debug(msg);
|
|
378
|
+
this.serial.write(msg);
|
|
379
|
+
}
|
|
380
|
+
sendMove(tempo = 1) {
|
|
381
|
+
const { bounds, pos, scale, targetPos, opts, isPenDown } = this;
|
|
382
|
+
if (bounds)
|
|
383
|
+
clamp2(null, targetPos, ...bounds);
|
|
384
|
+
const delta = sub2([], targetPos, pos);
|
|
385
|
+
set2(pos, targetPos);
|
|
386
|
+
const maxAxis = Math.max(...abs2([], delta));
|
|
387
|
+
const duration = 1e3 * maxAxis / ((isPenDown ? opts.speedDown : opts.speedUp) * tempo);
|
|
388
|
+
this.send(`XM,${duration | 0},${delta[0] | 0},${delta[1] | 0}\r`);
|
|
389
|
+
return [duration, mag(delta) / scale];
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Sends pen up/down config
|
|
393
|
+
*
|
|
394
|
+
* @remarks
|
|
395
|
+
* Reference:
|
|
396
|
+
* - https://github.com/evil-mad/AxiDraw-Processing/blob/80d81a8c897b8a1872b0555af52a8d1b5b13cec4/AxiGen1/AxiGen1.pde#L213
|
|
397
|
+
*
|
|
398
|
+
* @param id
|
|
399
|
+
* @param x
|
|
400
|
+
*/
|
|
401
|
+
sendPenConfig(id, x) {
|
|
402
|
+
this.send(`SC,${id},${7500 + 175 * x | 0}\r`);
|
|
403
|
+
}
|
|
399
404
|
}
|
|
405
|
+
export {
|
|
406
|
+
AxiDraw,
|
|
407
|
+
DEFAULT_OPTS
|
|
408
|
+
};
|