@thi.ng/axidraw 1.0.0 → 1.1.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 +33 -1
- package/README.md +140 -42
- package/api.d.ts +48 -1
- package/axidraw.d.ts +9 -2
- package/axidraw.js +63 -8
- package/commands.d.ts +13 -7
- package/commands.js +20 -6
- package/dip.d.ts +15 -5
- package/dip.js +11 -4
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +19 -14
- package/palettes.d.ts +145 -0
- package/palettes.js +145 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2023-03-
|
|
3
|
+
- **Last updated**: 2023-03-27T19:05:48Z
|
|
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,38 @@ 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
|
+
## [1.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/axidraw@1.1.0) (2023-03-22)
|
|
13
|
+
|
|
14
|
+
#### 🚀 Features
|
|
15
|
+
|
|
16
|
+
- add save/restore commands ([317f8e0](https://github.com/thi-ng/umbrella/commit/317f8e0))
|
|
17
|
+
- add/update command types
|
|
18
|
+
- add SAVE/RESTORE to store/restore pen levels
|
|
19
|
+
- update AxiDraw.draw() to restore state after one-off pen config
|
|
20
|
+
- update DipOpts & dip() ([52d8924](https://github.com/thi-ng/umbrella/commit/52d8924))
|
|
21
|
+
- rename `down` => `downDelay`, `up` => `upDelay`
|
|
22
|
+
- add `down`/`up` level opts
|
|
23
|
+
- update dip() impl to store/restore pen state if using custom
|
|
24
|
+
up/down levels for dipping
|
|
25
|
+
- add palette command seq gens ([0e453c1](https://github.com/thi-ng/umbrella/commit/0e453c1))
|
|
26
|
+
- add linearPalette() & radialPalette() and config options
|
|
27
|
+
- update pkg export maps
|
|
28
|
+
- add global clipping bounds option ([a99a58e](https://github.com/thi-ng/umbrella/commit/a99a58e))
|
|
29
|
+
- add AxiDrawOpts.clip
|
|
30
|
+
- add support for paper sizes, home offset ([c44510f](https://github.com/thi-ng/umbrella/commit/c44510f))
|
|
31
|
+
- update AxiDrawOpts.bounds to accept paper sizes ([@thi.ng/units](https://github.com/thi-ng/umbrella/tree/main/packages/units) quantities)
|
|
32
|
+
- add AxiDrawOpts.home
|
|
33
|
+
- update AxiDraw ctor & move/sendMove methods
|
|
34
|
+
- add AxiDraw.setHome()
|
|
35
|
+
- update pkg deps
|
|
36
|
+
|
|
37
|
+
#### ♻️ Refactoring
|
|
38
|
+
|
|
39
|
+
- remove obsolete clamping ([50978ba](https://github.com/thi-ng/umbrella/commit/50978ba))
|
|
40
|
+
- update linearPalette()
|
|
41
|
+
- update bounds handling/clamping ([7850ed6](https://github.com/thi-ng/umbrella/commit/7850ed6))
|
|
42
|
+
- precalc scale factor & bounds in ctor
|
|
43
|
+
|
|
12
44
|
# [1.0.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/axidraw@1.0.0) (2023-03-19)
|
|
13
45
|
|
|
14
46
|
#### 🛑 Breaking changes
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ This project is part of the
|
|
|
11
11
|
|
|
12
12
|
- [About](#about)
|
|
13
13
|
- [Declarative vs. imperative](#declarative-vs-imperative)
|
|
14
|
-
- [Units, limits &
|
|
14
|
+
- [Units, limits & clamping](#units-limits--clamping)
|
|
15
15
|
- [Path planning](#path-planning)
|
|
16
16
|
- [thi.ng/geom support](#thinggeom-support)
|
|
17
17
|
- [SVG support](#svg-support)
|
|
@@ -22,11 +22,12 @@ This project is part of the
|
|
|
22
22
|
- [Installation](#installation)
|
|
23
23
|
- [Dependencies](#dependencies)
|
|
24
24
|
- [API](#api)
|
|
25
|
+
- [Available draw commands](#available-draw-commands)
|
|
26
|
+
- [Command sequence generators](#command-sequence-generators)
|
|
25
27
|
- [Example usage](#example-usage)
|
|
26
28
|
- [Basics](#basics)
|
|
27
29
|
- [geom-axidraw example](#geom-axidraw-example)
|
|
28
|
-
- [
|
|
29
|
-
- [Command sequence generators](#command-sequence-generators)
|
|
30
|
+
- [Brush & paint palette](#brush--paint-palette)
|
|
30
31
|
- [Authors](#authors)
|
|
31
32
|
- [License](#license)
|
|
32
33
|
|
|
@@ -34,6 +35,10 @@ This project is part of the
|
|
|
34
35
|
|
|
35
36
|
Minimal AxiDraw plotter/drawing machine controller for Node.js.
|
|
36
37
|
|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
<small>AXI-SCAPE \#000, 12-layer watercolor painting/plot of cellular automata, © 2023 Karsten Schmidt</small>
|
|
41
|
+
|
|
37
42
|
This package provides a super-lightweight alternative to control an [AxiDraw
|
|
38
43
|
plotter](https://axidraw.com/) directly from Node.js, using a small custom set
|
|
39
44
|
of medium/high-level drawing commands. Structurally, these custom commands are
|
|
@@ -53,15 +58,30 @@ following the pattern of other packages in the
|
|
|
53
58
|
until the very last moment before being sent to the machine for physical
|
|
54
59
|
output...
|
|
55
60
|
|
|
56
|
-
### Units, limits &
|
|
61
|
+
### Units, limits & clamping
|
|
57
62
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
By default, bounds checking and coordinate clamping are applied (against a user
|
|
64
|
+
defined bounding rect or paper size), however this can be disabled (in which
|
|
65
|
+
case all given coordinates are expected to be valid and within machine limits).
|
|
66
|
+
Coordinates can be given in any unit, but if not using millimeters (default), a
|
|
67
|
+
conversion factor to inches (`unitsPerInch`) **MUST** be provided as part of the
|
|
68
|
+
[options
|
|
62
69
|
object](https://docs.thi.ng/umbrella/axidraw/interfaces/AxiDrawOpts.html) given
|
|
63
|
-
to the `AxiDraw` constructor.
|
|
64
|
-
geom-axidraw packages
|
|
70
|
+
to the `AxiDraw` constructor. Actual geometry clipping can be handled by the
|
|
71
|
+
[geom or geom-axidraw](#thinggeom-support) packages...
|
|
72
|
+
|
|
73
|
+
The bounding rect can be either defined by a tuple of `[[minX,minY],
|
|
74
|
+
[maxX,maxY]]` (in worldspace units) or as paper size defined as a
|
|
75
|
+
[`quantity()`](https://docs.thi.ng/umbrella/units/functions/quantity-1.html). The
|
|
76
|
+
default value is DIN A3 landscape.
|
|
77
|
+
|
|
78
|
+
If given as paper size (e.g. via
|
|
79
|
+
[thi.ng/units](https://github.com/thi-ng/umbrella/blob/develop/packages/units/)
|
|
80
|
+
presets), the actual units used to define these dimensions are irrelevant and
|
|
81
|
+
will be automatically converted.
|
|
82
|
+
|
|
83
|
+
[List of paper
|
|
84
|
+
sizes/presets](https://github.com/thi-ng/umbrella/blob/develop/packages/units/README.md#constants)
|
|
65
85
|
|
|
66
86
|
### Path planning
|
|
67
87
|
|
|
@@ -166,7 +186,7 @@ For Node.js REPL:
|
|
|
166
186
|
const axidraw = await import("@thi.ng/axidraw");
|
|
167
187
|
```
|
|
168
188
|
|
|
169
|
-
Package sizes (brotli'd, pre-treeshake): ESM:
|
|
189
|
+
Package sizes (brotli'd, pre-treeshake): ESM: 3.13 KB
|
|
170
190
|
|
|
171
191
|
## Dependencies
|
|
172
192
|
|
|
@@ -176,7 +196,9 @@ Package sizes (brotli'd, pre-treeshake): ESM: 2.46 KB
|
|
|
176
196
|
- [@thi.ng/date](https://github.com/thi-ng/umbrella/tree/develop/packages/date)
|
|
177
197
|
- [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/develop/packages/errors)
|
|
178
198
|
- [@thi.ng/logger](https://github.com/thi-ng/umbrella/tree/develop/packages/logger)
|
|
199
|
+
- [@thi.ng/math](https://github.com/thi-ng/umbrella/tree/develop/packages/math)
|
|
179
200
|
- [@thi.ng/transducers](https://github.com/thi-ng/umbrella/tree/develop/packages/transducers)
|
|
201
|
+
- [@thi.ng/units](https://github.com/thi-ng/umbrella/tree/develop/packages/units)
|
|
180
202
|
- [@thi.ng/vectors](https://github.com/thi-ng/umbrella/tree/develop/packages/vectors)
|
|
181
203
|
- [serialport](git://github.com/serialport/node-serialport.git)
|
|
182
204
|
|
|
@@ -184,6 +206,46 @@ Package sizes (brotli'd, pre-treeshake): ESM: 2.46 KB
|
|
|
184
206
|
|
|
185
207
|
[Generated API docs](https://docs.thi.ng/umbrella/axidraw/)
|
|
186
208
|
|
|
209
|
+
## Available draw commands
|
|
210
|
+
|
|
211
|
+
All
|
|
212
|
+
[`DrawCommand`s](https://docs.thi.ng/umbrella/axidraw/types/DrawCommand.html)
|
|
213
|
+
are expressed as S-expression-like, [thi.ng/hiccup]()-style elements, aka JS
|
|
214
|
+
arrays/tuples of `[command, ...args]`. The following commands are supported. All
|
|
215
|
+
also as predefined constants or factory functions for the parametric ones:
|
|
216
|
+
|
|
217
|
+
| Command | Preset/factory | Description |
|
|
218
|
+
|---------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------|
|
|
219
|
+
| `["comment", msg]` | [`COMMENT`](https://docs.thi.ng/umbrella/axidraw/functions/COMMENT.html) | Ignored, but logged during plotting |
|
|
220
|
+
| `["d", delay?, level?]` / `["u", delay?, level?]` | [`DOWN`](https://docs.thi.ng/umbrella/axidraw/functions/DOWN.html) / [`UP`](https://docs.thi.ng/umbrella/axidraw/functions/UP.html) | Move pen up/down w/ optional delay & level (0-100) |
|
|
221
|
+
| `["home"]` | `HOME` | Move to home position (origin) |
|
|
222
|
+
| `["m", [x,y], speed?]` | [`MOVE_REL`](https://docs.thi.ng/umbrella/axidraw/functions/MOVE_REL.html) | Move to relative position w/ optional speed factor |
|
|
223
|
+
| `["M", [x,y], speed?]` | [`MOVE`](https://docs.thi.ng/umbrella/axidraw/functions/MOVE.html) | Move to absolute position w/ optional speed factor |
|
|
224
|
+
| `["on"]` / `["off"]` | `ON` / `OFF` | Turn motors on/off |
|
|
225
|
+
| `["pen", down, up]` | [`PEN`](https://docs.thi.ng/umbrella/axidraw/functions/PEN.html) | Pen config (up/down levels) |
|
|
226
|
+
| `["reset"]` | `RESET` | Execute user defined reset sequence<sup>(1)</sup> |
|
|
227
|
+
| `["restore"]` | `RESTORE` | Restore saved pen up/down levels |
|
|
228
|
+
| `["save"]` | `SAVE` | Save current pen up/down levels |
|
|
229
|
+
| `["start"]` | `START` | Execute user defined start sequence<sup>(1)</sup> |
|
|
230
|
+
| `["stop"]` | `STOP` | Execute user defined stop sequence<sup>(1)</sup> |
|
|
231
|
+
| `["w", delay]` | [`WAIT`](https://docs.thi.ng/umbrella/axidraw/functions/WAIT.html) | Wait N milliseconds |
|
|
232
|
+
|
|
233
|
+
- <sup>(1)</sup> See
|
|
234
|
+
[AxiDrawOpts](https://docs.thi.ng/umbrella/axidraw/interfaces/AxiDrawOpts.html)
|
|
235
|
+
for details.
|
|
236
|
+
|
|
237
|
+
### Command sequence generators
|
|
238
|
+
|
|
239
|
+
Additionally, the following command sequence generators are provided (see their
|
|
240
|
+
docs for details and code examples):
|
|
241
|
+
|
|
242
|
+
- [`complete()`](https://docs.thi.ng/umbrella/axidraw/functions/complete.html)
|
|
243
|
+
- [`dip()`](https://docs.thi.ng/umbrella/axidraw/functions/dip.html)
|
|
244
|
+
- [`linearPalette()`](https://docs.thi.ng/umbrella/axidraw/functions/linearPalette.html)
|
|
245
|
+
- [`polyline()`](https://docs.thi.ng/umbrella/axidraw/functions/polyline.html)
|
|
246
|
+
- [`radialPalette()`](https://docs.thi.ng/umbrella/axidraw/functions/radialPalette.html)
|
|
247
|
+
- [`registrationMark()`](https://docs.thi.ng/umbrella/axidraw/functions/registrationMark.html)
|
|
248
|
+
|
|
187
249
|
### Example usage
|
|
188
250
|
|
|
189
251
|
#### Basics
|
|
@@ -259,6 +321,72 @@ import { map, range } from "@thi.ng/transducers";
|
|
|
259
321
|
})();
|
|
260
322
|
```
|
|
261
323
|
|
|
324
|
+
### Brush & paint palette
|
|
325
|
+
|
|
326
|
+
This example illustrates how the [`linearPalette()`]() command sequence
|
|
327
|
+
generator can be used to paint random dots with a brush which gets re-dipped in
|
|
328
|
+
different paints every 10 dots...
|
|
329
|
+
|
|
330
|
+
Also see
|
|
331
|
+
[`InterleaveOpts`](https://docs.thi.ng/umbrella/geom-axidraw/interfaces/InterleaveOpts.html)
|
|
332
|
+
for more details...
|
|
333
|
+
|
|
334
|
+
```ts
|
|
335
|
+
import { points } from "@thi.ng/geom";
|
|
336
|
+
import { asAxiDraw } from "@thi.ng/geom-axidraw";
|
|
337
|
+
import { repeatedly } from "@thi.ng/transducers";
|
|
338
|
+
import { randMinMax2 } from "@thi.ng/vectors";
|
|
339
|
+
|
|
340
|
+
// configure palette
|
|
341
|
+
// "linear" here means the palette slots are arranged in a line
|
|
342
|
+
// (there's also a radialPalette() function for circular/elliptical palette layouts)
|
|
343
|
+
const palette = linearPalette({
|
|
344
|
+
// first palette slot is near the world origin (slight offset)
|
|
345
|
+
pos: [2, 0],
|
|
346
|
+
// 2mm jitter radius (to not always move to exact same position)
|
|
347
|
+
jitter: 2,
|
|
348
|
+
// palette has 5 paint slots
|
|
349
|
+
num: 5,
|
|
350
|
+
// each slot 40mm separation along Y-axis
|
|
351
|
+
// (needs to be measured/determined manually)
|
|
352
|
+
step: [0, 40],
|
|
353
|
+
// dip brush 3x each time
|
|
354
|
+
repeat: 3,
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// define point cloud of 100 random points
|
|
358
|
+
// using a random palette slot each time (for each refill)
|
|
359
|
+
// assign axidraw-specific attribs to refill brush every 10 dots
|
|
360
|
+
const cloud = points(
|
|
361
|
+
// pick random points
|
|
362
|
+
[...repeatedly(() => randMinMax2([], [10, 10], [190, 190]), 100)],
|
|
363
|
+
// shape attributes
|
|
364
|
+
{
|
|
365
|
+
__axi: {
|
|
366
|
+
interleave: {
|
|
367
|
+
// every 10 elements/dots...
|
|
368
|
+
num: 10,
|
|
369
|
+
// execute these commands...
|
|
370
|
+
commands: () => [
|
|
371
|
+
// first brush cleaning in water
|
|
372
|
+
// (we decide to use the last palette slot for that)
|
|
373
|
+
...palette(4),
|
|
374
|
+
// now "refill" brush at a random other slot
|
|
375
|
+
...palette(Math.floor(Math.random() * 4))
|
|
376
|
+
]
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
// AxiDraw setup
|
|
383
|
+
const axi = new AxiDraw();
|
|
384
|
+
...
|
|
385
|
+
|
|
386
|
+
// convert geometry into axidraw commands and send to plotter
|
|
387
|
+
axi.draw(asAxiDraw(cloud));
|
|
388
|
+
```
|
|
389
|
+
|
|
262
390
|
Other selected toots/tweets:
|
|
263
391
|
|
|
264
392
|
- [Project announcement](https://mastodon.thi.ng/@toxi/109490174709589253)
|
|
@@ -268,39 +396,9 @@ Other selected toots/tweets:
|
|
|
268
396
|
- [Bitmap-to-vector conversions & shape sorting](https://mastodon.thi.ng/@toxi/109570540391689321)
|
|
269
397
|
- [Multi-color plotting](https://mastodon.thi.ng/@toxi/109586780630493994)
|
|
270
398
|
- [Using water color & paintbrush](https://mastodon.thi.ng/@toxi/110044424626641749)
|
|
399
|
+
- [Water color brush tests](https://mastodon.thi.ng/@toxi/110051629944117139)
|
|
271
400
|
- more to come...
|
|
272
401
|
|
|
273
|
-
### Available draw commands
|
|
274
|
-
|
|
275
|
-
All
|
|
276
|
-
[`DrawCommand`s](https://docs.thi.ng/umbrella/axidraw/types/DrawCommand.html)
|
|
277
|
-
are expressed as S-expression-like, [thi.ng/hiccup]()-style elements, aka JS
|
|
278
|
-
arrays/tuples of `[command, ...args]`. The following commands are supported. All
|
|
279
|
-
also as predefined constants or factory functions for the parametric ones:
|
|
280
|
-
|
|
281
|
-
| Command | Preset/factory |
|
|
282
|
-
|-----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------|
|
|
283
|
-
| `["comment", msg]` | [`COMMENT`](https://docs.thi.ng/umbrella/axidraw/functions/COMMENT.html) |
|
|
284
|
-
| `["d", delay?]` / `["u", delay?]` | [`DOWN`](https://docs.thi.ng/umbrella/axidraw/functions/DOWN.html) / [`UP`](https://docs.thi.ng/umbrella/axidraw/functions/UP.html) |
|
|
285
|
-
| `["home"]` | `HOME` |
|
|
286
|
-
| `["m", [x,y], speed?]` | [`MOVE_REL`](https://docs.thi.ng/umbrella/axidraw/functions/MOVE_REL.html) |
|
|
287
|
-
| `["M", [x,y], speed?]` | [`MOVE`](https://docs.thi.ng/umbrella/axidraw/functions/MOVE.html) |
|
|
288
|
-
| `["on"]` / `["off"]` | `ON` / `OFF` |
|
|
289
|
-
| `["pen", down, up]` | [`PEN`](https://docs.thi.ng/umbrella/axidraw/functions/PEN.html) |
|
|
290
|
-
| `["reset"]` | `RESET` |
|
|
291
|
-
| `["start"]` | `START` |
|
|
292
|
-
| `["stop"]` | `STOP` |
|
|
293
|
-
| `["w", delay]` | [`WAIT`](https://docs.thi.ng/umbrella/axidraw/functions/WAIT.html) |
|
|
294
|
-
|
|
295
|
-
#### Command sequence generators
|
|
296
|
-
|
|
297
|
-
Additionally, the following command sequence generators are provided (see their docs for code examples):
|
|
298
|
-
|
|
299
|
-
- [`complete`](https://docs.thi.ng/umbrella/axidraw/functions/complete.html)
|
|
300
|
-
- [`dip`](https://docs.thi.ng/umbrella/axidraw/functions/dip.html)
|
|
301
|
-
- [`polyline`](https://docs.thi.ng/umbrella/axidraw/functions/polyline.html)
|
|
302
|
-
- [`registrationMark`](https://docs.thi.ng/umbrella/axidraw/functions/registrationMark.html)
|
|
303
|
-
|
|
304
402
|
## Authors
|
|
305
403
|
|
|
306
404
|
- [Karsten Schmidt](https://thi.ng)
|
package/api.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { IDeref } from "@thi.ng/api";
|
|
2
2
|
import type { ILogger } from "@thi.ng/logger";
|
|
3
|
+
import type { Quantity } from "@thi.ng/units";
|
|
3
4
|
import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
4
5
|
/** Start command sequence (configurable via {@link AxiDrawOpts}) */
|
|
5
6
|
export type StartCommand = ["start"];
|
|
@@ -33,7 +34,15 @@ export type MoveRelCommand = ["m", ReadonlyVec, number?];
|
|
|
33
34
|
export type WaitCommand = ["w", number];
|
|
34
35
|
/** Ignored, but will be logged (if logging enabled) */
|
|
35
36
|
export type CommentCommand = ["comment", string];
|
|
36
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Stores current pen configuration on stack.
|
|
39
|
+
*/
|
|
40
|
+
export type SaveCommand = ["save"];
|
|
41
|
+
/**
|
|
42
|
+
* Restores current pen configuration from stack.
|
|
43
|
+
*/
|
|
44
|
+
export type RestoreCommand = ["restore"];
|
|
45
|
+
export type DrawCommand = CommentCommand | HomeCommand | MotorCommand | MoveRelCommand | MoveXYCommand | PenConfigCommand | PenUpDownCommand | ResetCommand | RestoreCommand | SaveCommand | StartCommand | StopCommand | WaitCommand;
|
|
37
46
|
/**
|
|
38
47
|
* Global plotter drawing configuration. Also see {@link DEFAULT_OPTS}.
|
|
39
48
|
*/
|
|
@@ -65,6 +74,28 @@ export interface AxiDrawOpts {
|
|
|
65
74
|
* all or if the control is in the {@link AxiDrawState.CONTINUE} state.
|
|
66
75
|
*/
|
|
67
76
|
control?: IDeref<AxiDrawState>;
|
|
77
|
+
/**
|
|
78
|
+
* All XY coords will be clamped to given bounding rect, either defined by
|
|
79
|
+
* `[[minX,minY], [maxX,maxY]]` (in worldspace units) or as paper size
|
|
80
|
+
* defined as a
|
|
81
|
+
* [`quantity`](https://docs.thi.ng/umbrella/units/functions/quantity-1.html).
|
|
82
|
+
* The default value is DIN A3 landscape.
|
|
83
|
+
*
|
|
84
|
+
* @remarks
|
|
85
|
+
* Set to `undefined` to disable bounds/clipping. If given a paper size (via
|
|
86
|
+
* thi.ng/units `quantity()`), the units used to define these dimensions are
|
|
87
|
+
* irrelevant (and independent of {@link AxiDrawOpts.unitsPerInch}!) and
|
|
88
|
+
* will be automatically converted. Also, the resulting bounds will always
|
|
89
|
+
* be based on [0, 0].
|
|
90
|
+
*
|
|
91
|
+
* List of paper sizes/presets:
|
|
92
|
+
* https://github.com/thi-ng/umbrella/blob/develop/packages/units/README.md#constants
|
|
93
|
+
*
|
|
94
|
+
* Also see {@link AxiDrawOpts.unitsPerInch}
|
|
95
|
+
*
|
|
96
|
+
* @defaultValue `DIN_A3_LANDSCAPE`
|
|
97
|
+
*/
|
|
98
|
+
bounds?: [ReadonlyVec, ReadonlyVec] | Quantity<number[]>;
|
|
68
99
|
/**
|
|
69
100
|
* Conversion factor from geometry worldspace units to inches.
|
|
70
101
|
* Default units are millimeters.
|
|
@@ -132,6 +163,22 @@ export interface AxiDrawOpts {
|
|
|
132
163
|
* @defaultValue `[UP, HOME, OFF]`
|
|
133
164
|
*/
|
|
134
165
|
stop: DrawCommand[];
|
|
166
|
+
/**
|
|
167
|
+
* Position (in world space units) which is considered the origin and which
|
|
168
|
+
* will be added to all coordinates as offset (e.g. for plotting with a
|
|
169
|
+
* physically offset easel).
|
|
170
|
+
*
|
|
171
|
+
* @remarks
|
|
172
|
+
* Note: The configured {@link AxiDrawOpts.bounds} are independent from this
|
|
173
|
+
* setting and are intended to enforce the plotter's physical movement
|
|
174
|
+
* limits. E.g. shifting the home/offset position by +100mm along X (and
|
|
175
|
+
* assuming only positive coordinates will be used for the drawing), simply
|
|
176
|
+
* means the available horizontal drawing space will be reduced by the same
|
|
177
|
+
* amount...
|
|
178
|
+
*
|
|
179
|
+
* @defaultValue [0, 0]
|
|
180
|
+
*/
|
|
181
|
+
home: ReadonlyVec;
|
|
135
182
|
/**
|
|
136
183
|
* Refresh interval for checking the control FSM in paused state.
|
|
137
184
|
*
|
package/axidraw.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IReset } from "@thi.ng/api";
|
|
2
|
-
import { ReadonlyVec, Vec } from "@thi.ng/vectors/api";
|
|
3
|
-
import { AxiDrawOpts, DrawCommand, ISerial, Metrics } from "./api.js";
|
|
2
|
+
import { type ReadonlyVec, type Vec, type VecPair } from "@thi.ng/vectors/api";
|
|
3
|
+
import { type AxiDrawOpts, type DrawCommand, type ISerial, type Metrics } from "./api.js";
|
|
4
4
|
export declare const DEFAULT_OPTS: AxiDrawOpts;
|
|
5
5
|
export declare class AxiDraw implements IReset {
|
|
6
6
|
serial: ISerial;
|
|
@@ -8,8 +8,12 @@ export declare class AxiDraw implements IReset {
|
|
|
8
8
|
isConnected: boolean;
|
|
9
9
|
isPenDown: boolean;
|
|
10
10
|
penLimits: [number, number];
|
|
11
|
+
penState: [number, number][];
|
|
11
12
|
pos: Vec;
|
|
12
13
|
targetPos: Vec;
|
|
14
|
+
homePos: ReadonlyVec;
|
|
15
|
+
scale: number;
|
|
16
|
+
bounds?: VecPair;
|
|
13
17
|
constructor(opts?: Partial<AxiDrawOpts>);
|
|
14
18
|
reset(): this;
|
|
15
19
|
/**
|
|
@@ -69,6 +73,8 @@ export declare class AxiDraw implements IReset {
|
|
|
69
73
|
draw1(cmd: DrawCommand): Promise<Metrics>;
|
|
70
74
|
motorsOn(): void;
|
|
71
75
|
motorsOff(): void;
|
|
76
|
+
save(): void;
|
|
77
|
+
restore(): void;
|
|
72
78
|
penConfig(down?: number, up?: number): void;
|
|
73
79
|
penUp(delay?: number, level?: number): number;
|
|
74
80
|
penDown(delay?: number, level?: number): number;
|
|
@@ -97,6 +103,7 @@ export declare class AxiDraw implements IReset {
|
|
|
97
103
|
* Syntax sugar for {@link AxiDraw.moveTo}([0, 0]).
|
|
98
104
|
*/
|
|
99
105
|
home(): number[];
|
|
106
|
+
setHome(pos: ReadonlyVec): void;
|
|
100
107
|
protected onSignal(): Promise<void>;
|
|
101
108
|
protected send(msg: string): void;
|
|
102
109
|
protected sendMove(tempo?: number): number[];
|
package/axidraw.js
CHANGED
|
@@ -5,8 +5,12 @@ import { assert } from "@thi.ng/errors/assert";
|
|
|
5
5
|
import { ioerror } from "@thi.ng/errors/io";
|
|
6
6
|
import { unsupported } from "@thi.ng/errors/unsupported";
|
|
7
7
|
import { ConsoleLogger } from "@thi.ng/logger/console";
|
|
8
|
+
import { DIN_A3_LANDSCAPE } from "@thi.ng/units/constants/paper-sizes";
|
|
9
|
+
import { convert, div, Quantity } from "@thi.ng/units/unit";
|
|
10
|
+
import { inch } from "@thi.ng/units/units/length";
|
|
8
11
|
import { abs2 } from "@thi.ng/vectors/abs";
|
|
9
|
-
import { ZERO2 } from "@thi.ng/vectors/api";
|
|
12
|
+
import { ZERO2, } from "@thi.ng/vectors/api";
|
|
13
|
+
import { clamp2 } from "@thi.ng/vectors/clamp";
|
|
10
14
|
import { maddN2 } from "@thi.ng/vectors/maddn";
|
|
11
15
|
import { mag } from "@thi.ng/vectors/mag";
|
|
12
16
|
import { mulN2 } from "@thi.ng/vectors/muln";
|
|
@@ -22,6 +26,8 @@ export const DEFAULT_OPTS = {
|
|
|
22
26
|
logger: new ConsoleLogger("axidraw"),
|
|
23
27
|
control: new AxiDrawControl(),
|
|
24
28
|
refresh: 1000,
|
|
29
|
+
bounds: DIN_A3_LANDSCAPE,
|
|
30
|
+
home: [0, 0],
|
|
25
31
|
unitsPerInch: 25.4,
|
|
26
32
|
stepsPerInch: 2032,
|
|
27
33
|
speedDown: 4000,
|
|
@@ -39,10 +45,26 @@ export class AxiDraw {
|
|
|
39
45
|
constructor(opts = {}) {
|
|
40
46
|
this.isConnected = false;
|
|
41
47
|
this.isPenDown = false;
|
|
48
|
+
this.penState = [];
|
|
42
49
|
this.pos = [0, 0];
|
|
43
50
|
this.targetPos = [0, 0];
|
|
44
51
|
this.opts = { ...DEFAULT_OPTS, ...opts };
|
|
45
52
|
this.penLimits = [this.opts.down, this.opts.up];
|
|
53
|
+
this.scale = this.opts.stepsPerInch / this.opts.unitsPerInch;
|
|
54
|
+
this.setHome(this.opts.home);
|
|
55
|
+
if (this.opts.bounds) {
|
|
56
|
+
this.bounds =
|
|
57
|
+
this.opts.bounds instanceof Quantity
|
|
58
|
+
? [
|
|
59
|
+
[0, 0],
|
|
60
|
+
convert(this.opts.bounds, div(inch, this.opts.stepsPerInch)),
|
|
61
|
+
]
|
|
62
|
+
: [
|
|
63
|
+
mulN2([], this.opts.bounds[0], this.scale),
|
|
64
|
+
mulN2([], this.opts.bounds[1], this.scale),
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
this.save();
|
|
46
68
|
}
|
|
47
69
|
reset() {
|
|
48
70
|
zero(this.pos);
|
|
@@ -185,6 +207,12 @@ export class AxiDraw {
|
|
|
185
207
|
wait = this.penDown(a, b);
|
|
186
208
|
penCommands++;
|
|
187
209
|
break;
|
|
210
|
+
case "save":
|
|
211
|
+
this.save();
|
|
212
|
+
break;
|
|
213
|
+
case "restore":
|
|
214
|
+
this.restore();
|
|
215
|
+
break;
|
|
188
216
|
case "w":
|
|
189
217
|
wait = a;
|
|
190
218
|
break;
|
|
@@ -207,6 +235,13 @@ export class AxiDraw {
|
|
|
207
235
|
logger.debug(`waiting ${wait}ms...`);
|
|
208
236
|
await delayed(0, wait);
|
|
209
237
|
}
|
|
238
|
+
// restore one-off pen config to current state
|
|
239
|
+
if (cmd === "d" && b !== undefined) {
|
|
240
|
+
this.sendPenConfig(5, this.penLimits[0]);
|
|
241
|
+
}
|
|
242
|
+
else if (cmd === "u" && b !== undefined) {
|
|
243
|
+
this.sendPenConfig(4, this.penLimits[1]);
|
|
244
|
+
}
|
|
210
245
|
}
|
|
211
246
|
const duration = Date.now() - t0;
|
|
212
247
|
if (showMetrics) {
|
|
@@ -239,6 +274,20 @@ export class AxiDraw {
|
|
|
239
274
|
motorsOff() {
|
|
240
275
|
this.send("EM,0,0\r");
|
|
241
276
|
}
|
|
277
|
+
save() {
|
|
278
|
+
this.opts.logger.debug("saving pen state:", this.penLimits);
|
|
279
|
+
this.penState.push(this.penLimits.slice());
|
|
280
|
+
}
|
|
281
|
+
restore() {
|
|
282
|
+
if (this.penState.length < 2) {
|
|
283
|
+
this.opts.logger.warn("stack underflow, can't restore pen state");
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const [down, up] = (this.penLimits = this.penState.pop());
|
|
287
|
+
this.sendPenConfig(5, down);
|
|
288
|
+
this.sendPenConfig(4, up);
|
|
289
|
+
this.opts.logger.debug("restored pen state:", this.penLimits);
|
|
290
|
+
}
|
|
242
291
|
penConfig(down, up) {
|
|
243
292
|
down = down !== undefined ? down : this.opts.down;
|
|
244
293
|
this.sendPenConfig(5, down);
|
|
@@ -278,9 +327,9 @@ export class AxiDraw {
|
|
|
278
327
|
* @param tempo
|
|
279
328
|
*/
|
|
280
329
|
moveTo(p, tempo) {
|
|
281
|
-
const {
|
|
282
|
-
// apply scale factor: worldspace units -> motor steps
|
|
283
|
-
|
|
330
|
+
const { homePos, scale, targetPos } = this;
|
|
331
|
+
// apply scale factor: worldspace units -> motor steps, add home offset
|
|
332
|
+
maddN2(targetPos, p, scale, homePos);
|
|
284
333
|
return this.sendMove(tempo);
|
|
285
334
|
}
|
|
286
335
|
/**
|
|
@@ -290,9 +339,9 @@ export class AxiDraw {
|
|
|
290
339
|
* @param tempo
|
|
291
340
|
*/
|
|
292
341
|
moveRelative(delta, tempo) {
|
|
293
|
-
const { pos,
|
|
342
|
+
const { pos, scale, targetPos } = this;
|
|
294
343
|
// apply scale factor: worldspace units -> motor steps
|
|
295
|
-
maddN2(targetPos, delta,
|
|
344
|
+
maddN2(targetPos, delta, scale, pos);
|
|
296
345
|
return this.sendMove(tempo);
|
|
297
346
|
}
|
|
298
347
|
/**
|
|
@@ -301,6 +350,10 @@ export class AxiDraw {
|
|
|
301
350
|
home() {
|
|
302
351
|
return this.moveTo(ZERO2);
|
|
303
352
|
}
|
|
353
|
+
setHome(pos) {
|
|
354
|
+
this.homePos = mulN2([], pos, this.scale);
|
|
355
|
+
this.opts.logger.debug("setting home position:", pos);
|
|
356
|
+
}
|
|
304
357
|
async onSignal() {
|
|
305
358
|
this.opts.logger.warn(`SIGNINT received, stop drawing...`);
|
|
306
359
|
this.penUp(0);
|
|
@@ -313,14 +366,16 @@ export class AxiDraw {
|
|
|
313
366
|
this.serial.write(msg);
|
|
314
367
|
}
|
|
315
368
|
sendMove(tempo = 1) {
|
|
316
|
-
const { pos, targetPos, opts, isPenDown } = this;
|
|
369
|
+
const { bounds, pos, scale, targetPos, opts, isPenDown } = this;
|
|
370
|
+
if (bounds)
|
|
371
|
+
clamp2(null, targetPos, ...bounds);
|
|
317
372
|
const delta = sub2([], targetPos, pos);
|
|
318
373
|
set2(pos, targetPos);
|
|
319
374
|
const maxAxis = Math.max(...abs2([], delta));
|
|
320
375
|
const duration = (1000 * maxAxis) /
|
|
321
376
|
((isPenDown ? opts.speedDown : opts.speedUp) * tempo);
|
|
322
377
|
this.send(`XM,${duration | 0},${delta[0] | 0},${delta[1] | 0}\r`);
|
|
323
|
-
return [duration,
|
|
378
|
+
return [duration, mag(delta) / scale];
|
|
324
379
|
}
|
|
325
380
|
/**
|
|
326
381
|
* Sends pen up/down config
|
package/commands.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
2
|
-
import type { CommentCommand, DrawCommand, HomeCommand, MotorCommand, MoveRelCommand, MoveXYCommand, PenConfigCommand, PenUpDownCommand, ResetCommand, StartCommand, StopCommand, WaitCommand } from "./api.js";
|
|
2
|
+
import type { CommentCommand, DrawCommand, HomeCommand, MotorCommand, MoveRelCommand, MoveXYCommand, PenConfigCommand, PenUpDownCommand, ResetCommand, RestoreCommand, SaveCommand, StartCommand, StopCommand, WaitCommand } from "./api.js";
|
|
3
3
|
export declare const START: StartCommand;
|
|
4
4
|
export declare const STOP: StopCommand;
|
|
5
5
|
export declare const HOME: HomeCommand;
|
|
6
6
|
export declare const RESET: ResetCommand;
|
|
7
|
+
export declare const ON: MotorCommand;
|
|
8
|
+
export declare const OFF: MotorCommand;
|
|
9
|
+
export declare const SAVE: SaveCommand;
|
|
10
|
+
export declare const RESTORE: RestoreCommand;
|
|
7
11
|
/**
|
|
8
12
|
* Creates a {@link PenConfigCommand} using provided down/up positions.
|
|
9
13
|
*
|
|
@@ -12,19 +16,21 @@ export declare const RESET: ResetCommand;
|
|
|
12
16
|
*/
|
|
13
17
|
export declare const PEN: (posDown?: number, posUp?: number) => PenConfigCommand;
|
|
14
18
|
/**
|
|
15
|
-
* Creates a {@link PenUpDownCommand} to move the pen up.
|
|
19
|
+
* Creates a {@link PenUpDownCommand} to move the pen up. Both args are optional
|
|
20
|
+
* and if omitted globally configured values are used.
|
|
16
21
|
*
|
|
17
22
|
* @param delay
|
|
23
|
+
* @param level
|
|
18
24
|
*/
|
|
19
|
-
export declare const UP: (delay?: number) => PenUpDownCommand;
|
|
25
|
+
export declare const UP: (delay?: number, level?: number) => PenUpDownCommand;
|
|
20
26
|
/**
|
|
21
|
-
* Creates a {@link PenUpDownCommand} to move the pen down.
|
|
27
|
+
* Creates a {@link PenUpDownCommand} to move the pen down. Both args are
|
|
28
|
+
* optional and if omitted globally configured values are used.
|
|
22
29
|
*
|
|
23
30
|
* @param delay
|
|
31
|
+
* @param level
|
|
24
32
|
*/
|
|
25
|
-
export declare const DOWN: (delay?: number) => PenUpDownCommand;
|
|
26
|
-
export declare const ON: MotorCommand;
|
|
27
|
-
export declare const OFF: MotorCommand;
|
|
33
|
+
export declare const DOWN: (delay?: number, level?: number) => PenUpDownCommand;
|
|
28
34
|
/**
|
|
29
35
|
* Creates a {@link MoveXYCommand} command (absolute coordinates).
|
|
30
36
|
*
|
package/commands.js
CHANGED
|
@@ -2,6 +2,10 @@ export const START = ["start"];
|
|
|
2
2
|
export const STOP = ["stop"];
|
|
3
3
|
export const HOME = ["home"];
|
|
4
4
|
export const RESET = ["reset"];
|
|
5
|
+
export const ON = ["on"];
|
|
6
|
+
export const OFF = ["off"];
|
|
7
|
+
export const SAVE = ["save"];
|
|
8
|
+
export const RESTORE = ["restore"];
|
|
5
9
|
/**
|
|
6
10
|
* Creates a {@link PenConfigCommand} using provided down/up positions.
|
|
7
11
|
*
|
|
@@ -14,19 +18,29 @@ export const PEN = (posDown, posUp) => [
|
|
|
14
18
|
posUp,
|
|
15
19
|
];
|
|
16
20
|
/**
|
|
17
|
-
* Creates a {@link PenUpDownCommand} to move the pen up.
|
|
21
|
+
* Creates a {@link PenUpDownCommand} to move the pen up. Both args are optional
|
|
22
|
+
* and if omitted globally configured values are used.
|
|
18
23
|
*
|
|
19
24
|
* @param delay
|
|
25
|
+
* @param level
|
|
20
26
|
*/
|
|
21
|
-
export const UP = (delay) => [
|
|
27
|
+
export const UP = (delay, level) => [
|
|
28
|
+
"u",
|
|
29
|
+
delay,
|
|
30
|
+
level,
|
|
31
|
+
];
|
|
22
32
|
/**
|
|
23
|
-
* Creates a {@link PenUpDownCommand} to move the pen down.
|
|
33
|
+
* Creates a {@link PenUpDownCommand} to move the pen down. Both args are
|
|
34
|
+
* optional and if omitted globally configured values are used.
|
|
24
35
|
*
|
|
25
36
|
* @param delay
|
|
37
|
+
* @param level
|
|
26
38
|
*/
|
|
27
|
-
export const DOWN = (delay) => [
|
|
28
|
-
|
|
29
|
-
|
|
39
|
+
export const DOWN = (delay, level) => [
|
|
40
|
+
"d",
|
|
41
|
+
delay,
|
|
42
|
+
level,
|
|
43
|
+
];
|
|
30
44
|
/**
|
|
31
45
|
* Creates a {@link MoveXYCommand} command (absolute coordinates).
|
|
32
46
|
*
|
package/dip.d.ts
CHANGED
|
@@ -2,18 +2,28 @@ import type { Fn0 } from "@thi.ng/api";
|
|
|
2
2
|
import type { DrawCommand } from "./api.js";
|
|
3
3
|
export interface DipOpts {
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* configured default.
|
|
5
|
+
* Pen level (0-99) for emitted {@link UP} commands. If omitted, uses
|
|
6
|
+
* currently configured default.
|
|
7
7
|
*/
|
|
8
8
|
up: number;
|
|
9
9
|
/**
|
|
10
|
-
* Delay for emitted {@link
|
|
10
|
+
* Delay for emitted {@link DOWN} commands. If omitted, uses currently
|
|
11
11
|
* configured default.
|
|
12
12
|
*/
|
|
13
|
+
upDelay: number;
|
|
14
|
+
/**
|
|
15
|
+
* Pen level (0-99) for emitted {@link DOWN} commands. If omitted, uses
|
|
16
|
+
* currently configured default.
|
|
17
|
+
*/
|
|
13
18
|
down: number;
|
|
19
|
+
/**
|
|
20
|
+
* Delay for emitted {@link UP} commands. If omitted, uses currently
|
|
21
|
+
* configured default.
|
|
22
|
+
*/
|
|
23
|
+
downDelay: number;
|
|
14
24
|
/**
|
|
15
25
|
* No-arg function to inject custom commands between each down - up command.
|
|
16
|
-
* See example in {@link
|
|
26
|
+
* See example in {@link dip} docs.
|
|
17
27
|
*/
|
|
18
28
|
commands: Fn0<Iterable<DrawCommand>>;
|
|
19
29
|
}
|
|
@@ -56,5 +66,5 @@ export interface DipOpts {
|
|
|
56
66
|
* @param n
|
|
57
67
|
* @param opts
|
|
58
68
|
*/
|
|
59
|
-
export declare const dip: (n: number, opts?: Partial<DipOpts>) =>
|
|
69
|
+
export declare const dip: (n: number, opts?: Partial<DipOpts>) => Iterable<DrawCommand>;
|
|
60
70
|
//# sourceMappingURL=dip.d.ts.map
|
package/dip.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { flatten1 } from "@thi.ng/transducers/flatten1";
|
|
2
2
|
import { repeatedly } from "@thi.ng/transducers/repeatedly";
|
|
3
|
-
import { DOWN, UP } from "./commands.js";
|
|
3
|
+
import { DOWN, RESTORE, SAVE, UP } from "./commands.js";
|
|
4
4
|
/**
|
|
5
5
|
* Yields a **sequence** of `n` repetitions of {@link DOWN}, {@link UP}
|
|
6
6
|
* commands, optionally interspersed with other user provided
|
|
@@ -40,6 +40,13 @@ import { DOWN, UP } from "./commands.js";
|
|
|
40
40
|
* @param n
|
|
41
41
|
* @param opts
|
|
42
42
|
*/
|
|
43
|
-
export const dip = (n, opts = {}) =>
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
export const dip = (n, opts = {}) => {
|
|
44
|
+
const down = DOWN(opts.downDelay, opts.down);
|
|
45
|
+
const up = UP(opts.upDelay, opts.up);
|
|
46
|
+
const main = flatten1(repeatedly(opts.commands
|
|
47
|
+
? () => [down, ...opts.commands(), up]
|
|
48
|
+
: () => [down, up], n));
|
|
49
|
+
return opts.down !== undefined || opts.up !== undefined
|
|
50
|
+
? [SAVE, ...main, RESTORE]
|
|
51
|
+
: main;
|
|
52
|
+
};
|
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": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Minimal AxiDraw plotter/drawing machine controller for Node.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -34,23 +34,25 @@
|
|
|
34
34
|
"test": "testament test"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@thi.ng/api": "^8.7.
|
|
38
|
-
"@thi.ng/checks": "^3.3.
|
|
39
|
-
"@thi.ng/compose": "^2.1.
|
|
40
|
-
"@thi.ng/date": "^2.4.
|
|
41
|
-
"@thi.ng/errors": "^2.2.
|
|
42
|
-
"@thi.ng/logger": "^1.4.
|
|
43
|
-
"@thi.ng/
|
|
44
|
-
"@thi.ng/
|
|
37
|
+
"@thi.ng/api": "^8.7.5",
|
|
38
|
+
"@thi.ng/checks": "^3.3.11",
|
|
39
|
+
"@thi.ng/compose": "^2.1.30",
|
|
40
|
+
"@thi.ng/date": "^2.4.10",
|
|
41
|
+
"@thi.ng/errors": "^2.2.14",
|
|
42
|
+
"@thi.ng/logger": "^1.4.12",
|
|
43
|
+
"@thi.ng/math": "^5.4.6",
|
|
44
|
+
"@thi.ng/transducers": "^8.4.1",
|
|
45
|
+
"@thi.ng/units": "^0.4.1",
|
|
46
|
+
"@thi.ng/vectors": "^7.6.10",
|
|
45
47
|
"serialport": "^10.5.0"
|
|
46
48
|
},
|
|
47
49
|
"devDependencies": {
|
|
48
50
|
"@microsoft/api-extractor": "^7.34.4",
|
|
49
|
-
"@thi.ng/testament": "^0.3.
|
|
50
|
-
"rimraf": "^4.4.
|
|
51
|
+
"@thi.ng/testament": "^0.3.14",
|
|
52
|
+
"rimraf": "^4.4.1",
|
|
51
53
|
"tools": "^0.0.1",
|
|
52
|
-
"typedoc": "^0.23.
|
|
53
|
-
"typescript": "^
|
|
54
|
+
"typedoc": "^0.23.28",
|
|
55
|
+
"typescript": "^5.0.2"
|
|
54
56
|
},
|
|
55
57
|
"keywords": [
|
|
56
58
|
"2d",
|
|
@@ -96,6 +98,9 @@
|
|
|
96
98
|
"./dip": {
|
|
97
99
|
"default": "./dip.js"
|
|
98
100
|
},
|
|
101
|
+
"./palettes": {
|
|
102
|
+
"default": "./palettes.js"
|
|
103
|
+
},
|
|
99
104
|
"./polyline": {
|
|
100
105
|
"default": "./polyline.js"
|
|
101
106
|
},
|
|
@@ -110,5 +115,5 @@
|
|
|
110
115
|
"status": "alpha",
|
|
111
116
|
"year": 2022
|
|
112
117
|
},
|
|
113
|
-
"gitHead": "
|
|
118
|
+
"gitHead": "83b15b34326d480cbca0472b20390d4d3bbb792a\n"
|
|
114
119
|
}
|
package/palettes.d.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { ReadonlyVec } from "@thi.ng/vectors/api";
|
|
2
|
+
import { type DipOpts } from "./dip.js";
|
|
3
|
+
export interface BasePaletteOpts extends Partial<DipOpts> {
|
|
4
|
+
/**
|
|
5
|
+
* Jitter radius for base position.
|
|
6
|
+
*
|
|
7
|
+
* @defaultValue 0
|
|
8
|
+
*/
|
|
9
|
+
jitter?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Number of palette slots.
|
|
12
|
+
*/
|
|
13
|
+
num: number;
|
|
14
|
+
/**
|
|
15
|
+
* Number of brush dips to execute.
|
|
16
|
+
*
|
|
17
|
+
* @defaultValue 1
|
|
18
|
+
*/
|
|
19
|
+
repeat?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface LinearPaletteOpts extends BasePaletteOpts {
|
|
22
|
+
/**
|
|
23
|
+
* Palette's base position (in world space units). See {@link AxiDrawOpts.unitsPerInch}.
|
|
24
|
+
*
|
|
25
|
+
* @defaultValue [0, 0]
|
|
26
|
+
*/
|
|
27
|
+
pos?: ReadonlyVec;
|
|
28
|
+
/**
|
|
29
|
+
* Step direction vector between palette slots.
|
|
30
|
+
*/
|
|
31
|
+
step: ReadonlyVec;
|
|
32
|
+
}
|
|
33
|
+
export interface RadialPaletteOpts extends BasePaletteOpts {
|
|
34
|
+
/**
|
|
35
|
+
* Palette's center position (in world space units). See {@link AxiDrawOpts.unitsPerInch}.
|
|
36
|
+
*/
|
|
37
|
+
pos: ReadonlyVec;
|
|
38
|
+
/**
|
|
39
|
+
* Palette radius (measured to the center of each palette slot). If
|
|
40
|
+
* circular, given as number. If elliptical, given as vector.
|
|
41
|
+
*/
|
|
42
|
+
radius: number | ReadonlyVec;
|
|
43
|
+
/**
|
|
44
|
+
* Start angle (in radians).
|
|
45
|
+
*
|
|
46
|
+
* @defaultValue 0
|
|
47
|
+
*/
|
|
48
|
+
startTheta?: number;
|
|
49
|
+
/**
|
|
50
|
+
* End angle (in radians). This is 2π for a full-circle palette.
|
|
51
|
+
*/
|
|
52
|
+
endTheta?: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Higher order {@link DrawCommand} sequence generator for working with paint
|
|
56
|
+
* brushes. Takes an config object describing a linear paint palette layout and
|
|
57
|
+
* behavior options for dipping the brush. Returns a function which takes a
|
|
58
|
+
* palette slot index as argument and returns a command sequence moving the
|
|
59
|
+
* plotter to the palette slot and dipping the brush to refill.
|
|
60
|
+
*
|
|
61
|
+
* @remarks
|
|
62
|
+
* Can be used with
|
|
63
|
+
* [InterleaveOpts](https://docs.thi.ng/umbrella/geom-axidraw/interfaces/InterleaveOpts.html)
|
|
64
|
+
* of https://thi.ng/geom-axidraw.
|
|
65
|
+
*
|
|
66
|
+
* Also see:
|
|
67
|
+
* - {@link LinearPaletteOpts} for options
|
|
68
|
+
* - {@link radialPalette} for circular/elliptical palette layouts
|
|
69
|
+
* - {@link dip} (used internally for dipping sequence)
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* import { points } from "@thi.ng/geom";
|
|
74
|
+
* import { asAxiDraw } from "@thi.ng/geom-axidraw";
|
|
75
|
+
* import { repeatedly } from "@thi.ng/transducers";
|
|
76
|
+
* import { randMinMax2 } from "@thi.ng/vectors";
|
|
77
|
+
*
|
|
78
|
+
* // configure palette
|
|
79
|
+
* const palette = linearPalette({
|
|
80
|
+
* // first palette slot is near the world origin (slight offset)
|
|
81
|
+
* pos: [2, 0],
|
|
82
|
+
* // palette has 5 paint slots
|
|
83
|
+
* num: 5,
|
|
84
|
+
* // each slot 40mm separation along Y-axis
|
|
85
|
+
* // (needs to be measured/determined manually)
|
|
86
|
+
* step: [0, 40],
|
|
87
|
+
* // 2mm jitter radius (to not always move to exact same position)
|
|
88
|
+
* jitter: 2,
|
|
89
|
+
* // dip brush 3x each time
|
|
90
|
+
* repeat: 3,
|
|
91
|
+
* });
|
|
92
|
+
*
|
|
93
|
+
* // investigate command sequence for requesting slot #1
|
|
94
|
+
* palette(1)
|
|
95
|
+
* // [
|
|
96
|
+
* // [ "M", [ 0.8949, 41.6697 ], 1 ],
|
|
97
|
+
* // [ "d", undefined, undefined ],
|
|
98
|
+
* // [ "u", undefined, undefined ],
|
|
99
|
+
* // [ "d", undefined, undefined ],
|
|
100
|
+
* // [ "u", undefined, undefined ],
|
|
101
|
+
* // [ "d", undefined, undefined ],
|
|
102
|
+
* // [ "u", undefined, undefined ]
|
|
103
|
+
* // ]
|
|
104
|
+
*
|
|
105
|
+
* // define point cloud of 100 random points
|
|
106
|
+
* // using a random palette slot each time (for each refill)
|
|
107
|
+
* // assign axidraw-specific attribs to refill brush every 10 dots
|
|
108
|
+
* const cloud = points(
|
|
109
|
+
* [...repeatedly(() => randMinMax2([], [10, 10], [190, 190]), 100)],
|
|
110
|
+
* {
|
|
111
|
+
* __axi: {
|
|
112
|
+
* interleave: {
|
|
113
|
+
* num: 10,
|
|
114
|
+
* commands: () => palette((Math.random() * 5) | 0)
|
|
115
|
+
* }
|
|
116
|
+
* }
|
|
117
|
+
* }
|
|
118
|
+
* );
|
|
119
|
+
*
|
|
120
|
+
* // AxiDraw setup
|
|
121
|
+
* const axi = new AxiDraw();
|
|
122
|
+
* ...
|
|
123
|
+
*
|
|
124
|
+
* // convert geometry into axidraw commands and send to plotter
|
|
125
|
+
* axi.draw(asAxiDraw(cloud));
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* @param opts
|
|
129
|
+
*/
|
|
130
|
+
export declare const linearPalette: (opts: LinearPaletteOpts) => (id: number) => import("./api.js").DrawCommand[];
|
|
131
|
+
/**
|
|
132
|
+
* Higher order {@link DrawCommand} sequence generator for working with paint
|
|
133
|
+
* brushes. Similar to {@link linearPalette}, but for circular or elliptic
|
|
134
|
+
* palette layouts.
|
|
135
|
+
*
|
|
136
|
+
* @remarks
|
|
137
|
+
* Also see:
|
|
138
|
+
* - {@link RadialPaletteOpts} for options
|
|
139
|
+
* - {@link linearPalette} for more details & code example
|
|
140
|
+
* - {@link dip} (used internally for dipping sequence)
|
|
141
|
+
*
|
|
142
|
+
* @param opts
|
|
143
|
+
*/
|
|
144
|
+
export declare const radialPalette: (opts: RadialPaletteOpts) => (id: number) => import("./api.js").DrawCommand[];
|
|
145
|
+
//# sourceMappingURL=palettes.d.ts.map
|
package/palettes.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { isNumber } from "@thi.ng/checks/is-number";
|
|
2
|
+
import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
|
|
3
|
+
import { cossin } from "@thi.ng/math/angle";
|
|
4
|
+
import { mix } from "@thi.ng/math/mix";
|
|
5
|
+
import { jitter } from "@thi.ng/vectors/jitter";
|
|
6
|
+
import { madd2 } from "@thi.ng/vectors/madd";
|
|
7
|
+
import { maddN2 } from "@thi.ng/vectors/maddn";
|
|
8
|
+
import { MOVE } from "./commands.js";
|
|
9
|
+
import { dip } from "./dip.js";
|
|
10
|
+
/**
|
|
11
|
+
* Higher order {@link DrawCommand} sequence generator for working with paint
|
|
12
|
+
* brushes. Takes an config object describing a linear paint palette layout and
|
|
13
|
+
* behavior options for dipping the brush. Returns a function which takes a
|
|
14
|
+
* palette slot index as argument and returns a command sequence moving the
|
|
15
|
+
* plotter to the palette slot and dipping the brush to refill.
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
18
|
+
* Can be used with
|
|
19
|
+
* [InterleaveOpts](https://docs.thi.ng/umbrella/geom-axidraw/interfaces/InterleaveOpts.html)
|
|
20
|
+
* of https://thi.ng/geom-axidraw.
|
|
21
|
+
*
|
|
22
|
+
* Also see:
|
|
23
|
+
* - {@link LinearPaletteOpts} for options
|
|
24
|
+
* - {@link radialPalette} for circular/elliptical palette layouts
|
|
25
|
+
* - {@link dip} (used internally for dipping sequence)
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { points } from "@thi.ng/geom";
|
|
30
|
+
* import { asAxiDraw } from "@thi.ng/geom-axidraw";
|
|
31
|
+
* import { repeatedly } from "@thi.ng/transducers";
|
|
32
|
+
* import { randMinMax2 } from "@thi.ng/vectors";
|
|
33
|
+
*
|
|
34
|
+
* // configure palette
|
|
35
|
+
* const palette = linearPalette({
|
|
36
|
+
* // first palette slot is near the world origin (slight offset)
|
|
37
|
+
* pos: [2, 0],
|
|
38
|
+
* // palette has 5 paint slots
|
|
39
|
+
* num: 5,
|
|
40
|
+
* // each slot 40mm separation along Y-axis
|
|
41
|
+
* // (needs to be measured/determined manually)
|
|
42
|
+
* step: [0, 40],
|
|
43
|
+
* // 2mm jitter radius (to not always move to exact same position)
|
|
44
|
+
* jitter: 2,
|
|
45
|
+
* // dip brush 3x each time
|
|
46
|
+
* repeat: 3,
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* // investigate command sequence for requesting slot #1
|
|
50
|
+
* palette(1)
|
|
51
|
+
* // [
|
|
52
|
+
* // [ "M", [ 0.8949, 41.6697 ], 1 ],
|
|
53
|
+
* // [ "d", undefined, undefined ],
|
|
54
|
+
* // [ "u", undefined, undefined ],
|
|
55
|
+
* // [ "d", undefined, undefined ],
|
|
56
|
+
* // [ "u", undefined, undefined ],
|
|
57
|
+
* // [ "d", undefined, undefined ],
|
|
58
|
+
* // [ "u", undefined, undefined ]
|
|
59
|
+
* // ]
|
|
60
|
+
*
|
|
61
|
+
* // define point cloud of 100 random points
|
|
62
|
+
* // using a random palette slot each time (for each refill)
|
|
63
|
+
* // assign axidraw-specific attribs to refill brush every 10 dots
|
|
64
|
+
* const cloud = points(
|
|
65
|
+
* [...repeatedly(() => randMinMax2([], [10, 10], [190, 190]), 100)],
|
|
66
|
+
* {
|
|
67
|
+
* __axi: {
|
|
68
|
+
* interleave: {
|
|
69
|
+
* num: 10,
|
|
70
|
+
* commands: () => palette((Math.random() * 5) | 0)
|
|
71
|
+
* }
|
|
72
|
+
* }
|
|
73
|
+
* }
|
|
74
|
+
* );
|
|
75
|
+
*
|
|
76
|
+
* // AxiDraw setup
|
|
77
|
+
* const axi = new AxiDraw();
|
|
78
|
+
* ...
|
|
79
|
+
*
|
|
80
|
+
* // convert geometry into axidraw commands and send to plotter
|
|
81
|
+
* axi.draw(asAxiDraw(cloud));
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* @param opts
|
|
85
|
+
*/
|
|
86
|
+
export const linearPalette = (opts) => {
|
|
87
|
+
const $opts = {
|
|
88
|
+
pos: [0, 0],
|
|
89
|
+
repeat: 1,
|
|
90
|
+
jitter: 0,
|
|
91
|
+
...opts,
|
|
92
|
+
};
|
|
93
|
+
const dipOpts = __dipOpts($opts);
|
|
94
|
+
return (id) => {
|
|
95
|
+
if (id < 0 || id >= $opts.num) {
|
|
96
|
+
illegalArgs(`invalid palette index: ${id}`);
|
|
97
|
+
}
|
|
98
|
+
return [
|
|
99
|
+
MOVE(jitter(null, maddN2([], $opts.step, id, $opts.pos), $opts.jitter)),
|
|
100
|
+
...dip($opts.repeat, dipOpts),
|
|
101
|
+
];
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Higher order {@link DrawCommand} sequence generator for working with paint
|
|
106
|
+
* brushes. Similar to {@link linearPalette}, but for circular or elliptic
|
|
107
|
+
* palette layouts.
|
|
108
|
+
*
|
|
109
|
+
* @remarks
|
|
110
|
+
* Also see:
|
|
111
|
+
* - {@link RadialPaletteOpts} for options
|
|
112
|
+
* - {@link linearPalette} for more details & code example
|
|
113
|
+
* - {@link dip} (used internally for dipping sequence)
|
|
114
|
+
*
|
|
115
|
+
* @param opts
|
|
116
|
+
*/
|
|
117
|
+
export const radialPalette = (opts) => {
|
|
118
|
+
const $opts = {
|
|
119
|
+
repeat: 1,
|
|
120
|
+
jitter: 0,
|
|
121
|
+
startTheta: 0,
|
|
122
|
+
endTheta: Math.PI * 2,
|
|
123
|
+
...opts,
|
|
124
|
+
};
|
|
125
|
+
const radius = isNumber($opts.radius)
|
|
126
|
+
? [$opts.radius, $opts.radius]
|
|
127
|
+
: $opts.radius;
|
|
128
|
+
const dipOpts = __dipOpts($opts);
|
|
129
|
+
return (id) => {
|
|
130
|
+
if (id < 0 || id >= $opts.num) {
|
|
131
|
+
illegalArgs(`invalid palette index: ${id}`);
|
|
132
|
+
}
|
|
133
|
+
return [
|
|
134
|
+
MOVE(jitter(null, madd2(null, cossin(mix($opts.startTheta, $opts.endTheta, id / $opts.num)), radius, $opts.pos), $opts.jitter)),
|
|
135
|
+
...dip($opts.repeat, dipOpts),
|
|
136
|
+
];
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
const __dipOpts = (opts) => ({
|
|
140
|
+
down: opts.down,
|
|
141
|
+
downDelay: opts.downDelay,
|
|
142
|
+
up: opts.up,
|
|
143
|
+
upDelay: opts.upDelay,
|
|
144
|
+
commands: opts.commands,
|
|
145
|
+
});
|