@thi.ng/geom-axidraw
Version:
Conversion and preparation of thi.ng/geom shapes & shape groups to/from AxiDraw pen plotter draw commands
123 lines (122 loc) • 3.97 kB
JavaScript
import { DOWN, MOVE, UP } from "@thi.ng/axidraw/commands";
import { polyline } from "@thi.ng/axidraw/polyline";
import { defmulti } from "@thi.ng/defmulti/defmulti";
import { clipPolylinePoly } from "@thi.ng/geom-clip-line/clip-poly";
import { pointInPolygon2 } from "@thi.ng/geom-isec/point";
import { applyTransforms } from "@thi.ng/geom/apply-transforms";
import { asPolyline } from "@thi.ng/geom/as-polyline";
import { __dispatch } from "@thi.ng/geom/internal/dispatch";
import { __sampleAttribs } from "@thi.ng/geom/internal/vertices";
import { withAttribs } from "@thi.ng/geom/with-attribs";
import { mapcat } from "@thi.ng/transducers/mapcat";
import { takeNth } from "@thi.ng/transducers/take-nth";
import { pointsByNearestNeighbor } from "./sort.js";
const asAxiDraw = defmulti(
__dispatch,
{
arc: "circle",
cubic: "circle",
ellipse: "circle",
line: "polyline",
path: "circle",
poly: "polyline",
quad: "polyline",
quadratic: "circle",
rect: "polyline",
tri: "polyline"
},
{
points: ($, opts) => __points(applyTransforms($).points, $.attribs, opts),
// used for all shapes which need to be sampled
circle: ($, opts) => mapcat(
(line) => __polyline(line.points, $.attribs, opts),
asPolyline(applyTransforms($), opts?.samples)
),
complexpoly: ($, opts) => mapcat(
(poly) => asAxiDraw(withAttribs(poly, $.attribs, false), opts),
[$.boundary, ...$.children]
),
// ignore sample opts for polyline & other polygonal shapes
// i.e. use points verbatim
polyline: ($, opts) => __polyline(
asPolyline(applyTransforms($))[0].points,
$.attribs,
opts
),
group: __group
}
);
function* __interleaved(emitChunk, items, opts) {
const { num, commands } = opts;
if (opts.start !== false) yield* commands(0);
for (let i = 0, n = items.length; i < n; ) {
yield* emitChunk(items.slice(i, i + num));
i += num;
if (i < n) yield* commands(i);
}
if (opts.end) yield* opts.commands(items.length);
}
function* __group($, opts) {
const $sampleOpts = __sampleAttribs(opts?.samples, $.attribs);
const { skip, sort, interleave } = __axiAttribs($.attribs);
const children = skip ? [...takeNth(skip + 1, $.children)] : $.children;
function* emitChunk(chunk) {
const iter = sort ? sort(chunk) : chunk;
for (const child of iter) {
const shape = applyTransforms(child);
shape.attribs = {
...$.attribs,
...shape.attribs,
__samples: __sampleAttribs($sampleOpts, shape.attribs)
};
yield* asAxiDraw(shape, opts);
}
}
yield* interleave ? __interleaved(emitChunk, children, interleave) : emitChunk(children);
}
function* __points(pts, attribs, opts) {
if (!pts.length) return;
const {
sort = pointsByNearestNeighbor(),
clip,
delayDown,
delayUp,
down,
interleave,
skip,
speed
} = __axiAttribs(attribs);
const clipPts = clip || opts?.clip;
if (clipPts) {
pts = pts.filter((p) => !!pointInPolygon2(p, clipPts));
if (!pts.length) return;
}
if (skip) {
pts = [...takeNth(skip + 1, pts)];
}
function* emitChunk($pts) {
if (down != void 0) yield ["pen", down];
for (const p of sort ? sort($pts) : $pts) {
yield MOVE(p, speed);
yield DOWN(delayDown);
yield UP(delayUp);
}
if (down != void 0) yield ["pen"];
}
yield UP();
yield* interleave ? __interleaved(emitChunk, pts, interleave) : emitChunk(pts);
}
function* __polyline(pts, attribs, opts) {
if (!pts.length) return;
const { clip, down, delayDown, delayUp, speed } = __axiAttribs(attribs);
const clipPts = clip || opts?.clip;
const chunks = clipPts ? clipPolylinePoly(pts, clipPts) : [pts];
if (!chunks.length) return;
for (const chunk of chunks) {
yield* polyline(chunk, { down, delayDown, delayUp, speed });
}
}
const __axiAttribs = (attribs) => attribs ? attribs.__axi || {} : {};
export {
asAxiDraw
};