@thi.ng/axidraw 0.2.3 → 0.3.0
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 +23 -1
- package/README.md +25 -4
- package/api.d.ts +82 -20
- package/axidraw.d.ts +20 -10
- package/axidraw.js +61 -17
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/package.json +11 -4
- package/serial.d.ts +27 -0
- package/serial.js +38 -0
- package/utils.d.ts +17 -0
- package/utils.js +34 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2022-12-
|
|
3
|
+
- **Last updated**: 2022-12-29T20:56:59Z
|
|
4
4
|
- **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
|
|
5
5
|
|
|
6
6
|
All notable changes to this project will be documented in this file.
|
|
@@ -9,6 +9,28 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
|
|
|
9
9
|
**Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
|
|
10
10
|
and/or version bumps of transitive dependencies.
|
|
11
11
|
|
|
12
|
+
## [0.3.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/axidraw@0.3.0) (2022-12-29)
|
|
13
|
+
|
|
14
|
+
#### 🚀 Features
|
|
15
|
+
|
|
16
|
+
- add draw metrics ([0ff015c](https://github.com/thi-ng/umbrella/commit/0ff015c))
|
|
17
|
+
- add Metrics interface
|
|
18
|
+
- update AxiDraw.draw() to record metrics
|
|
19
|
+
- update .moveTo() to return delay & distance
|
|
20
|
+
- add serial port abstraction & impls ([c774da1](https://github.com/thi-ng/umbrella/commit/c774da1))
|
|
21
|
+
- add SerialConnection adapter
|
|
22
|
+
- add `SERIAL_PORT` default impl (actual serial port)
|
|
23
|
+
- add `MOCK_SERIAL` & MockSerial impl for testing
|
|
24
|
+
- update AxiDrawOpts & AxiDraw to use adapter only
|
|
25
|
+
- add registrationMark() util, fix imports ([e05e99d](https://github.com/thi-ng/umbrella/commit/e05e99d))
|
|
26
|
+
- add Metrics.penCommands, fix nested metrics handling ([a7149cd](https://github.com/thi-ng/umbrella/commit/a7149cd))
|
|
27
|
+
- add counter for pen up/down commands
|
|
28
|
+
- add start/stop cmd metrics to current tally
|
|
29
|
+
|
|
30
|
+
#### ♻️ Refactoring
|
|
31
|
+
|
|
32
|
+
- update "no-browser" pkg handling ([0e84f1b](https://github.com/thi-ng/umbrella/commit/0e84f1b))
|
|
33
|
+
|
|
12
34
|
## [0.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/axidraw@0.2.0) (2022-12-10)
|
|
13
35
|
|
|
14
36
|
#### 🚀 Features
|
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ This project is part of the
|
|
|
17
17
|
- [SVG support](#svg-support)
|
|
18
18
|
- [Serial port support](#serial-port-support)
|
|
19
19
|
- [Draw control](#draw-control)
|
|
20
|
+
- [Metrics](#metrics)
|
|
20
21
|
- [Status](#status)
|
|
21
22
|
- [Installation](#installation)
|
|
22
23
|
- [Dependencies](#dependencies)
|
|
@@ -115,9 +116,11 @@ only aimed at Node.js though...
|
|
|
115
116
|
|
|
116
117
|
### Draw control
|
|
117
118
|
|
|
118
|
-
The main
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
The main
|
|
120
|
+
[`draw()`](https://docs.thi.ng/umbrella/axidraw/classes/AxiDraw.html#draw)
|
|
121
|
+
function provided by this package is async and supports custom implementations
|
|
122
|
+
to pause, resume or cancel the processing of further drawing commands. By the
|
|
123
|
+
default
|
|
121
124
|
[`AxiDrawControl`](https://docs.thi.ng/umbrella/axidraw/classes/AxiDrawControl.html)
|
|
122
125
|
is used as default implementation.
|
|
123
126
|
|
|
@@ -129,6 +132,20 @@ when resuming it will be sent down again (if it was originally down). Draw
|
|
|
129
132
|
commands are only sent to the machine if no control is provided at all or if the
|
|
130
133
|
control is in the "continue" state.
|
|
131
134
|
|
|
135
|
+
### Metrics
|
|
136
|
+
|
|
137
|
+
The [`draw()`](https://docs.thi.ng/umbrella/axidraw/classes/AxiDraw.html#draw)
|
|
138
|
+
function also records several
|
|
139
|
+
[metrics](https://docs.thi.ng/umbrella/axidraw/interfaces/Metrics.html), useful
|
|
140
|
+
for further analysis (or to identify optimizations) of the plotting process.
|
|
141
|
+
These metrics include:
|
|
142
|
+
|
|
143
|
+
- total duration
|
|
144
|
+
- total distance traveled
|
|
145
|
+
- draw distance (i.e. only whilst pen is down)
|
|
146
|
+
- number of pen up/down commands (i.e. to consider servo lifespan)
|
|
147
|
+
- total number of commands
|
|
148
|
+
|
|
132
149
|
## Status
|
|
133
150
|
|
|
134
151
|
**ALPHA** - bleeding edge / work-in-progress
|
|
@@ -147,7 +164,7 @@ For Node.js REPL:
|
|
|
147
164
|
const axidraw = await import("@thi.ng/axidraw");
|
|
148
165
|
```
|
|
149
166
|
|
|
150
|
-
Package sizes (brotli'd, pre-treeshake): ESM:
|
|
167
|
+
Package sizes (brotli'd, pre-treeshake): ESM: 3.96 KB
|
|
151
168
|
|
|
152
169
|
## Dependencies
|
|
153
170
|
|
|
@@ -240,8 +257,12 @@ import { map, range } from "@thi.ng/transducers";
|
|
|
240
257
|
|
|
241
258
|
Other selected toots/tweets:
|
|
242
259
|
|
|
260
|
+
- https://mastodon.thi.ng/@toxi/109490174709589253
|
|
261
|
+
- https://mastodon.thi.ng/@toxi/109473655772673067
|
|
243
262
|
- https://mastodon.thi.ng/@toxi/109474947869078797
|
|
244
263
|
- https://mastodon.thi.ng/@toxi/109483553358349473
|
|
264
|
+
- https://mastodon.thi.ng/@toxi/109570540391689321
|
|
265
|
+
- https://mastodon.thi.ng/@toxi/109586780630493994
|
|
245
266
|
- more to come...
|
|
246
267
|
|
|
247
268
|
## Authors
|
package/api.d.ts
CHANGED
|
@@ -31,6 +31,33 @@ export type DrawCommand = StartCommand | StopCommand | HomeCommand | ResetComman
|
|
|
31
31
|
* Global plotter drawing configuration. Also see {@link DEFAULT_OPTS}.
|
|
32
32
|
*/
|
|
33
33
|
export interface AxiDrawOpts {
|
|
34
|
+
/**
|
|
35
|
+
* Serial connection to use (only used for testing/dev purposes, otherwise
|
|
36
|
+
* leave default).
|
|
37
|
+
*
|
|
38
|
+
* @defaultValue {@link SERIAL_PORT}
|
|
39
|
+
*/
|
|
40
|
+
serial: SerialConnection;
|
|
41
|
+
/**
|
|
42
|
+
* Logger instance for outputting draw commands, state info and metrics.
|
|
43
|
+
*/
|
|
44
|
+
logger: ILogger;
|
|
45
|
+
/**
|
|
46
|
+
* Optional implementation to pause, resume or cancel the processing of
|
|
47
|
+
* drawing commands (see {@link AxiDrawControl} for default impl).
|
|
48
|
+
*
|
|
49
|
+
* @remarks
|
|
50
|
+
* If a control is provided, it will be checked prior to processing each
|
|
51
|
+
* individual command. Drawing will be paused if the control state is in
|
|
52
|
+
* {@link AxiDrawState.PAUSE} state and the control will be rechecked every
|
|
53
|
+
* {@link AxiDrawOpts.refresh} milliseconds for updates. In paused state,
|
|
54
|
+
* the pen will be automatically lifted (if it wasn't already) and when
|
|
55
|
+
* resuming it will be sent down again (if it was originally down).
|
|
56
|
+
*
|
|
57
|
+
* Draw commands are only sent to the machine if no control is provided at
|
|
58
|
+
* all or if the control is in the {@link AxiDrawState.CONTINUE} state.
|
|
59
|
+
*/
|
|
60
|
+
control?: IDeref<AxiDrawState>;
|
|
34
61
|
/**
|
|
35
62
|
* Conversion factor from geometry worldspace units to inches.
|
|
36
63
|
* Default units are millimeters.
|
|
@@ -92,26 +119,6 @@ export interface AxiDrawOpts {
|
|
|
92
119
|
* @defaultValue `[UP, HOME, OFF]`
|
|
93
120
|
*/
|
|
94
121
|
stop: DrawCommand[];
|
|
95
|
-
/**
|
|
96
|
-
* Logger instance
|
|
97
|
-
*/
|
|
98
|
-
logger: ILogger;
|
|
99
|
-
/**
|
|
100
|
-
* Optional implementation to pause, resume or cancel the processing of
|
|
101
|
-
* drawing commands (see {@link AxiDrawControl} for default impl).
|
|
102
|
-
*
|
|
103
|
-
* @remarks
|
|
104
|
-
* If a control is provided, it will be checked prior to processing each
|
|
105
|
-
* individual command. Drawing will be paused if the control state is in
|
|
106
|
-
* {@link AxiDrawState.PAUSE} state and the control will be rechecked every
|
|
107
|
-
* {@link AxiDrawOpts.refresh} milliseconds for updates. In paused state,
|
|
108
|
-
* the pen will be automatically lifted (if it wasn't already) and when
|
|
109
|
-
* resuming it will be sent down again (if it was originally down).
|
|
110
|
-
*
|
|
111
|
-
* Draw commands are only sent to the machine if no control is provided at
|
|
112
|
-
* all or if the control is in the {@link AxiDrawState.CONTINUE} state.
|
|
113
|
-
*/
|
|
114
|
-
control?: IDeref<AxiDrawState>;
|
|
115
122
|
/**
|
|
116
123
|
* Refresh interval for checking the control FSM in paused state.
|
|
117
124
|
*
|
|
@@ -184,4 +191,59 @@ export interface PolylineOpts {
|
|
|
184
191
|
*/
|
|
185
192
|
onlyGeo: boolean;
|
|
186
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Metrics returned by {@link AxiDraw.draw}.
|
|
196
|
+
*/
|
|
197
|
+
export interface Metrics {
|
|
198
|
+
/**
|
|
199
|
+
* Total number of milliseconds taken for drawing all given commands (incl.
|
|
200
|
+
* any pauses caused by the control)
|
|
201
|
+
*/
|
|
202
|
+
duration: number;
|
|
203
|
+
/**
|
|
204
|
+
* Total draw distance, i.e. distance traveled whilst pen down (in original
|
|
205
|
+
* user units, see {@link AxiDrawOpts.unitsPerInch}).
|
|
206
|
+
*/
|
|
207
|
+
drawDist: number;
|
|
208
|
+
/**
|
|
209
|
+
* Total traveled, incl. any movements without drawing (in original user
|
|
210
|
+
* units, see {@link AxiDrawOpts.unitsPerInch}).
|
|
211
|
+
*/
|
|
212
|
+
totalDist: number;
|
|
213
|
+
/**
|
|
214
|
+
* Number of pen up/down commands (useful for measuring servo lifespan).
|
|
215
|
+
*/
|
|
216
|
+
penCommands: number;
|
|
217
|
+
/**
|
|
218
|
+
* Total number of {@link DrawCommand}s processed.
|
|
219
|
+
*/
|
|
220
|
+
commands: number;
|
|
221
|
+
}
|
|
222
|
+
export interface SerialConnection {
|
|
223
|
+
/**
|
|
224
|
+
* Async function. Returns a list of available serial ports. The arg given
|
|
225
|
+
* is the path requested by the user when calling {@link AxiDraw.connect}.
|
|
226
|
+
*
|
|
227
|
+
* @param path
|
|
228
|
+
*/
|
|
229
|
+
list(path: string): Promise<{
|
|
230
|
+
path: string;
|
|
231
|
+
}[]>;
|
|
232
|
+
/**
|
|
233
|
+
* Returns an actual serial port (or mock) instance, is given the first
|
|
234
|
+
* matching path in array returned by {@link SerialConnection.list}.
|
|
235
|
+
*
|
|
236
|
+
* @param path
|
|
237
|
+
* @param baudRate
|
|
238
|
+
*/
|
|
239
|
+
ctor(path: string, baudRate: number): ISerial;
|
|
240
|
+
}
|
|
241
|
+
export interface ISerial {
|
|
242
|
+
/**
|
|
243
|
+
* Writes given string to the port.
|
|
244
|
+
*
|
|
245
|
+
* @param msg
|
|
246
|
+
*/
|
|
247
|
+
write(msg: string): void;
|
|
248
|
+
}
|
|
187
249
|
//# sourceMappingURL=api.d.ts.map
|
package/axidraw.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { IReset } from "@thi.ng/api";
|
|
2
2
|
import { ReadonlyVec, Vec } from "@thi.ng/vectors";
|
|
3
|
-
import {
|
|
4
|
-
import { AxiDrawOpts, DrawCommand } from "./api.js";
|
|
3
|
+
import { AxiDrawOpts, DrawCommand, ISerial, Metrics } from "./api.js";
|
|
5
4
|
export declare const DEFAULT_OPTS: AxiDrawOpts;
|
|
6
5
|
export declare class AxiDraw implements IReset {
|
|
7
|
-
serial:
|
|
6
|
+
serial: ISerial;
|
|
8
7
|
opts: AxiDrawOpts;
|
|
9
8
|
isConnected: boolean;
|
|
10
9
|
isPenDown: boolean;
|
|
@@ -30,9 +29,9 @@ export declare class AxiDraw implements IReset {
|
|
|
30
29
|
* Async function. Converts sequence of {@link DrawCommand}s into actual EBB
|
|
31
30
|
* commands and sends them via configured serial port to the AxiDraw. If
|
|
32
31
|
* `wrap` is enabled (default), the given commands will be automatically
|
|
33
|
-
* wrapped with start/stop commands via {@link complete}. Returns
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
* wrapped with start/stop commands via {@link complete}. Returns object of
|
|
33
|
+
* collected {@link Metrics}. If `showMetrics` is enabled (default), the
|
|
34
|
+
* metrics will also be written to the configured logger.
|
|
36
35
|
*
|
|
37
36
|
* @remarks
|
|
38
37
|
* This function is async and if using `await` will only return once all
|
|
@@ -57,22 +56,33 @@ export declare class AxiDraw implements IReset {
|
|
|
57
56
|
*
|
|
58
57
|
* @param commands
|
|
59
58
|
* @param wrap
|
|
59
|
+
* @param showMetrics
|
|
60
60
|
*/
|
|
61
|
-
draw(commands: Iterable<DrawCommand>, wrap?: boolean): Promise<
|
|
61
|
+
draw(commands: Iterable<DrawCommand>, wrap?: boolean, showMetrics?: boolean): Promise<Metrics>;
|
|
62
62
|
/**
|
|
63
63
|
* Syntax sugar for drawing a **single** command only, otherwise same as
|
|
64
64
|
* {@link AxiDraw.draw}.
|
|
65
65
|
*
|
|
66
66
|
* @param cmd
|
|
67
67
|
*/
|
|
68
|
-
draw1(cmd: DrawCommand): Promise<
|
|
68
|
+
draw1(cmd: DrawCommand): Promise<Metrics>;
|
|
69
69
|
motorsOn(): void;
|
|
70
70
|
motorsOff(): void;
|
|
71
71
|
penConfig(down?: number, up?: number): void;
|
|
72
72
|
penUp(delay?: number, level?: number): number;
|
|
73
73
|
penDown(delay?: number, level?: number): number;
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Sends a "moveto" command (absolute coords). Returns tuple of `[duration,
|
|
76
|
+
* distance]` (distance in original/configured units)
|
|
77
|
+
*
|
|
78
|
+
* @param p
|
|
79
|
+
* @param tempo
|
|
80
|
+
*/
|
|
81
|
+
moveTo(p: ReadonlyVec, tempo?: number): number[];
|
|
82
|
+
/**
|
|
83
|
+
* Syntax sugar for {@link AxiDraw.moveTo}([0, 0]).
|
|
84
|
+
*/
|
|
85
|
+
home(): number[];
|
|
76
86
|
protected onSignal(): Promise<void>;
|
|
77
87
|
protected send(msg: string): void;
|
|
78
88
|
/**
|
package/axidraw.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { isString } from "@thi.ng/checks";
|
|
2
2
|
import { delayed } from "@thi.ng/compose";
|
|
3
|
+
import { formatDuration } from "@thi.ng/date";
|
|
3
4
|
import { assert, ioerror, unsupported } from "@thi.ng/errors";
|
|
4
5
|
import { ConsoleLogger } from "@thi.ng/logger";
|
|
5
|
-
import { abs2, mulN2, set2, sub2, zero, ZERO2, } from "@thi.ng/vectors";
|
|
6
|
-
import { SerialPort } from "serialport";
|
|
6
|
+
import { abs2, mag, mulN2, set2, sub2, zero, ZERO2, } from "@thi.ng/vectors";
|
|
7
7
|
import { AxiDrawState, HOME, OFF, ON, PEN, UP, } from "./api.js";
|
|
8
8
|
import { AxiDrawControl } from "./control.js";
|
|
9
9
|
import { complete } from "./polyline.js";
|
|
10
|
+
import { SERIAL_PORT } from "./serial.js";
|
|
10
11
|
export const DEFAULT_OPTS = {
|
|
12
|
+
serial: SERIAL_PORT,
|
|
11
13
|
logger: new ConsoleLogger("axidraw"),
|
|
12
14
|
control: new AxiDrawControl(),
|
|
13
15
|
refresh: 1000,
|
|
@@ -51,14 +53,11 @@ export class AxiDraw {
|
|
|
51
53
|
*/
|
|
52
54
|
async connect(path = "/dev/tty.usbmodem") {
|
|
53
55
|
const isStr = isString(path);
|
|
54
|
-
for (let port of await
|
|
56
|
+
for (let port of await this.opts.serial.list(path.toString())) {
|
|
55
57
|
if ((isStr && port.path.startsWith(path)) ||
|
|
56
58
|
(!isStr && path.test(port.path))) {
|
|
57
59
|
this.opts.logger.info(`using device: ${port.path}...`);
|
|
58
|
-
this.serial =
|
|
59
|
-
path: port.path,
|
|
60
|
-
baudRate: 38400,
|
|
61
|
-
});
|
|
60
|
+
this.serial = this.opts.serial.ctor(port.path, 38400);
|
|
62
61
|
this.isConnected = true;
|
|
63
62
|
if (this.opts.sigint) {
|
|
64
63
|
this.opts.logger.debug("installing signal handler...");
|
|
@@ -73,9 +72,9 @@ export class AxiDraw {
|
|
|
73
72
|
* Async function. Converts sequence of {@link DrawCommand}s into actual EBB
|
|
74
73
|
* commands and sends them via configured serial port to the AxiDraw. If
|
|
75
74
|
* `wrap` is enabled (default), the given commands will be automatically
|
|
76
|
-
* wrapped with start/stop commands via {@link complete}. Returns
|
|
77
|
-
*
|
|
78
|
-
*
|
|
75
|
+
* wrapped with start/stop commands via {@link complete}. Returns object of
|
|
76
|
+
* collected {@link Metrics}. If `showMetrics` is enabled (default), the
|
|
77
|
+
* metrics will also be written to the configured logger.
|
|
79
78
|
*
|
|
80
79
|
* @remarks
|
|
81
80
|
* This function is async and if using `await` will only return once all
|
|
@@ -100,12 +99,23 @@ export class AxiDraw {
|
|
|
100
99
|
*
|
|
101
100
|
* @param commands
|
|
102
101
|
* @param wrap
|
|
102
|
+
* @param showMetrics
|
|
103
103
|
*/
|
|
104
|
-
async draw(commands, wrap = true) {
|
|
104
|
+
async draw(commands, wrap = true, showMetrics = true) {
|
|
105
105
|
assert(this.isConnected, "AxiDraw not yet connected, need to call .connect() first");
|
|
106
106
|
let t0 = Date.now();
|
|
107
|
+
let numCommands = 0;
|
|
108
|
+
let penCommands = 0;
|
|
109
|
+
let totalDist = 0;
|
|
110
|
+
let drawDist = 0;
|
|
111
|
+
const $recordDist = (dist) => {
|
|
112
|
+
totalDist += dist;
|
|
113
|
+
if (this.isPenDown)
|
|
114
|
+
drawDist += dist;
|
|
115
|
+
};
|
|
107
116
|
const { control, logger, preDelay, refresh } = this.opts;
|
|
108
117
|
for (let $cmd of wrap ? complete(commands) : commands) {
|
|
118
|
+
numCommands++;
|
|
109
119
|
if (control) {
|
|
110
120
|
let state = control.deref();
|
|
111
121
|
if (state === AxiDrawState.PAUSE) {
|
|
@@ -126,13 +136,20 @@ export class AxiDraw {
|
|
|
126
136
|
}
|
|
127
137
|
const [cmd, a, b] = $cmd;
|
|
128
138
|
let wait = -1;
|
|
139
|
+
let dist;
|
|
129
140
|
switch (cmd) {
|
|
130
141
|
case "start":
|
|
131
|
-
case "stop":
|
|
132
|
-
await this.draw(this.opts[cmd], false);
|
|
142
|
+
case "stop": {
|
|
143
|
+
const metrics = await this.draw(this.opts[cmd], false, false);
|
|
144
|
+
numCommands += metrics.commands;
|
|
145
|
+
penCommands += metrics.penCommands;
|
|
146
|
+
totalDist += metrics.totalDist;
|
|
147
|
+
drawDist += metrics.drawDist;
|
|
133
148
|
break;
|
|
149
|
+
}
|
|
134
150
|
case "home":
|
|
135
|
-
wait = this.home();
|
|
151
|
+
[wait, dist] = this.home();
|
|
152
|
+
$recordDist(dist);
|
|
136
153
|
break;
|
|
137
154
|
case "reset":
|
|
138
155
|
this.reset();
|
|
@@ -148,15 +165,18 @@ export class AxiDraw {
|
|
|
148
165
|
break;
|
|
149
166
|
case "u":
|
|
150
167
|
wait = this.penUp(a, b);
|
|
168
|
+
penCommands++;
|
|
151
169
|
break;
|
|
152
170
|
case "d":
|
|
153
171
|
wait = this.penDown(a, b);
|
|
172
|
+
penCommands++;
|
|
154
173
|
break;
|
|
155
174
|
case "w":
|
|
156
175
|
wait = a;
|
|
157
176
|
break;
|
|
158
177
|
case "m":
|
|
159
|
-
wait = this.moveTo(a, b);
|
|
178
|
+
[wait, dist] = this.moveTo(a, b);
|
|
179
|
+
$recordDist(dist);
|
|
160
180
|
break;
|
|
161
181
|
default:
|
|
162
182
|
unsupported(`unknown command: ${$cmd}`);
|
|
@@ -167,7 +187,21 @@ export class AxiDraw {
|
|
|
167
187
|
await delayed(0, wait);
|
|
168
188
|
}
|
|
169
189
|
}
|
|
170
|
-
|
|
190
|
+
const duration = Date.now() - t0;
|
|
191
|
+
if (showMetrics) {
|
|
192
|
+
logger.info(`total duration : ${formatDuration(duration)}`);
|
|
193
|
+
logger.info(`total commands : ${numCommands}`);
|
|
194
|
+
logger.info(`pen up/downs : ${penCommands}`);
|
|
195
|
+
logger.info(`total distance : ${totalDist.toFixed(2)}`);
|
|
196
|
+
logger.info(`draw distance : ${drawDist.toFixed(2)}`);
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
duration,
|
|
200
|
+
drawDist,
|
|
201
|
+
totalDist,
|
|
202
|
+
penCommands,
|
|
203
|
+
commands: numCommands,
|
|
204
|
+
};
|
|
171
205
|
}
|
|
172
206
|
/**
|
|
173
207
|
* Syntax sugar for drawing a **single** command only, otherwise same as
|
|
@@ -209,6 +243,13 @@ export class AxiDraw {
|
|
|
209
243
|
this.isPenDown = true;
|
|
210
244
|
return delay;
|
|
211
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* Sends a "moveto" command (absolute coords). Returns tuple of `[duration,
|
|
248
|
+
* distance]` (distance in original/configured units)
|
|
249
|
+
*
|
|
250
|
+
* @param p
|
|
251
|
+
* @param tempo
|
|
252
|
+
*/
|
|
212
253
|
moveTo(p, tempo = 1) {
|
|
213
254
|
const { pos, targetPos, opts } = this;
|
|
214
255
|
// apply scale factor: worldspace units -> motor steps
|
|
@@ -218,8 +259,11 @@ export class AxiDraw {
|
|
|
218
259
|
const maxAxis = Math.max(...abs2([], delta));
|
|
219
260
|
const duration = (1000 * maxAxis) / (opts.speed * tempo);
|
|
220
261
|
this.send(`XM,${duration | 0},${delta[0] | 0},${delta[1] | 0}\r`);
|
|
221
|
-
return duration;
|
|
262
|
+
return [duration, (mag(delta) * opts.unitsPerInch) / opts.stepsPerInch];
|
|
222
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* Syntax sugar for {@link AxiDraw.moveTo}([0, 0]).
|
|
266
|
+
*/
|
|
223
267
|
home() {
|
|
224
268
|
return this.moveTo(ZERO2);
|
|
225
269
|
}
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/axidraw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Minimal AxiDraw plotter/drawing machine controller for Node.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"@thi.ng/compose": "^2.1.23",
|
|
40
40
|
"@thi.ng/errors": "^2.2.7",
|
|
41
41
|
"@thi.ng/logger": "^1.4.6",
|
|
42
|
-
"@thi.ng/vectors": "^7.5.
|
|
42
|
+
"@thi.ng/vectors": "^7.5.30",
|
|
43
43
|
"serialport": "^10.5.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
"geometry",
|
|
59
59
|
"io",
|
|
60
60
|
"logger",
|
|
61
|
-
"
|
|
61
|
+
"nodejs",
|
|
62
|
+
"no-browser",
|
|
62
63
|
"penplotter",
|
|
63
64
|
"polyline",
|
|
64
65
|
"serialport",
|
|
@@ -89,11 +90,17 @@
|
|
|
89
90
|
},
|
|
90
91
|
"./polyline": {
|
|
91
92
|
"default": "./polyline.js"
|
|
93
|
+
},
|
|
94
|
+
"./serial": {
|
|
95
|
+
"default": "./serial.js"
|
|
96
|
+
},
|
|
97
|
+
"./utils": {
|
|
98
|
+
"default": "./utils.js"
|
|
92
99
|
}
|
|
93
100
|
},
|
|
94
101
|
"thi.ng": {
|
|
95
102
|
"status": "alpha",
|
|
96
103
|
"year": 2022
|
|
97
104
|
},
|
|
98
|
-
"gitHead": "
|
|
105
|
+
"gitHead": "28bb74c67217a352d673b6efdab234921d4a370e\n"
|
|
99
106
|
}
|
package/serial.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ISerial, SerialConnection } from "./api.js";
|
|
2
|
+
/**
|
|
3
|
+
* Default connection using the actual serial port.
|
|
4
|
+
*/
|
|
5
|
+
export declare const SERIAL_PORT: SerialConnection;
|
|
6
|
+
/**
|
|
7
|
+
* Mock serial connection which uses a in-memory logger to record all draw
|
|
8
|
+
* commands. Only intended for testing/dev purposes.
|
|
9
|
+
*/
|
|
10
|
+
export declare const MOCK_SERIAL: SerialConnection;
|
|
11
|
+
/**
|
|
12
|
+
* Minimal mock "serial port" implementation for testing purposes. Records all
|
|
13
|
+
* sent messages into an internal string array for later analysis.
|
|
14
|
+
*/
|
|
15
|
+
export declare class MockSerial implements ISerial {
|
|
16
|
+
sent: string[];
|
|
17
|
+
/**
|
|
18
|
+
* Clears internal log of "sent" message.
|
|
19
|
+
*/
|
|
20
|
+
clear(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Appends
|
|
23
|
+
* @param msg
|
|
24
|
+
*/
|
|
25
|
+
write(msg: string): void;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=serial.d.ts.map
|
package/serial.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { SerialPort } from "serialport";
|
|
2
|
+
/**
|
|
3
|
+
* Default connection using the actual serial port.
|
|
4
|
+
*/
|
|
5
|
+
export const SERIAL_PORT = {
|
|
6
|
+
list: () => SerialPort.list(),
|
|
7
|
+
ctor: (path, baudRate) => new SerialPort({ path, baudRate }),
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Mock serial connection which uses a in-memory logger to record all draw
|
|
11
|
+
* commands. Only intended for testing/dev purposes.
|
|
12
|
+
*/
|
|
13
|
+
export const MOCK_SERIAL = {
|
|
14
|
+
list: async (path) => [{ path }],
|
|
15
|
+
ctor: () => new MockSerial(),
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Minimal mock "serial port" implementation for testing purposes. Records all
|
|
19
|
+
* sent messages into an internal string array for later analysis.
|
|
20
|
+
*/
|
|
21
|
+
export class MockSerial {
|
|
22
|
+
constructor() {
|
|
23
|
+
this.sent = [];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Clears internal log of "sent" message.
|
|
27
|
+
*/
|
|
28
|
+
clear() {
|
|
29
|
+
this.sent = [];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Appends
|
|
33
|
+
* @param msg
|
|
34
|
+
*/
|
|
35
|
+
write(msg) {
|
|
36
|
+
this.sent.push(msg);
|
|
37
|
+
}
|
|
38
|
+
}
|
package/utils.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ReadonlyVec } from "@thi.ng/vectors";
|
|
2
|
+
import type { DrawCommand } from "./api.js";
|
|
3
|
+
/**
|
|
4
|
+
* Generates a {@link DrawCommand} sequence to draw a registration mark
|
|
5
|
+
* (crosshair + circle) centered around `pos`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* axi.draw(registrationMark([20, 20]))
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* @param pos
|
|
13
|
+
* @param size
|
|
14
|
+
* @param r
|
|
15
|
+
*/
|
|
16
|
+
export declare const registrationMark: ([x, y]: ReadonlyVec, size?: number, r?: number) => DrawCommand[];
|
|
17
|
+
//# sourceMappingURL=utils.d.ts.map
|
package/utils.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { map, normRange } from "@thi.ng/transducers";
|
|
2
|
+
import { cartesian2 } from "@thi.ng/vectors";
|
|
3
|
+
/**
|
|
4
|
+
* Generates a {@link DrawCommand} sequence to draw a registration mark
|
|
5
|
+
* (crosshair + circle) centered around `pos`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* axi.draw(registrationMark([20, 20]))
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* @param pos
|
|
13
|
+
* @param size
|
|
14
|
+
* @param r
|
|
15
|
+
*/
|
|
16
|
+
export const registrationMark = ([x, y], size = 5, r = size * 0.75) => [
|
|
17
|
+
// crosshair
|
|
18
|
+
// horizontal
|
|
19
|
+
["m", [x - size, y]],
|
|
20
|
+
["d"],
|
|
21
|
+
["m", [x + size, y]],
|
|
22
|
+
["u"],
|
|
23
|
+
// vertical
|
|
24
|
+
["m", [x, y - size]],
|
|
25
|
+
["d"],
|
|
26
|
+
["m", [x, y + size]],
|
|
27
|
+
["u"],
|
|
28
|
+
// circle
|
|
29
|
+
["m", [x + r, y]],
|
|
30
|
+
["d"],
|
|
31
|
+
...map((t) => ["m", cartesian2([], [r, t * Math.PI * 2], [x, y])], normRange(40)),
|
|
32
|
+
["u"],
|
|
33
|
+
["m", [x, y]],
|
|
34
|
+
];
|