@thi.ng/axidraw 0.1.0 → 0.2.1
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 +30 -1
- package/README.md +153 -52
- package/api.d.ts +99 -13
- package/api.js +20 -0
- package/axidraw.d.ts +46 -20
- package/axidraw.js +148 -69
- package/control.d.ts +20 -0
- package/control.js +34 -0
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/package.json +24 -16
- package/polyline.d.ts +29 -0
- package/polyline.js +54 -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-16T12:52:25Z
|
|
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,35 @@ 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.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/axidraw@0.2.0) (2022-12-10)
|
|
13
|
+
|
|
14
|
+
#### 🚀 Features
|
|
15
|
+
|
|
16
|
+
- major updates/additions ([eb41c28](https://github.com/thi-ng/umbrella/commit/eb41c28))
|
|
17
|
+
- extract polyline() as standalone fn
|
|
18
|
+
- add complete() syntax sugar
|
|
19
|
+
- update UP/DOWN commands to accept opt. pen level/position
|
|
20
|
+
- add RESET command
|
|
21
|
+
- extract various draw commands into separate methods, simplify draw()
|
|
22
|
+
- update draw() w/ FSM to pause/resume/cancel processing
|
|
23
|
+
- add AxiDrawState FSM enum
|
|
24
|
+
- add AxiDrawControl class, use as default controller
|
|
25
|
+
- update AxiDrawOpts w/ new options
|
|
26
|
+
- update connect() to throw error if unsuccessful
|
|
27
|
+
- add SIGINT signal handler to handle Ctrl+C
|
|
28
|
+
- update .draw() to auto-wrap command seq ([60aaad2](https://github.com/thi-ng/umbrella/commit/60aaad2))
|
|
29
|
+
- add PolylineOpts, update polyline() ([c8a271f](https://github.com/thi-ng/umbrella/commit/c8a271f))
|
|
30
|
+
|
|
31
|
+
#### 🩹 Bug fixes
|
|
32
|
+
|
|
33
|
+
- fix polyline(), only apply custom speed for drawing ([c43b6f5](https://github.com/thi-ng/umbrella/commit/c43b6f5))
|
|
34
|
+
- update draw calls to disable cmd wrapping ([4cd5e53](https://github.com/thi-ng/umbrella/commit/4cd5e53))
|
|
35
|
+
- fix waiting for start/stop/home commands ([42bf4eb](https://github.com/thi-ng/umbrella/commit/42bf4eb))
|
|
36
|
+
|
|
37
|
+
#### ⏱ Performance improvements
|
|
38
|
+
|
|
39
|
+
- remove obsolete UP command (and delay) in polyline() ([f71c64b](https://github.com/thi-ng/umbrella/commit/f71c64b))
|
|
40
|
+
|
|
12
41
|
## [0.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/axidraw@0.1.0) (2022-12-06)
|
|
13
42
|
|
|
14
43
|
#### 🚀 Features
|
package/README.md
CHANGED
|
@@ -1,35 +1,43 @@
|
|
|
1
1
|
<!-- This file is generated - DO NOT EDIT! -->
|
|
2
2
|
|
|
3
|
-
# 
|
|
3
|
+
# 
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@thi.ng/axidraw)
|
|
6
6
|

|
|
7
|
-
[](https://mastodon.thi.ng/@toxi)
|
|
8
8
|
|
|
9
9
|
This project is part of the
|
|
10
10
|
[@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo.
|
|
11
11
|
|
|
12
12
|
- [About](#about)
|
|
13
13
|
- [Declarative vs. imperative](#declarative-vs-imperative)
|
|
14
|
-
- [
|
|
14
|
+
- [Units, limits & clipping](#units-limits--clipping)
|
|
15
|
+
- [Path planning](#path-planning)
|
|
16
|
+
- [thi.ng/geom support](#thinggeom-support)
|
|
17
|
+
- [SVG support](#svg-support)
|
|
15
18
|
- [Serial port support](#serial-port-support)
|
|
19
|
+
- [Draw control](#draw-control)
|
|
16
20
|
- [Status](#status)
|
|
17
21
|
- [Installation](#installation)
|
|
18
22
|
- [Dependencies](#dependencies)
|
|
19
23
|
- [API](#api)
|
|
24
|
+
- [Example usage](#example-usage)
|
|
25
|
+
- [Basics](#basics)
|
|
26
|
+
- [geom-axidraw example](#geom-axidraw-example)
|
|
20
27
|
- [Authors](#authors)
|
|
21
28
|
- [License](#license)
|
|
22
29
|
|
|
23
30
|
## About
|
|
24
31
|
|
|
25
|
-
Minimal AxiDraw plotter/drawing machine controller for Node.js
|
|
32
|
+
Minimal AxiDraw plotter/drawing machine controller for Node.js
|
|
26
33
|
|
|
27
34
|
This package provides a super-lightweight alternative to control an [AxiDraw
|
|
28
35
|
plotter](https://axidraw.com/) directly from Node.js, using a small custom set
|
|
29
36
|
of medium/high-level drawing commands. Structurally, these custom commands are
|
|
30
|
-
[thi.ng/hiccup
|
|
37
|
+
[thi.ng/hiccup](https://github.com/thi-ng/umbrella/blob/develop/packages/hiccup/)-like
|
|
31
38
|
S-expressions, which can be easily serialized to/from JSON and are translated to
|
|
32
|
-
[EBB commands](https://evil-mad.github.io/EggBot/ebb.html) for the
|
|
39
|
+
the native [EBB commands](https://evil-mad.github.io/EggBot/ebb.html) for the
|
|
40
|
+
plotter.
|
|
33
41
|
|
|
34
42
|
### Declarative vs. imperative
|
|
35
43
|
|
|
@@ -42,28 +50,84 @@ following the pattern of other packages in the
|
|
|
42
50
|
until the very last moment before being sent to the machine for physical
|
|
43
51
|
output...
|
|
44
52
|
|
|
45
|
-
###
|
|
46
|
-
|
|
47
|
-
This package
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
### Units, limits & clipping
|
|
54
|
+
|
|
55
|
+
This package performs **no bounds checking nor clipping** and expects all given
|
|
56
|
+
coordinates to be valid and within machine limits. Coordinates can be given in
|
|
57
|
+
any unit, but if not using millimeters (default), a conversion factor to inches
|
|
58
|
+
(`unitsPerInch`) **MUST** be provided as part of the [options
|
|
59
|
+
object](https://docs.thi.ng/umbrella/axidraw/interfaces/AxiDrawOpts.html) given
|
|
60
|
+
to the `AxiDraw` constructor. Clipping can be handled by the geom or
|
|
61
|
+
geom-axidraw packages (see below)...
|
|
62
|
+
|
|
63
|
+
### Path planning
|
|
64
|
+
|
|
65
|
+
Path planning is considered a higher level operation than what's addressed by
|
|
66
|
+
this package and is therefore out of scope. The
|
|
67
|
+
[thi.ng/geom-axidraw](https://github.com/thi-ng/umbrella/tree/develop/packages/geom-axidraw)
|
|
68
|
+
provides some configurable point & shape sorting functions, but this is an
|
|
69
|
+
interim solution and a full path/route planning facility is currently still
|
|
70
|
+
outstanding and awaiting to be ported from other projects.
|
|
71
|
+
|
|
72
|
+
### thi.ng/geom support
|
|
73
|
+
|
|
74
|
+
The [thi.ng/geom](https://github.com/thi-ng/umbrella/tree/develop/packages/geom)
|
|
75
|
+
package provides numerous shape types & operations to generate & transform
|
|
76
|
+
geometry. Additionally,
|
|
77
|
+
[thi.ng/geom-axidraw](https://github.com/thi-ng/umbrella/tree/develop/packages/geom-axidraw)
|
|
78
|
+
can act as bridge API and provides the polymorphic
|
|
79
|
+
[`asAxiDraw()`](https://docs.thi.ng/umbrella/geom-axidraw/functions/asAxiDraw.html)
|
|
80
|
+
function to convert single shapes or entire shape groups/hierarchies directly
|
|
81
|
+
into the draw commands used by this (axidraw) package. See package readme for
|
|
82
|
+
more details and examples.
|
|
83
|
+
|
|
84
|
+
### SVG support
|
|
85
|
+
|
|
86
|
+
This package does **not** provide any direct conversions from SVG or any other
|
|
87
|
+
geometry format. But again, whilst not containing a full SVG parser (at current
|
|
88
|
+
only single paths can be parsed), the family of
|
|
50
89
|
[thi.ng/geom](https://github.com/thi-ng/umbrella/tree/develop/packages/geom)
|
|
51
|
-
packages provides numerous
|
|
90
|
+
packages provides numerous shape types & operations which can be directly
|
|
52
91
|
utilized to output generated geometry together with this package...
|
|
53
92
|
|
|
54
|
-
The only built-in conversion provided is the
|
|
55
|
-
|
|
56
|
-
|
|
93
|
+
The only built-in conversion provided here is the
|
|
94
|
+
[`polyline()`](https://docs.thi.ng/umbrella/axidraw/functions/polyline.html)
|
|
95
|
+
utility function to convert an array of points (representing a polyline) to an
|
|
96
|
+
array of drawing commands (with various config options). All other conversions
|
|
97
|
+
are out of scope for this package (& for now).
|
|
57
98
|
|
|
58
99
|
### Serial port support
|
|
59
100
|
|
|
60
101
|
We're using the [serialport](https://serialport.io/) NPM package to submit data
|
|
61
|
-
directly to the drawing machine. That
|
|
102
|
+
directly to the drawing machine. That package includes native bindings for
|
|
62
103
|
Linux, MacOS and Windows.
|
|
63
104
|
|
|
64
|
-
The
|
|
65
|
-
|
|
66
|
-
|
|
105
|
+
The
|
|
106
|
+
[`AxiDraw.connect()`](https://docs.thi.ng/umbrella/axidraw/classes/AxiDraw.html#connect)
|
|
107
|
+
function (see example below) attempts to find the drawing machine by matching a
|
|
108
|
+
given regexp with available port names. The default regexp might only work on
|
|
109
|
+
Mac, but YMMV!
|
|
110
|
+
|
|
111
|
+
At some point it would also be worth looking into
|
|
112
|
+
[WebSerial](https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API)
|
|
113
|
+
support to enable plotting directly from the browser. Right now this package is
|
|
114
|
+
only aimed at Node.js though...
|
|
115
|
+
|
|
116
|
+
### Draw control
|
|
117
|
+
|
|
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
|
|
121
|
+
[`AxiDrawControl`](https://docs.thi.ng/umbrella/axidraw/classes/AxiDrawControl.html)
|
|
122
|
+
is used as default implementation.
|
|
123
|
+
|
|
124
|
+
If a control is provided, it will be checked prior to processing each individual
|
|
125
|
+
command. Drawing will be paused if the control state is in paused state and the
|
|
126
|
+
control will be rechecked every N milliseconds for updates (configurable). In
|
|
127
|
+
paused state, the pen will be automatically lifted (if it wasn't already) and
|
|
128
|
+
when resuming it will be sent down again (if it was originally down). Draw
|
|
129
|
+
commands are only sent to the machine if no control is provided at all or if the
|
|
130
|
+
control is in the "continue" state.
|
|
67
131
|
|
|
68
132
|
## Status
|
|
69
133
|
|
|
@@ -87,14 +151,11 @@ ES module import:
|
|
|
87
151
|
|
|
88
152
|
For Node.js REPL:
|
|
89
153
|
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
node --experimental-repl-await
|
|
93
|
-
|
|
94
|
-
> const axidraw = await import("@thi.ng/axidraw");
|
|
154
|
+
```js
|
|
155
|
+
const axidraw = await import("@thi.ng/axidraw");
|
|
95
156
|
```
|
|
96
157
|
|
|
97
|
-
Package sizes (brotli'd, pre-treeshake): ESM: 1.
|
|
158
|
+
Package sizes (brotli'd, pre-treeshake): ESM: 1.67 KB
|
|
98
159
|
|
|
99
160
|
## Dependencies
|
|
100
161
|
|
|
@@ -109,50 +170,90 @@ Package sizes (brotli'd, pre-treeshake): ESM: 1.01 KB
|
|
|
109
170
|
|
|
110
171
|
[Generated API docs](https://docs.thi.ng/umbrella/axidraw/)
|
|
111
172
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
173
|
+
### Example usage
|
|
174
|
+
|
|
175
|
+
#### Basics
|
|
176
|
+
|
|
177
|
+
```js tangle:export/readme-basic.js
|
|
178
|
+
import { AxiDraw, polyline } from "@thi.ng/axidraw";
|
|
115
179
|
|
|
116
180
|
(async () => {
|
|
117
181
|
|
|
118
182
|
// instantiate w/ default options (see docs for info)
|
|
119
|
-
// default paper size is DIN A4
|
|
120
183
|
const axi = new AxiDraw();
|
|
121
184
|
|
|
122
|
-
// connect to 1st serial port matching given regexp
|
|
123
|
-
|
|
185
|
+
// connect to 1st serial port matching given pre-string or regexp
|
|
186
|
+
// (the port used here is the default arg)
|
|
187
|
+
await axi.connect("/dev/tty.usbmodem");
|
|
124
188
|
// true
|
|
125
189
|
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
const verts = vertices(circle([100, 50], 30), { num: 60, last: true });
|
|
129
|
-
// [
|
|
130
|
-
// [ 130, 50 ],
|
|
131
|
-
// [ 129.8356568610482, 53.1358538980296 ],
|
|
132
|
-
// [ 129.34442802201417, 56.23735072453278 ],
|
|
133
|
-
// ...
|
|
134
|
-
// ]
|
|
190
|
+
// vertices defining a polyline of a 100x100 mm square (top left at 20,20)
|
|
191
|
+
const verts = [[20, 20], [120, 20], [120, 120], [20, 120], [20, 20]];
|
|
135
192
|
|
|
136
|
-
// convert to drawing commands (w/
|
|
137
|
-
|
|
193
|
+
// convert to drawing commands (w/ custom speed, 25%)
|
|
194
|
+
// see docs for config options
|
|
195
|
+
const path = polyline(verts, { speed: 0.25 })
|
|
138
196
|
// [
|
|
139
|
-
// [
|
|
140
|
-
// [
|
|
141
|
-
// [
|
|
142
|
-
// [
|
|
143
|
-
// [
|
|
144
|
-
//
|
|
197
|
+
// ["m", [20, 20]],
|
|
198
|
+
// ["d"],
|
|
199
|
+
// ["m", [120, 20], 0.25],
|
|
200
|
+
// ["m", [120, 120], 0.25],
|
|
201
|
+
// ["m", [20, 120], 0.25],
|
|
202
|
+
// ["m", [20, 20], 0.25],
|
|
203
|
+
// ["u"]
|
|
145
204
|
// ]
|
|
146
205
|
|
|
147
|
-
// draw/send seq of commands
|
|
148
|
-
|
|
206
|
+
// draw/send seq of commands
|
|
207
|
+
// by default the given commands will be wrapped with a start/end
|
|
208
|
+
// command sequence, configurable via options given to AxiDraw ctor)...
|
|
209
|
+
await axi.draw(path);
|
|
149
210
|
|
|
150
211
|
})();
|
|
151
212
|
```
|
|
152
213
|
|
|
214
|
+
### geom-axidraw example
|
|
215
|
+
|
|
216
|
+
Result shown here: https://mastodon.thi.ng/@toxi/109473655772673067
|
|
217
|
+
|
|
218
|
+
```js tangle:export/readme-geom.js
|
|
219
|
+
import { AxiDraw } from "@thi.ng/axidraw";
|
|
220
|
+
import { asCubic, group, pathFromCubics, star } from "@thi.ng/geom";
|
|
221
|
+
import { asAxiDraw } from "@thi.ng/geom-axidraw";
|
|
222
|
+
import { map, range } from "@thi.ng/transducers";
|
|
223
|
+
|
|
224
|
+
(async () => {
|
|
225
|
+
// create group of bezier-interpolated star polygons,
|
|
226
|
+
// with each path using a slightly different configuration
|
|
227
|
+
const geo = group({ translate: [100, 100] }, [
|
|
228
|
+
...map(
|
|
229
|
+
(t) =>
|
|
230
|
+
pathFromCubics(
|
|
231
|
+
asCubic(star(90, 6, [t, 1]), {
|
|
232
|
+
breakPoints: true,
|
|
233
|
+
scale: 0.66,
|
|
234
|
+
})
|
|
235
|
+
),
|
|
236
|
+
range(0.3, 1.01, 0.05)
|
|
237
|
+
),
|
|
238
|
+
]);
|
|
239
|
+
|
|
240
|
+
// connect to plotter
|
|
241
|
+
const axi = new AxiDraw();
|
|
242
|
+
await axi.connect();
|
|
243
|
+
// convert geometry to drawing commands & send to plotter
|
|
244
|
+
await axi.draw(asAxiDraw(geo, { samples: 40 }));
|
|
245
|
+
})();
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Other selected toots/tweets:
|
|
249
|
+
|
|
250
|
+
- https://mastodon.thi.ng/@toxi/109474947869078797
|
|
251
|
+
- https://mastodon.thi.ng/@toxi/109483553358349473
|
|
252
|
+
- more to come...
|
|
253
|
+
|
|
153
254
|
## Authors
|
|
154
255
|
|
|
155
|
-
Karsten Schmidt
|
|
256
|
+
- [Karsten Schmidt](https://thi.ng)
|
|
156
257
|
|
|
157
258
|
If this project contributes to an academic publication, please cite it as:
|
|
158
259
|
|
|
@@ -167,4 +268,4 @@ If this project contributes to an academic publication, please cite it as:
|
|
|
167
268
|
|
|
168
269
|
## License
|
|
169
270
|
|
|
170
|
-
© 2022 Karsten Schmidt // Apache
|
|
271
|
+
© 2022 Karsten Schmidt // Apache License 2.0
|
package/api.d.ts
CHANGED
|
@@ -1,28 +1,35 @@
|
|
|
1
|
+
import type { IDeref } from "@thi.ng/api";
|
|
1
2
|
import type { ILogger } from "@thi.ng/logger";
|
|
2
3
|
import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
3
4
|
/** Start command sequence (configurable via {@link AxiDrawOpts}) */
|
|
4
|
-
export
|
|
5
|
+
export type StartCommand = ["start"];
|
|
5
6
|
/** Stop command sequence (configurable via {@link AxiDrawOpts}) */
|
|
6
|
-
export
|
|
7
|
+
export type StopCommand = ["stop"];
|
|
7
8
|
/** Return plotter to initial XY position */
|
|
8
|
-
export
|
|
9
|
+
export type HomeCommand = ["home"];
|
|
10
|
+
/** Reset curr position as home (0,0) */
|
|
11
|
+
export type ResetCommand = ["reset"];
|
|
9
12
|
/** Turn XY motors on/off */
|
|
10
|
-
export
|
|
13
|
+
export type MotorCommand = ["on" | "off"];
|
|
11
14
|
/** Pen config, min/down position, max/up position (in %) */
|
|
12
|
-
export
|
|
15
|
+
export type PenConfigCommand = ["pen", number?, number?];
|
|
13
16
|
/**
|
|
14
|
-
* Pen up/down, optional delay (in ms),
|
|
15
|
-
* {@link AxiDrawOpts}.
|
|
17
|
+
* Pen up/down, optional delay (in ms), optional custom level/position. If
|
|
18
|
+
* omitted, default values used from {@link AxiDrawOpts}. Using -1 as delay also
|
|
19
|
+
* uses default.
|
|
16
20
|
*/
|
|
17
|
-
export
|
|
21
|
+
export type PenUpDownCommand = ["u" | "d", number?, number?];
|
|
18
22
|
/**
|
|
19
23
|
* Move to abs pos (in worldspace coords, default mm), optional speed factor
|
|
20
24
|
* (default: 1)
|
|
21
25
|
*/
|
|
22
|
-
export
|
|
26
|
+
export type MoveXYCommand = ["m", ReadonlyVec, number?];
|
|
23
27
|
/** Explicit delay (in ms) */
|
|
24
|
-
export
|
|
25
|
-
export
|
|
28
|
+
export type WaitCommand = ["w", number];
|
|
29
|
+
export type DrawCommand = StartCommand | StopCommand | HomeCommand | ResetCommand | MotorCommand | PenConfigCommand | PenUpDownCommand | MoveXYCommand | WaitCommand;
|
|
30
|
+
/**
|
|
31
|
+
* Global plotter drawing configuration. Also see {@link DEFAULT_OPTS}.
|
|
32
|
+
*/
|
|
26
33
|
export interface AxiDrawOpts {
|
|
27
34
|
/**
|
|
28
35
|
* Conversion factor from geometry worldspace units to inches.
|
|
@@ -58,13 +65,13 @@ export interface AxiDrawOpts {
|
|
|
58
65
|
/**
|
|
59
66
|
* Delay after pen up
|
|
60
67
|
*
|
|
61
|
-
* @defaultValue
|
|
68
|
+
* @defaultValue 150
|
|
62
69
|
*/
|
|
63
70
|
delayUp: number;
|
|
64
71
|
/**
|
|
65
72
|
* Delay after pen down
|
|
66
73
|
*
|
|
67
|
-
* @defaultValue
|
|
74
|
+
* @defaultValue 150
|
|
68
75
|
*/
|
|
69
76
|
delayDown: number;
|
|
70
77
|
/**
|
|
@@ -89,13 +96,92 @@ export interface AxiDrawOpts {
|
|
|
89
96
|
* Logger instance
|
|
90
97
|
*/
|
|
91
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
|
+
/**
|
|
116
|
+
* Refresh interval for checking the control FSM in paused state.
|
|
117
|
+
*
|
|
118
|
+
* @defaultValue 1000
|
|
119
|
+
*/
|
|
120
|
+
refresh: number;
|
|
121
|
+
/**
|
|
122
|
+
* If true (default), installs SIGINT handler to lift pen when the Node.js
|
|
123
|
+
* process is terminated.
|
|
124
|
+
*/
|
|
125
|
+
sigint: boolean;
|
|
92
126
|
}
|
|
93
127
|
export declare const START: StartCommand;
|
|
94
128
|
export declare const STOP: StopCommand;
|
|
95
129
|
export declare const HOME: HomeCommand;
|
|
130
|
+
export declare const RESET: ResetCommand;
|
|
96
131
|
export declare const PEN: PenConfigCommand;
|
|
97
132
|
export declare const UP: PenUpDownCommand;
|
|
98
133
|
export declare const DOWN: PenUpDownCommand;
|
|
99
134
|
export declare const ON: MotorCommand;
|
|
100
135
|
export declare const OFF: MotorCommand;
|
|
136
|
+
/**
|
|
137
|
+
* FSM state enum for (interactive) control for processing of drawing commands.
|
|
138
|
+
* See {@link AxiDraw.draw} and {@link AxiDrawControl} for details.
|
|
139
|
+
*/
|
|
140
|
+
export declare enum AxiDrawState {
|
|
141
|
+
/**
|
|
142
|
+
* Draw command processing can continue as normal.
|
|
143
|
+
*/
|
|
144
|
+
CONTINUE = 0,
|
|
145
|
+
/**
|
|
146
|
+
* Draw command processing is suspended indefinitely.
|
|
147
|
+
*/
|
|
148
|
+
PAUSE = 1,
|
|
149
|
+
/**
|
|
150
|
+
* Draw command processing is cancelled.
|
|
151
|
+
*/
|
|
152
|
+
CANCEL = 2
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Drawing behavior options for a single polyline.
|
|
156
|
+
*/
|
|
157
|
+
export interface PolylineOpts {
|
|
158
|
+
/**
|
|
159
|
+
* Speed factor (multiple of globally configured draw speed). Depending on
|
|
160
|
+
* pen used, slower speeds might result in thicker strokes.
|
|
161
|
+
*
|
|
162
|
+
* @defaultValue 1
|
|
163
|
+
*/
|
|
164
|
+
speed: number;
|
|
165
|
+
/**
|
|
166
|
+
* Pen down (Z) position (%) for this particular shape/polyline. Will be
|
|
167
|
+
* reset to globally configured default at the end of the shape.
|
|
168
|
+
*/
|
|
169
|
+
down: number;
|
|
170
|
+
/**
|
|
171
|
+
* Delay for pen down command at the start of this particular
|
|
172
|
+
* shape/polyline.
|
|
173
|
+
*/
|
|
174
|
+
delayDown: number;
|
|
175
|
+
/**
|
|
176
|
+
* Delay for pen up command at the end this particular shape/polyline.
|
|
177
|
+
*/
|
|
178
|
+
delayUp: number;
|
|
179
|
+
/**
|
|
180
|
+
* If enabled, no pen up/down commands will be included.
|
|
181
|
+
* {@link PolylineOpts.speed} is the only other option considered then.
|
|
182
|
+
*
|
|
183
|
+
* @defaultValue false
|
|
184
|
+
*/
|
|
185
|
+
onlyGeo: boolean;
|
|
186
|
+
}
|
|
101
187
|
//# sourceMappingURL=api.d.ts.map
|
package/api.js
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
export const START = ["start"];
|
|
2
2
|
export const STOP = ["stop"];
|
|
3
3
|
export const HOME = ["home"];
|
|
4
|
+
export const RESET = ["reset"];
|
|
4
5
|
export const PEN = ["pen"];
|
|
5
6
|
export const UP = ["u"];
|
|
6
7
|
export const DOWN = ["d"];
|
|
7
8
|
export const ON = ["on"];
|
|
8
9
|
export const OFF = ["off"];
|
|
10
|
+
/**
|
|
11
|
+
* FSM state enum for (interactive) control for processing of drawing commands.
|
|
12
|
+
* See {@link AxiDraw.draw} and {@link AxiDrawControl} for details.
|
|
13
|
+
*/
|
|
14
|
+
export var AxiDrawState;
|
|
15
|
+
(function (AxiDrawState) {
|
|
16
|
+
/**
|
|
17
|
+
* Draw command processing can continue as normal.
|
|
18
|
+
*/
|
|
19
|
+
AxiDrawState[AxiDrawState["CONTINUE"] = 0] = "CONTINUE";
|
|
20
|
+
/**
|
|
21
|
+
* Draw command processing is suspended indefinitely.
|
|
22
|
+
*/
|
|
23
|
+
AxiDrawState[AxiDrawState["PAUSE"] = 1] = "PAUSE";
|
|
24
|
+
/**
|
|
25
|
+
* Draw command processing is cancelled.
|
|
26
|
+
*/
|
|
27
|
+
AxiDrawState[AxiDrawState["CANCEL"] = 2] = "CANCEL";
|
|
28
|
+
})(AxiDrawState || (AxiDrawState = {}));
|
package/axidraw.d.ts
CHANGED
|
@@ -1,33 +1,47 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { IReset } from "@thi.ng/api";
|
|
2
2
|
import { ReadonlyVec, Vec } from "@thi.ng/vectors";
|
|
3
3
|
import { SerialPort } from "serialport";
|
|
4
4
|
import { AxiDrawOpts, DrawCommand } from "./api.js";
|
|
5
5
|
export declare const DEFAULT_OPTS: AxiDrawOpts;
|
|
6
|
-
export declare class AxiDraw {
|
|
6
|
+
export declare class AxiDraw implements IReset {
|
|
7
7
|
serial: SerialPort;
|
|
8
8
|
opts: AxiDrawOpts;
|
|
9
9
|
isConnected: boolean;
|
|
10
|
+
isPenDown: boolean;
|
|
11
|
+
penLimits: [number, number];
|
|
10
12
|
pos: Vec;
|
|
13
|
+
targetPos: Vec;
|
|
11
14
|
constructor(opts?: Partial<AxiDrawOpts>);
|
|
15
|
+
reset(): this;
|
|
12
16
|
/**
|
|
13
17
|
* Async function. Attempts to connect to the drawing machine via given
|
|
14
18
|
* (partial) serial port path/name, returns true if successful.
|
|
15
19
|
*
|
|
20
|
+
* @remarks
|
|
21
|
+
* First matching port will be used. If `path` is a sting, a port name must
|
|
22
|
+
* only start with it in order to be considered a match.
|
|
23
|
+
*
|
|
24
|
+
* An error is thrown if no matching port could be found.
|
|
25
|
+
*
|
|
16
26
|
* @param path
|
|
17
27
|
*/
|
|
18
|
-
connect(path?: RegExp): Promise<
|
|
28
|
+
connect(path?: string | RegExp): Promise<void>;
|
|
19
29
|
/**
|
|
20
30
|
* Async function. Converts sequence of {@link DrawCommand}s into actual EBB
|
|
21
|
-
* commands and sends them via configured serial port to the AxiDraw.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
31
|
+
* commands and sends them via configured serial port to the AxiDraw. If
|
|
32
|
+
* `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).
|
|
26
36
|
*
|
|
27
37
|
* @remarks
|
|
28
38
|
* This function is async and if using `await` will only return once all
|
|
29
39
|
* commands have been processed or cancelled.
|
|
30
40
|
*
|
|
41
|
+
* The `control` implementation/ provided as part of {@link AxiDrawOpts} can
|
|
42
|
+
* be used to pause, resume or cancel the drawing (see
|
|
43
|
+
* {@link AxiDrawOpts.control} for details).
|
|
44
|
+
*
|
|
31
45
|
* Reference:
|
|
32
46
|
* - http://evil-mad.github.io/EggBot/ebb.html
|
|
33
47
|
*
|
|
@@ -42,23 +56,35 @@ export declare class AxiDraw {
|
|
|
42
56
|
* ```
|
|
43
57
|
*
|
|
44
58
|
* @param commands
|
|
45
|
-
* @param
|
|
59
|
+
* @param wrap
|
|
46
60
|
*/
|
|
47
|
-
draw(commands: Iterable<DrawCommand>,
|
|
61
|
+
draw(commands: Iterable<DrawCommand>, wrap?: boolean): Promise<number>;
|
|
48
62
|
/**
|
|
49
|
-
*
|
|
50
|
-
* {@link
|
|
51
|
-
*
|
|
63
|
+
* Syntax sugar for drawing a **single** command only, otherwise same as
|
|
64
|
+
* {@link AxiDraw.draw}.
|
|
65
|
+
*
|
|
66
|
+
* @param cmd
|
|
67
|
+
*/
|
|
68
|
+
draw1(cmd: DrawCommand): Promise<number>;
|
|
69
|
+
motorsOn(): void;
|
|
70
|
+
motorsOff(): void;
|
|
71
|
+
penConfig(down?: number, up?: number): void;
|
|
72
|
+
penUp(delay?: number, level?: number): number;
|
|
73
|
+
penDown(delay?: number, level?: number): number;
|
|
74
|
+
moveTo(p: ReadonlyVec, tempo?: number): number;
|
|
75
|
+
home(): number;
|
|
76
|
+
protected onSignal(): Promise<void>;
|
|
77
|
+
protected send(msg: string): void;
|
|
78
|
+
/**
|
|
79
|
+
* Sends pen up/down config
|
|
52
80
|
*
|
|
53
81
|
* @remarks
|
|
54
|
-
*
|
|
55
|
-
*
|
|
82
|
+
* Reference:
|
|
83
|
+
* - https://github.com/evil-mad/AxiDraw-Processing/blob/80d81a8c897b8a1872b0555af52a8d1b5b13cec4/AxiGen1/AxiGen1.pde#L213
|
|
56
84
|
*
|
|
57
|
-
* @param
|
|
58
|
-
* @param
|
|
59
|
-
* @param onlyGeo
|
|
85
|
+
* @param id
|
|
86
|
+
* @param x
|
|
60
87
|
*/
|
|
61
|
-
|
|
62
|
-
protected send(msg: string): void;
|
|
88
|
+
protected sendPenConfig(id: number, x: number): void;
|
|
63
89
|
}
|
|
64
90
|
//# sourceMappingURL=axidraw.d.ts.map
|
package/axidraw.js
CHANGED
|
@@ -1,60 +1,90 @@
|
|
|
1
|
+
import { isString } from "@thi.ng/checks";
|
|
1
2
|
import { delayed } from "@thi.ng/compose";
|
|
2
|
-
import { assert, unsupported } from "@thi.ng/errors";
|
|
3
|
+
import { assert, ioerror, unsupported } from "@thi.ng/errors";
|
|
3
4
|
import { ConsoleLogger } from "@thi.ng/logger";
|
|
4
|
-
import { abs2, mulN2, set2, sub2 } from "@thi.ng/vectors";
|
|
5
|
+
import { abs2, mulN2, set2, sub2, zero, ZERO2, } from "@thi.ng/vectors";
|
|
5
6
|
import { SerialPort } from "serialport";
|
|
6
|
-
import {
|
|
7
|
+
import { AxiDrawState, HOME, OFF, ON, PEN, UP, } from "./api.js";
|
|
8
|
+
import { AxiDrawControl } from "./control.js";
|
|
9
|
+
import { complete } from "./polyline.js";
|
|
7
10
|
export const DEFAULT_OPTS = {
|
|
8
11
|
logger: new ConsoleLogger("axidraw"),
|
|
12
|
+
control: new AxiDrawControl(),
|
|
13
|
+
refresh: 1000,
|
|
9
14
|
unitsPerInch: 25.4,
|
|
10
15
|
stepsPerInch: 2032,
|
|
11
16
|
speed: 4000,
|
|
12
17
|
up: 60,
|
|
13
18
|
down: 30,
|
|
14
|
-
delayUp:
|
|
15
|
-
delayDown:
|
|
19
|
+
delayUp: 150,
|
|
20
|
+
delayDown: 150,
|
|
16
21
|
preDelay: 0,
|
|
17
22
|
start: [ON, PEN, UP],
|
|
18
23
|
stop: [UP, HOME, OFF],
|
|
24
|
+
sigint: true,
|
|
19
25
|
};
|
|
20
26
|
export class AxiDraw {
|
|
21
27
|
constructor(opts = {}) {
|
|
22
28
|
this.isConnected = false;
|
|
29
|
+
this.isPenDown = false;
|
|
23
30
|
this.pos = [0, 0];
|
|
31
|
+
this.targetPos = [0, 0];
|
|
24
32
|
this.opts = { ...DEFAULT_OPTS, ...opts };
|
|
33
|
+
this.penLimits = [this.opts.down, this.opts.up];
|
|
34
|
+
}
|
|
35
|
+
reset() {
|
|
36
|
+
zero(this.pos);
|
|
37
|
+
zero(this.targetPos);
|
|
38
|
+
return this;
|
|
25
39
|
}
|
|
26
40
|
/**
|
|
27
41
|
* Async function. Attempts to connect to the drawing machine via given
|
|
28
42
|
* (partial) serial port path/name, returns true if successful.
|
|
29
43
|
*
|
|
44
|
+
* @remarks
|
|
45
|
+
* First matching port will be used. If `path` is a sting, a port name must
|
|
46
|
+
* only start with it in order to be considered a match.
|
|
47
|
+
*
|
|
48
|
+
* An error is thrown if no matching port could be found.
|
|
49
|
+
*
|
|
30
50
|
* @param path
|
|
31
51
|
*/
|
|
32
|
-
async connect(path =
|
|
52
|
+
async connect(path = "/dev/tty.usbmodem") {
|
|
53
|
+
const isStr = isString(path);
|
|
33
54
|
for (let port of await SerialPort.list()) {
|
|
34
|
-
if (
|
|
55
|
+
if ((isStr && port.path.startsWith(path)) ||
|
|
56
|
+
(!isStr && path.test(port.path))) {
|
|
35
57
|
this.opts.logger.info(`using device: ${port.path}...`);
|
|
36
58
|
this.serial = new SerialPort({
|
|
37
59
|
path: port.path,
|
|
38
60
|
baudRate: 38400,
|
|
39
61
|
});
|
|
40
62
|
this.isConnected = true;
|
|
41
|
-
|
|
63
|
+
if (this.opts.sigint) {
|
|
64
|
+
this.opts.logger.debug("installing signal handler...");
|
|
65
|
+
process.on("SIGINT", this.onSignal.bind(this));
|
|
66
|
+
}
|
|
67
|
+
return;
|
|
42
68
|
}
|
|
43
69
|
}
|
|
44
|
-
|
|
70
|
+
ioerror(`no matching device for ${path}`);
|
|
45
71
|
}
|
|
46
72
|
/**
|
|
47
73
|
* Async function. Converts sequence of {@link DrawCommand}s into actual EBB
|
|
48
|
-
* commands and sends them via configured serial port to the AxiDraw.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
74
|
+
* commands and sends them via configured serial port to the AxiDraw. If
|
|
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).
|
|
53
79
|
*
|
|
54
80
|
* @remarks
|
|
55
81
|
* This function is async and if using `await` will only return once all
|
|
56
82
|
* commands have been processed or cancelled.
|
|
57
83
|
*
|
|
84
|
+
* The `control` implementation/ provided as part of {@link AxiDrawOpts} can
|
|
85
|
+
* be used to pause, resume or cancel the drawing (see
|
|
86
|
+
* {@link AxiDrawOpts.control} for details).
|
|
87
|
+
*
|
|
58
88
|
* Reference:
|
|
59
89
|
* - http://evil-mad.github.io/EggBot/ebb.html
|
|
60
90
|
*
|
|
@@ -69,103 +99,152 @@ export class AxiDraw {
|
|
|
69
99
|
* ```
|
|
70
100
|
*
|
|
71
101
|
* @param commands
|
|
72
|
-
* @param
|
|
102
|
+
* @param wrap
|
|
73
103
|
*/
|
|
74
|
-
async draw(commands,
|
|
104
|
+
async draw(commands, wrap = true) {
|
|
75
105
|
assert(this.isConnected, "AxiDraw not yet connected, need to call .connect() first");
|
|
76
106
|
let t0 = Date.now();
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
107
|
+
const { control, logger, preDelay, refresh } = this.opts;
|
|
108
|
+
for (let $cmd of wrap ? complete(commands) : commands) {
|
|
109
|
+
if (control) {
|
|
110
|
+
let state = control.deref();
|
|
111
|
+
if (state === AxiDrawState.PAUSE) {
|
|
112
|
+
const penDown = this.isPenDown;
|
|
113
|
+
if (penDown)
|
|
114
|
+
this.penUp();
|
|
115
|
+
do {
|
|
116
|
+
await delayed(0, refresh);
|
|
117
|
+
} while ((state = control.deref()) === AxiDrawState.PAUSE);
|
|
118
|
+
if (state === AxiDrawState.CONTINUE && penDown) {
|
|
119
|
+
this.penDown();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (state === AxiDrawState.CANCEL) {
|
|
123
|
+
this.penUp();
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
88
127
|
const [cmd, a, b] = $cmd;
|
|
89
128
|
let wait = -1;
|
|
90
129
|
switch (cmd) {
|
|
91
130
|
case "start":
|
|
92
131
|
case "stop":
|
|
93
|
-
this.draw(
|
|
132
|
+
await this.draw(this.opts[cmd], false);
|
|
94
133
|
break;
|
|
95
134
|
case "home":
|
|
96
|
-
this.
|
|
135
|
+
wait = this.home();
|
|
136
|
+
break;
|
|
137
|
+
case "reset":
|
|
138
|
+
this.reset();
|
|
97
139
|
break;
|
|
98
140
|
case "on":
|
|
99
|
-
this.
|
|
141
|
+
this.motorsOn();
|
|
100
142
|
break;
|
|
101
143
|
case "off":
|
|
102
|
-
this.
|
|
144
|
+
this.motorsOff();
|
|
103
145
|
break;
|
|
104
146
|
case "pen":
|
|
105
|
-
|
|
106
|
-
let val = a !== undefined ? a : config.down;
|
|
107
|
-
// unit ref:
|
|
108
|
-
// https://github.com/evil-mad/AxiDraw-Processing/blob/80d81a8c897b8a1872b0555af52a8d1b5b13cec4/AxiGen1/AxiGen1.pde#L213
|
|
109
|
-
this.send(`SC,5,${(7500 + 175 * val) | 0}\r`);
|
|
110
|
-
val = b !== undefined ? b : config.up;
|
|
111
|
-
this.send(`SC,4,${(7500 + 175 * val) | 0}\r`);
|
|
112
|
-
this.send(`SC,10,65535\r`);
|
|
113
|
-
}
|
|
147
|
+
this.penConfig(a, b);
|
|
114
148
|
break;
|
|
115
149
|
case "u":
|
|
116
|
-
wait = a
|
|
117
|
-
this.send(`SP,1,${wait}\r`);
|
|
150
|
+
wait = this.penUp(a, b);
|
|
118
151
|
break;
|
|
119
152
|
case "d":
|
|
120
|
-
wait = a
|
|
121
|
-
this.send(`SP,0,${wait}\r`);
|
|
153
|
+
wait = this.penDown(a, b);
|
|
122
154
|
break;
|
|
123
155
|
case "w":
|
|
124
156
|
wait = a;
|
|
125
157
|
break;
|
|
126
158
|
case "m":
|
|
127
|
-
|
|
128
|
-
mulN2(targetPos, a, scale);
|
|
129
|
-
sub2(delta, targetPos, pos);
|
|
130
|
-
set2(pos, targetPos);
|
|
131
|
-
config.logger.info("target", targetPos, "delta", delta);
|
|
132
|
-
const maxAxis = Math.max(...abs2([], delta));
|
|
133
|
-
wait = (1000 * maxAxis) / (speed * (b || 1));
|
|
134
|
-
this.send(`XM,${wait | 0},${delta[0] | 0},${delta[1] | 0}\r`);
|
|
135
|
-
}
|
|
159
|
+
wait = this.moveTo(a, b);
|
|
136
160
|
break;
|
|
137
161
|
default:
|
|
138
162
|
unsupported(`unknown command: ${$cmd}`);
|
|
139
163
|
}
|
|
140
164
|
if (wait > 0) {
|
|
141
165
|
wait = Math.max(0, wait - preDelay);
|
|
142
|
-
|
|
166
|
+
logger.debug(`waiting ${wait}ms...`);
|
|
143
167
|
await delayed(0, wait);
|
|
144
168
|
}
|
|
145
169
|
}
|
|
146
170
|
return Date.now() - t0;
|
|
147
171
|
}
|
|
148
172
|
/**
|
|
149
|
-
*
|
|
150
|
-
* {@link
|
|
151
|
-
* draw speed (default: 1).
|
|
173
|
+
* Syntax sugar for drawing a **single** command only, otherwise same as
|
|
174
|
+
* {@link AxiDraw.draw}.
|
|
152
175
|
*
|
|
153
|
-
* @
|
|
154
|
-
* Unless `onlyGeo` is explicitly enabled, the resulting command sequence
|
|
155
|
-
* will also contain necessary pen up/down commands.
|
|
156
|
-
*
|
|
157
|
-
* @param pts
|
|
158
|
-
* @param speed
|
|
159
|
-
* @param onlyGeo
|
|
176
|
+
* @param cmd
|
|
160
177
|
*/
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
178
|
+
draw1(cmd) {
|
|
179
|
+
return this.draw([cmd], false);
|
|
180
|
+
}
|
|
181
|
+
motorsOn() {
|
|
182
|
+
this.send("EM,1,1\r");
|
|
183
|
+
}
|
|
184
|
+
motorsOff() {
|
|
185
|
+
this.send("EM,0,0\r");
|
|
186
|
+
}
|
|
187
|
+
penConfig(down, up) {
|
|
188
|
+
down = down !== undefined ? down : this.opts.down;
|
|
189
|
+
this.sendPenConfig(5, down);
|
|
190
|
+
this.penLimits[0] = down;
|
|
191
|
+
up = up !== undefined ? up : this.opts.up;
|
|
192
|
+
this.sendPenConfig(4, up);
|
|
193
|
+
this.penLimits[1] = up;
|
|
194
|
+
this.send(`SC,10,65535\r`);
|
|
195
|
+
}
|
|
196
|
+
penUp(delay, level) {
|
|
197
|
+
if (level !== undefined)
|
|
198
|
+
this.sendPenConfig(4, level);
|
|
199
|
+
delay = delay !== undefined && delay >= 0 ? delay : this.opts.delayUp;
|
|
200
|
+
this.send(`SP,1,${delay}\r`);
|
|
201
|
+
this.isPenDown = false;
|
|
202
|
+
return delay;
|
|
203
|
+
}
|
|
204
|
+
penDown(delay, level) {
|
|
205
|
+
if (level !== undefined)
|
|
206
|
+
this.sendPenConfig(5, level);
|
|
207
|
+
delay = delay !== undefined && delay >= 0 ? delay : this.opts.delayDown;
|
|
208
|
+
this.send(`SP,0,${delay}\r`);
|
|
209
|
+
this.isPenDown = true;
|
|
210
|
+
return delay;
|
|
211
|
+
}
|
|
212
|
+
moveTo(p, tempo = 1) {
|
|
213
|
+
const { pos, targetPos, opts } = this;
|
|
214
|
+
// apply scale factor: worldspace units -> motor steps
|
|
215
|
+
mulN2(targetPos, p, opts.stepsPerInch / opts.unitsPerInch);
|
|
216
|
+
const delta = sub2([], targetPos, pos);
|
|
217
|
+
set2(pos, targetPos);
|
|
218
|
+
const maxAxis = Math.max(...abs2([], delta));
|
|
219
|
+
const duration = (1000 * maxAxis) / (opts.speed * tempo);
|
|
220
|
+
this.send(`XM,${duration | 0},${delta[0] | 0},${delta[1] | 0}\r`);
|
|
221
|
+
return duration;
|
|
222
|
+
}
|
|
223
|
+
home() {
|
|
224
|
+
return this.moveTo(ZERO2);
|
|
225
|
+
}
|
|
226
|
+
async onSignal() {
|
|
227
|
+
this.opts.logger.warn(`SIGNINT received, stop drawing...`);
|
|
228
|
+
this.penUp(0);
|
|
229
|
+
this.motorsOff();
|
|
230
|
+
await delayed(0, 100);
|
|
231
|
+
process.exit(1);
|
|
166
232
|
}
|
|
167
233
|
send(msg) {
|
|
168
234
|
this.opts.logger.debug(msg);
|
|
169
235
|
this.serial.write(msg);
|
|
170
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Sends pen up/down config
|
|
239
|
+
*
|
|
240
|
+
* @remarks
|
|
241
|
+
* Reference:
|
|
242
|
+
* - https://github.com/evil-mad/AxiDraw-Processing/blob/80d81a8c897b8a1872b0555af52a8d1b5b13cec4/AxiGen1/AxiGen1.pde#L213
|
|
243
|
+
*
|
|
244
|
+
* @param id
|
|
245
|
+
* @param x
|
|
246
|
+
*/
|
|
247
|
+
sendPenConfig(id, x) {
|
|
248
|
+
this.send(`SC,${id},${(7500 + 175 * x) | 0}\r`);
|
|
249
|
+
}
|
|
171
250
|
}
|
package/control.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { IDeref, IReset } from "@thi.ng/api";
|
|
2
|
+
import { AxiDrawState } from "./api.js";
|
|
3
|
+
/**
|
|
4
|
+
* Default implementation with easy-to-use API to control the processing of draw
|
|
5
|
+
* commands via {@link AxiDraw.draw}.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* See {@link AxiDrawOpts.control} for further details.
|
|
9
|
+
*/
|
|
10
|
+
export declare class AxiDrawControl implements IDeref<AxiDrawState>, IReset {
|
|
11
|
+
interval: number;
|
|
12
|
+
state: AxiDrawState;
|
|
13
|
+
constructor(interval?: number);
|
|
14
|
+
deref(): AxiDrawState;
|
|
15
|
+
reset(): this;
|
|
16
|
+
pause(): void;
|
|
17
|
+
resume(): void;
|
|
18
|
+
cancel(): void;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=control.d.ts.map
|
package/control.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { AxiDrawState } from "./api.js";
|
|
2
|
+
/**
|
|
3
|
+
* Default implementation with easy-to-use API to control the processing of draw
|
|
4
|
+
* commands via {@link AxiDraw.draw}.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* See {@link AxiDrawOpts.control} for further details.
|
|
8
|
+
*/
|
|
9
|
+
export class AxiDrawControl {
|
|
10
|
+
constructor(interval = 1000) {
|
|
11
|
+
this.interval = interval;
|
|
12
|
+
this.state = AxiDrawState.CONTINUE;
|
|
13
|
+
}
|
|
14
|
+
deref() {
|
|
15
|
+
return this.state;
|
|
16
|
+
}
|
|
17
|
+
reset() {
|
|
18
|
+
this.state = AxiDrawState.CONTINUE;
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
pause() {
|
|
22
|
+
if (this.state === AxiDrawState.CONTINUE) {
|
|
23
|
+
this.state = AxiDrawState.PAUSE;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
resume() {
|
|
27
|
+
if (this.state === AxiDrawState.PAUSE) {
|
|
28
|
+
this.state = AxiDrawState.CONTINUE;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
cancel() {
|
|
32
|
+
this.state = AxiDrawState.CANCEL;
|
|
33
|
+
}
|
|
34
|
+
}
|
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.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Minimal AxiDraw plotter/drawing machine controller for Node.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"type": "git",
|
|
11
11
|
"url": "https://github.com/thi-ng/umbrella.git"
|
|
12
12
|
},
|
|
13
|
-
"homepage": "https://github.com/thi-ng/umbrella/tree/
|
|
13
|
+
"homepage": "https://github.com/thi-ng/umbrella/tree/develop/packages/axidraw#readme",
|
|
14
14
|
"funding": [
|
|
15
15
|
{
|
|
16
16
|
"type": "github",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"url": "https://patreon.com/thing_umbrella"
|
|
22
22
|
}
|
|
23
23
|
],
|
|
24
|
-
"author": "Karsten Schmidt
|
|
24
|
+
"author": "Karsten Schmidt (https://thi.ng)",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "yarn clean && tsc --declaration",
|
|
@@ -34,31 +34,33 @@
|
|
|
34
34
|
"test": "testament test"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@thi.ng/api": "^8.
|
|
38
|
-
"@thi.ng/compose": "^2.1.
|
|
39
|
-
"@thi.ng/errors": "^2.2.
|
|
40
|
-
"@thi.ng/logger": "^1.4.
|
|
41
|
-
"@thi.ng/vectors": "^7.5.
|
|
37
|
+
"@thi.ng/api": "^8.6.0",
|
|
38
|
+
"@thi.ng/compose": "^2.1.21",
|
|
39
|
+
"@thi.ng/errors": "^2.2.6",
|
|
40
|
+
"@thi.ng/logger": "^1.4.5",
|
|
41
|
+
"@thi.ng/vectors": "^7.5.27",
|
|
42
42
|
"serialport": "^10.5.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@microsoft/api-extractor": "^7.33.
|
|
46
|
-
"@thi.ng/testament": "^0.3.
|
|
45
|
+
"@microsoft/api-extractor": "^7.33.7",
|
|
46
|
+
"@thi.ng/testament": "^0.3.7",
|
|
47
47
|
"rimraf": "^3.0.2",
|
|
48
48
|
"tools": "^0.0.1",
|
|
49
|
-
"typedoc": "^0.23.
|
|
50
|
-
"typescript": "^4.
|
|
49
|
+
"typedoc": "^0.23.22",
|
|
50
|
+
"typescript": "^4.9.4"
|
|
51
51
|
},
|
|
52
52
|
"keywords": [
|
|
53
|
+
"2d",
|
|
54
|
+
"async",
|
|
53
55
|
"axidraw",
|
|
56
|
+
"driver",
|
|
54
57
|
"geometry",
|
|
55
58
|
"io",
|
|
56
59
|
"logger",
|
|
57
60
|
"node",
|
|
58
|
-
"
|
|
59
|
-
"polygon",
|
|
61
|
+
"penplotter",
|
|
60
62
|
"polyline",
|
|
61
|
-
"
|
|
63
|
+
"serialport",
|
|
62
64
|
"typescript"
|
|
63
65
|
],
|
|
64
66
|
"publishConfig": {
|
|
@@ -80,11 +82,17 @@
|
|
|
80
82
|
},
|
|
81
83
|
"./axidraw": {
|
|
82
84
|
"default": "./axidraw.js"
|
|
85
|
+
},
|
|
86
|
+
"./control": {
|
|
87
|
+
"default": "./control.js"
|
|
88
|
+
},
|
|
89
|
+
"./polyline": {
|
|
90
|
+
"default": "./polyline.js"
|
|
83
91
|
}
|
|
84
92
|
},
|
|
85
93
|
"thi.ng": {
|
|
86
94
|
"status": "alpha",
|
|
87
95
|
"year": 2022
|
|
88
96
|
},
|
|
89
|
-
"gitHead": "
|
|
97
|
+
"gitHead": "f445a9cc8022bcdebbf6ff91fd66ced016d72f01\n"
|
|
90
98
|
}
|
package/polyline.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
2
|
+
import { DrawCommand, PolylineOpts } from "./api.js";
|
|
3
|
+
/**
|
|
4
|
+
* Takes an array of 2D points and yields an iterable of {@link DrawCommand}s.
|
|
5
|
+
* The drawing behavior can be customized via additional {@link PolylineOpts}
|
|
6
|
+
* given.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* The resulting command sequence assumes the pen is in the **up** position at
|
|
10
|
+
* the beginning of the line. Each polyline will end with a {@link UP} command.
|
|
11
|
+
*
|
|
12
|
+
* @param pts
|
|
13
|
+
* @param opts
|
|
14
|
+
*/
|
|
15
|
+
export declare function polyline(pts: ReadonlyVec[], opts: Partial<PolylineOpts>): IterableIterator<DrawCommand>;
|
|
16
|
+
/**
|
|
17
|
+
* Syntax sugar. Takes an iterable of draw commands, adds {@link START} as
|
|
18
|
+
* prefix and {@link STOP} as suffix. I.e. it creates a "complete" drawing...
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* [...complete([ ["m", [0, 0]] ])]
|
|
23
|
+
* // [ ["start"], ["m", [0, 0]], ["stop"] ]
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @param commands
|
|
27
|
+
*/
|
|
28
|
+
export declare function complete(commands: Iterable<DrawCommand>): Generator<DrawCommand, void, undefined>;
|
|
29
|
+
//# sourceMappingURL=polyline.d.ts.map
|
package/polyline.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { DOWN, START, STOP, UP } from "./api.js";
|
|
2
|
+
/**
|
|
3
|
+
* Takes an array of 2D points and yields an iterable of {@link DrawCommand}s.
|
|
4
|
+
* The drawing behavior can be customized via additional {@link PolylineOpts}
|
|
5
|
+
* given.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* The resulting command sequence assumes the pen is in the **up** position at
|
|
9
|
+
* the beginning of the line. Each polyline will end with a {@link UP} command.
|
|
10
|
+
*
|
|
11
|
+
* @param pts
|
|
12
|
+
* @param opts
|
|
13
|
+
*/
|
|
14
|
+
export function* polyline(pts, opts) {
|
|
15
|
+
if (!pts.length)
|
|
16
|
+
return;
|
|
17
|
+
const { speed, delayDown, delayUp, down, onlyGeo } = {
|
|
18
|
+
speed: 1,
|
|
19
|
+
onlyGeo: false,
|
|
20
|
+
...opts,
|
|
21
|
+
};
|
|
22
|
+
if (onlyGeo) {
|
|
23
|
+
for (let p of pts)
|
|
24
|
+
yield ["m", p, speed];
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
yield ["m", pts[0]];
|
|
28
|
+
if (down !== undefined)
|
|
29
|
+
yield ["pen", down];
|
|
30
|
+
yield delayDown != undefined ? ["d", delayDown] : DOWN;
|
|
31
|
+
for (let i = 1, n = pts.length; i < n; i++)
|
|
32
|
+
yield ["m", pts[i], speed];
|
|
33
|
+
yield delayUp != undefined ? ["u", delayUp] : UP;
|
|
34
|
+
// reset pen to configured defaults
|
|
35
|
+
if (down !== undefined)
|
|
36
|
+
yield ["pen"];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Syntax sugar. Takes an iterable of draw commands, adds {@link START} as
|
|
40
|
+
* prefix and {@link STOP} as suffix. I.e. it creates a "complete" drawing...
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* [...complete([ ["m", [0, 0]] ])]
|
|
45
|
+
* // [ ["start"], ["m", [0, 0]], ["stop"] ]
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @param commands
|
|
49
|
+
*/
|
|
50
|
+
export function* complete(commands) {
|
|
51
|
+
yield START;
|
|
52
|
+
yield* commands;
|
|
53
|
+
yield STOP;
|
|
54
|
+
}
|