@thi.ng/axidraw 0.2.2 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2022-12-16T12:52:25Z
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)
@@ -29,7 +30,7 @@ This project is part of the
29
30
 
30
31
  ## About
31
32
 
32
- Minimal AxiDraw plotter/drawing machine controller for Node.js
33
+ Minimal AxiDraw plotter/drawing machine controller for Node.js.
33
34
 
34
35
  This package provides a super-lightweight alternative to control an [AxiDraw
35
36
  plotter](https://axidraw.com/) directly from Node.js, using a small custom set
@@ -115,9 +116,11 @@ only aimed at Node.js though...
115
116
 
116
117
  ### Draw control
117
118
 
118
- The main draw function provided by this package is async and can supports custom
119
- implementations to pause, resume or cancel the processing of further drawing
120
- commands. By the default
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
@@ -141,25 +158,18 @@ control is in the "continue" state.
141
158
  yarn add @thi.ng/axidraw
142
159
  ```
143
160
 
144
- ES module import:
145
-
146
- ```html
147
- <script type="module" src="https://cdn.skypack.dev/@thi.ng/axidraw"></script>
148
- ```
149
-
150
- [Skypack documentation](https://docs.skypack.dev/)
151
-
152
161
  For Node.js REPL:
153
162
 
154
163
  ```js
155
164
  const axidraw = await import("@thi.ng/axidraw");
156
165
  ```
157
166
 
158
- Package sizes (brotli'd, pre-treeshake): ESM: 1.67 KB
167
+ Package sizes (brotli'd, pre-treeshake): ESM: 3.96 KB
159
168
 
160
169
  ## Dependencies
161
170
 
162
171
  - [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/develop/packages/api)
172
+ - [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/develop/packages/checks)
163
173
  - [@thi.ng/compose](https://github.com/thi-ng/umbrella/tree/develop/packages/compose)
164
174
  - [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/develop/packages/errors)
165
175
  - [@thi.ng/logger](https://github.com/thi-ng/umbrella/tree/develop/packages/logger)
@@ -247,8 +257,12 @@ import { map, range } from "@thi.ng/transducers";
247
257
 
248
258
  Other selected toots/tweets:
249
259
 
260
+ - https://mastodon.thi.ng/@toxi/109490174709589253
261
+ - https://mastodon.thi.ng/@toxi/109473655772673067
250
262
  - https://mastodon.thi.ng/@toxi/109474947869078797
251
263
  - https://mastodon.thi.ng/@toxi/109483553358349473
264
+ - https://mastodon.thi.ng/@toxi/109570540391689321
265
+ - https://mastodon.thi.ng/@toxi/109586780630493994
252
266
  - more to come...
253
267
 
254
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 { SerialPort } from "serialport";
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: SerialPort;
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 total
34
- * number of milliseconds taken for drawing (incl. any pauses caused by the
35
- * control).
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<number>;
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<number>;
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
- moveTo(p: ReadonlyVec, tempo?: number): number;
75
- home(): number;
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 SerialPort.list()) {
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 = new SerialPort({
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 total
77
- * number of milliseconds taken for drawing (incl. any pauses caused by the
78
- * control).
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
- return Date.now() - t0;
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
@@ -2,4 +2,6 @@ export * from "./api.js";
2
2
  export * from "./axidraw.js";
3
3
  export * from "./control.js";
4
4
  export * from "./polyline.js";
5
+ export * from "./serial.js";
6
+ export * from "./utils.js";
5
7
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -2,3 +2,5 @@ export * from "./api.js";
2
2
  export * from "./axidraw.js";
3
3
  export * from "./control.js";
4
4
  export * from "./polyline.js";
5
+ export * from "./serial.js";
6
+ export * from "./utils.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/axidraw",
3
- "version": "0.2.2",
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",
@@ -34,16 +34,17 @@
34
34
  "test": "testament test"
35
35
  },
36
36
  "dependencies": {
37
- "@thi.ng/api": "^8.6.1",
38
- "@thi.ng/compose": "^2.1.22",
39
- "@thi.ng/errors": "^2.2.6",
40
- "@thi.ng/logger": "^1.4.5",
41
- "@thi.ng/vectors": "^7.5.28",
37
+ "@thi.ng/api": "^8.6.2",
38
+ "@thi.ng/checks": "^3.3.6",
39
+ "@thi.ng/compose": "^2.1.23",
40
+ "@thi.ng/errors": "^2.2.7",
41
+ "@thi.ng/logger": "^1.4.6",
42
+ "@thi.ng/vectors": "^7.5.30",
42
43
  "serialport": "^10.5.0"
43
44
  },
44
45
  "devDependencies": {
45
46
  "@microsoft/api-extractor": "^7.33.7",
46
- "@thi.ng/testament": "^0.3.7",
47
+ "@thi.ng/testament": "^0.3.8",
47
48
  "rimraf": "^3.0.2",
48
49
  "tools": "^0.0.1",
49
50
  "typedoc": "^0.23.22",
@@ -57,7 +58,8 @@
57
58
  "geometry",
58
59
  "io",
59
60
  "logger",
60
- "node",
61
+ "nodejs",
62
+ "no-browser",
61
63
  "penplotter",
62
64
  "polyline",
63
65
  "serialport",
@@ -88,11 +90,17 @@
88
90
  },
89
91
  "./polyline": {
90
92
  "default": "./polyline.js"
93
+ },
94
+ "./serial": {
95
+ "default": "./serial.js"
96
+ },
97
+ "./utils": {
98
+ "default": "./utils.js"
91
99
  }
92
100
  },
93
101
  "thi.ng": {
94
102
  "status": "alpha",
95
103
  "year": 2022
96
104
  },
97
- "gitHead": "7b2af448da8a63fb21704a79cc4cdf1f3d7d7a64\n"
105
+ "gitHead": "28bb74c67217a352d673b6efdab234921d4a370e\n"
98
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
+ ];