UNPKG

@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
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 };