Skip to content

Commit

Permalink
feat(geom-axidraw): add shape/point ordering support
Browse files Browse the repository at this point in the history
- add Point/ShapeOrdering types
- add pointsByProximity()/shapesByProximity() HOF ordering fns
- add AxiDrawAttribs.sort attrib
- add/update doc strings
- update deps
  • Loading branch information
postspectacular committed Dec 9, 2022
1 parent c43b6f5 commit 58b3b5d
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 12 deletions.
7 changes: 6 additions & 1 deletion packages/geom-axidraw/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,17 @@
"test": "testament test"
},
"dependencies": {
"@thi.ng/api": "^8.5.1",
"@thi.ng/arrays": "^2.4.4",
"@thi.ng/axidraw": "^0.1.0",
"@thi.ng/compare": "^2.1.18",
"@thi.ng/defmulti": "^2.1.23",
"@thi.ng/geom": "^3.4.23",
"@thi.ng/geom-api": "^3.3.20",
"@thi.ng/geom-clip-line": "^2.1.37",
"@thi.ng/geom-isec": "^2.1.37"
"@thi.ng/geom-isec": "^2.1.37",
"@thi.ng/geom-poly-utils": "^2.3.21",
"@thi.ng/vectors": "^7.5.26"
},
"devDependencies": {
"@microsoft/api-extractor": "^7.33.5",
Expand Down
27 changes: 27 additions & 0 deletions packages/geom-axidraw/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
import type { FnU } from "@thi.ng/api";
import type { IShape } from "@thi.ng/geom-api";
import type { ReadonlyVec } from "@thi.ng/vectors";

/**
* Package-specific shape attributes used to control conversion behavior. MUST
* be stored under the `__axi` attribute name.
*
* @example
* ```ts
* // a circle which will be drawn at 25% speed
* circle(100, { __axi: { speed: 0.25 } });
* ```
*/
export interface AxiDrawAttribs {
/**
* Clip polygon vertices. See {@link AsAxiDrawOpts.clip}.
Expand All @@ -19,4 +31,19 @@ export interface AxiDrawAttribs {
* Optional pen down position (%).
*/
down: number;
/**
* Ordering function (in lieu of full path planning/optimization, which is
* planned for a later stage). By default order of appearance is used.
*
* @remarks
* Currently available implementations:
*
* - {@link pointsByProximity} (for `points()`)
* - {@link shapesByProximity} (for `group()`)
*/
sort: PointOrdering | ShapeOrdering;
}

export type PointOrdering = FnU<ReadonlyVec[]>;

export type ShapeOrdering = FnU<IShape[]>;
50 changes: 39 additions & 11 deletions packages/geom-axidraw/src/as-axidraw.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { Predicate } from "@thi.ng/api";
import { DrawCommand, UP } from "@thi.ng/axidraw/api";
import { polyline } from "@thi.ng/axidraw/utils";
import type { MultiFn1O } from "@thi.ng/defmulti";
Expand All @@ -11,7 +10,7 @@ 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 type { ReadonlyVec } from "@thi.ng/vectors";
import type { AxiDrawAttribs } from "./api.js";
import type { AxiDrawAttribs, PointOrdering, ShapeOrdering } from "./api.js";

export interface AsAxiDrawOpts {
/**
Expand All @@ -31,6 +30,33 @@ export interface AsAxiDrawOpts {
clip: ReadonlyVec[];
}

/**
* Lazily converts given shape (or group) into an iterable of thi.ng/axidraw
* drawing commands, using optionally provided config options.
*
* @remarks
* The provided conversion options can (and will) be overridden by a shape's
* `__axi` attribute. See {@link AxiDrawAttribs} for details.
*
* Currently supported shape types (basically all types which
* are supported by the
* [`asPolyline()`](https://docs.thi.ng/umbrella/geom/functions/asPolyline.html)
* function):
*
* - arc
* - circle
* - cubic
* - ellipse
* - line
* - path
* - polygon
* - polyline
* - quad
* - quadratic
* - rect
* - triangle
*
*/
export const asAxiDraw: MultiFn1O<
IShape,
Partial<AsAxiDrawOpts>,
Expand Down Expand Up @@ -74,7 +100,9 @@ function* __group(
$: Group,
opts?: Partial<AsAxiDrawOpts>
): IterableIterator<DrawCommand> {
for (let child of $.children) {
const { sort } = __axiAttribs($.attribs);
const children = sort ? (<ShapeOrdering>sort)($.children) : $.children;
for (let child of children) {
const shape = applyTransforms(child);
shape.attribs = {
...shape.attribs,
Expand All @@ -90,17 +118,16 @@ function* __points(
opts?: Partial<AsAxiDrawOpts>
): IterableIterator<DrawCommand> {
if (!pts.length) return;
const { clip, delay, down, speed } = __axiAttribs(attribs);
const { clip, delay, down, speed, sort } = __axiAttribs(attribs);
const clipPts = clip || opts?.clip;
const clipFn: Predicate<ReadonlyVec> = clipPts
? (p: ReadonlyVec) => !!pointInPolygon2(p, clipPts)
: () => true;
if (clipPts) {
pts = pts.filter((p) => !!pointInPolygon2(p, clipPts));
if (!pts.length) return;
}
yield UP;
if (down != undefined) yield ["pen", down];
for (let p of pts) {
if (clipFn(p)) {
yield* [["m", p, speed], ["d", delay], UP];
}
for (let p of sort ? (<PointOrdering>sort)(pts) : pts) {
yield* [["m", p, speed], ["d", delay], UP];
}
if (down != undefined) yield ["pen"];
}
Expand All @@ -119,6 +146,7 @@ function* __polyline(
for (let chunk of chunks) {
yield* polyline(chunk, speed);
}
// reset pen to configured defaults
if (down != undefined) yield ["pen"];
}

Expand Down
1 change: 1 addition & 0 deletions packages/geom-axidraw/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./api.js";
export * from "./as-axidraw.js";
export * from "./sort.js";
44 changes: 44 additions & 0 deletions packages/geom-axidraw/src/sort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { sortByCachedKey } from "@thi.ng/arrays/sort-cached";
import { compareNumAsc } from "@thi.ng/compare/numeric";
import type { IShape } from "@thi.ng/geom-api";
import { bounds2 } from "@thi.ng/geom-poly-utils/bounds";
import { bounds } from "@thi.ng/geom/bounds";
import { centroid } from "@thi.ng/geom/centroid";
import { __collBounds } from "@thi.ng/geom/internal/bounds";
import { ReadonlyVec, ZERO2 } from "@thi.ng/vectors/api";
import { distSq2 } from "@thi.ng/vectors/distsq";
import type { PointOrdering, ShapeOrdering } from "./api.js";

/**
* Higher order point sorting fn. Sorts points by proximity to given `ref` point
* (default: bounds min).
*
* @param ref
*/
export const pointsByProximity =
(ref?: ReadonlyVec): PointOrdering =>
(pts: ReadonlyVec[]) => {
ref = ref || bounds2(pts)[0];
return sortByCachedKey(
pts.slice(),
(p) => distSq2(p, ref!),
compareNumAsc
);
};

/**
* Higher order shape sorting fn. Sorts shapes by their centroid's proximity to
* given `ref` point (default: bounds min).
*
* @param ref
*/
export const shapesByProximity =
(ref?: ReadonlyVec): ShapeOrdering =>
(shapes: IShape[]) => {
ref = ref || __collBounds(shapes, bounds)![0];
return sortByCachedKey(
shapes.slice(),
(s) => distSq2(centroid(s) || ZERO2, ref!),
compareNumAsc
);
};
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3046,13 +3046,18 @@ __metadata:
resolution: "@thi.ng/geom-axidraw@workspace:packages/geom-axidraw"
dependencies:
"@microsoft/api-extractor": ^7.33.5
"@thi.ng/api": ^8.5.1
"@thi.ng/arrays": ^2.4.4
"@thi.ng/axidraw": ^0.1.0
"@thi.ng/compare": ^2.1.18
"@thi.ng/defmulti": ^2.1.23
"@thi.ng/geom": ^3.4.23
"@thi.ng/geom-api": ^3.3.20
"@thi.ng/geom-clip-line": ^2.1.37
"@thi.ng/geom-isec": ^2.1.37
"@thi.ng/geom-poly-utils": ^2.3.21
"@thi.ng/testament": ^0.3.6
"@thi.ng/vectors": ^7.5.26
rimraf: ^3.0.2
tools: "workspace:^"
typedoc: ^0.23.20
Expand Down

0 comments on commit 58b3b5d

Please sign in to comment.