@thi.ng/axidraw 0.2.3 → 0.4.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-22T21:47:07Z
3
+ - **Last updated**: 2023-01-10T15:20:18Z
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,34 @@ 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.4.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/axidraw@0.4.0) (2023-01-10)
13
+
14
+ #### 🚀 Features
15
+
16
+ - also send "reset" cmd in .reset() ([30fe365](https://github.com/thi-ng/umbrella/commit/30fe365))
17
+
18
+ ## [0.3.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/axidraw@0.3.0) (2022-12-29)
19
+
20
+ #### 🚀 Features
21
+
22
+ - add draw metrics ([0ff015c](https://github.com/thi-ng/umbrella/commit/0ff015c))
23
+ - add Metrics interface
24
+ - update AxiDraw.draw() to record metrics
25
+ - update .moveTo() to return delay & distance
26
+ - add serial port abstraction & impls ([c774da1](https://github.com/thi-ng/umbrella/commit/c774da1))
27
+ - add SerialConnection adapter
28
+ - add `SERIAL_PORT` default impl (actual serial port)
29
+ - add `MOCK_SERIAL` & MockSerial impl for testing
30
+ - update AxiDrawOpts & AxiDraw to use adapter only
31
+ - add registrationMark() util, fix imports ([e05e99d](https://github.com/thi-ng/umbrella/commit/e05e99d))
32
+ - add Metrics.penCommands, fix nested metrics handling ([a7149cd](https://github.com/thi-ng/umbrella/commit/a7149cd))
33
+ - add counter for pen up/down commands
34
+ - add start/stop cmd metrics to current tally
35
+
36
+ #### ♻️ Refactoring
37
+
38
+ - update "no-browser" pkg handling ([0e84f1b](https://github.com/thi-ng/umbrella/commit/0e84f1b))
39
+
12
40
  ## [0.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/axidraw@0.2.0) (2022-12-10)
13
41
 
14
42
  #### 🚀 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 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
@@ -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: 1.67 KB
167
+ Package sizes (brotli'd, pre-treeshake): ESM: 3.97 KB
151
168
 
152
169
  ## Dependencies
153
170
 
@@ -157,7 +174,7 @@ Package sizes (brotli'd, pre-treeshake): ESM: 1.67 KB
157
174
  - [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/develop/packages/errors)
158
175
  - [@thi.ng/logger](https://github.com/thi-ng/umbrella/tree/develop/packages/logger)
159
176
  - [@thi.ng/vectors](https://github.com/thi-ng/umbrella/tree/develop/packages/vectors)
160
- - [serialport](https://github.com/thi-ng/umbrella/tree/develop/packages/undefined)
177
+ - [serialport](git://github.com/serialport/node-serialport.git)
161
178
 
162
179
  ## API
163
180
 
@@ -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
@@ -261,4 +282,4 @@ If this project contributes to an academic publication, please cite it as:
261
282
 
262
283
  ## License
263
284
 
264
- © 2022 Karsten Schmidt // Apache License 2.0
285
+ © 2022 - 2023 Karsten Schmidt // Apache License 2.0
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,
@@ -35,6 +37,7 @@ export class AxiDraw {
35
37
  reset() {
36
38
  zero(this.pos);
37
39
  zero(this.targetPos);
40
+ this.send("R\r");
38
41
  return this;
39
42
  }
40
43
  /**
@@ -51,14 +54,11 @@ export class AxiDraw {
51
54
  */
52
55
  async connect(path = "/dev/tty.usbmodem") {
53
56
  const isStr = isString(path);
54
- for (let port of await SerialPort.list()) {
57
+ for (let port of await this.opts.serial.list(path.toString())) {
55
58
  if ((isStr && port.path.startsWith(path)) ||
56
59
  (!isStr && path.test(port.path))) {
57
60
  this.opts.logger.info(`using device: ${port.path}...`);
58
- this.serial = new SerialPort({
59
- path: port.path,
60
- baudRate: 38400,
61
- });
61
+ this.serial = this.opts.serial.ctor(port.path, 38400);
62
62
  this.isConnected = true;
63
63
  if (this.opts.sigint) {
64
64
  this.opts.logger.debug("installing signal handler...");
@@ -73,9 +73,9 @@ export class AxiDraw {
73
73
  * Async function. Converts sequence of {@link DrawCommand}s into actual EBB
74
74
  * commands and sends them via configured serial port to the AxiDraw. If
75
75
  * `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).
76
+ * wrapped with start/stop commands via {@link complete}. Returns object of
77
+ * collected {@link Metrics}. If `showMetrics` is enabled (default), the
78
+ * metrics will also be written to the configured logger.
79
79
  *
80
80
  * @remarks
81
81
  * This function is async and if using `await` will only return once all
@@ -100,12 +100,23 @@ export class AxiDraw {
100
100
  *
101
101
  * @param commands
102
102
  * @param wrap
103
+ * @param showMetrics
103
104
  */
104
- async draw(commands, wrap = true) {
105
+ async draw(commands, wrap = true, showMetrics = true) {
105
106
  assert(this.isConnected, "AxiDraw not yet connected, need to call .connect() first");
106
107
  let t0 = Date.now();
108
+ let numCommands = 0;
109
+ let penCommands = 0;
110
+ let totalDist = 0;
111
+ let drawDist = 0;
112
+ const $recordDist = (dist) => {
113
+ totalDist += dist;
114
+ if (this.isPenDown)
115
+ drawDist += dist;
116
+ };
107
117
  const { control, logger, preDelay, refresh } = this.opts;
108
118
  for (let $cmd of wrap ? complete(commands) : commands) {
119
+ numCommands++;
109
120
  if (control) {
110
121
  let state = control.deref();
111
122
  if (state === AxiDrawState.PAUSE) {
@@ -126,13 +137,20 @@ export class AxiDraw {
126
137
  }
127
138
  const [cmd, a, b] = $cmd;
128
139
  let wait = -1;
140
+ let dist;
129
141
  switch (cmd) {
130
142
  case "start":
131
- case "stop":
132
- await this.draw(this.opts[cmd], false);
143
+ case "stop": {
144
+ const metrics = await this.draw(this.opts[cmd], false, false);
145
+ numCommands += metrics.commands;
146
+ penCommands += metrics.penCommands;
147
+ totalDist += metrics.totalDist;
148
+ drawDist += metrics.drawDist;
133
149
  break;
150
+ }
134
151
  case "home":
135
- wait = this.home();
152
+ [wait, dist] = this.home();
153
+ $recordDist(dist);
136
154
  break;
137
155
  case "reset":
138
156
  this.reset();
@@ -148,15 +166,18 @@ export class AxiDraw {
148
166
  break;
149
167
  case "u":
150
168
  wait = this.penUp(a, b);
169
+ penCommands++;
151
170
  break;
152
171
  case "d":
153
172
  wait = this.penDown(a, b);
173
+ penCommands++;
154
174
  break;
155
175
  case "w":
156
176
  wait = a;
157
177
  break;
158
178
  case "m":
159
- wait = this.moveTo(a, b);
179
+ [wait, dist] = this.moveTo(a, b);
180
+ $recordDist(dist);
160
181
  break;
161
182
  default:
162
183
  unsupported(`unknown command: ${$cmd}`);
@@ -167,7 +188,21 @@ export class AxiDraw {
167
188
  await delayed(0, wait);
168
189
  }
169
190
  }
170
- return Date.now() - t0;
191
+ const duration = Date.now() - t0;
192
+ if (showMetrics) {
193
+ logger.info(`total duration : ${formatDuration(duration)}`);
194
+ logger.info(`total commands : ${numCommands}`);
195
+ logger.info(`pen up/downs : ${penCommands}`);
196
+ logger.info(`total distance : ${totalDist.toFixed(2)}`);
197
+ logger.info(`draw distance : ${drawDist.toFixed(2)}`);
198
+ }
199
+ return {
200
+ duration,
201
+ drawDist,
202
+ totalDist,
203
+ penCommands,
204
+ commands: numCommands,
205
+ };
171
206
  }
172
207
  /**
173
208
  * Syntax sugar for drawing a **single** command only, otherwise same as
@@ -209,6 +244,13 @@ export class AxiDraw {
209
244
  this.isPenDown = true;
210
245
  return delay;
211
246
  }
247
+ /**
248
+ * Sends a "moveto" command (absolute coords). Returns tuple of `[duration,
249
+ * distance]` (distance in original/configured units)
250
+ *
251
+ * @param p
252
+ * @param tempo
253
+ */
212
254
  moveTo(p, tempo = 1) {
213
255
  const { pos, targetPos, opts } = this;
214
256
  // apply scale factor: worldspace units -> motor steps
@@ -218,8 +260,11 @@ export class AxiDraw {
218
260
  const maxAxis = Math.max(...abs2([], delta));
219
261
  const duration = (1000 * maxAxis) / (opts.speed * tempo);
220
262
  this.send(`XM,${duration | 0},${delta[0] | 0},${delta[1] | 0}\r`);
221
- return duration;
263
+ return [duration, (mag(delta) * opts.unitsPerInch) / opts.stepsPerInch];
222
264
  }
265
+ /**
266
+ * Syntax sugar for {@link AxiDraw.moveTo}([0, 0]).
267
+ */
223
268
  home() {
224
269
  return this.moveTo(ZERO2);
225
270
  }
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.3",
3
+ "version": "0.4.0",
4
4
  "description": "Minimal AxiDraw plotter/drawing machine controller for Node.js",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -34,17 +34,17 @@
34
34
  "test": "testament test"
35
35
  },
36
36
  "dependencies": {
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.29",
37
+ "@thi.ng/api": "^8.6.3",
38
+ "@thi.ng/checks": "^3.3.7",
39
+ "@thi.ng/compose": "^2.1.24",
40
+ "@thi.ng/errors": "^2.2.8",
41
+ "@thi.ng/logger": "^1.4.7",
42
+ "@thi.ng/vectors": "^7.5.31",
43
43
  "serialport": "^10.5.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@microsoft/api-extractor": "^7.33.7",
47
- "@thi.ng/testament": "^0.3.8",
47
+ "@thi.ng/testament": "^0.3.9",
48
48
  "rimraf": "^3.0.2",
49
49
  "tools": "^0.0.1",
50
50
  "typedoc": "^0.23.22",
@@ -58,7 +58,8 @@
58
58
  "geometry",
59
59
  "io",
60
60
  "logger",
61
- "node-only",
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": "bc6f7f5e2765bb96fe64db804eaf4b2443b47fc6\n"
105
+ "gitHead": "3f0b3e2a7c82aefc7e46fb4338369836b5e1b8cf\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
+ ];