@logomarks/core 0.1.9 → 0.2.0
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 +49 -0
- package/README.md +10 -1
- package/package.json +59 -4
- package/src/batch/args.d.ts +18 -0
- package/src/batch/args.js +44 -0
- package/src/batch/index.d.ts +7 -0
- package/src/batch/index.js +10 -0
- package/src/batch/run.d.ts +53 -0
- package/src/batch/run.js +92 -0
- package/src/batch/slug.d.ts +2 -0
- package/src/batch/slug.js +12 -0
- package/src/batch/source.d.ts +12 -0
- package/src/batch/source.js +23 -0
- package/src/fit/index.d.ts +4 -0
- package/src/fit/index.js +29 -5
- package/src/font/glyphs.d.ts +31 -0
- package/src/font/glyphs.js +228 -0
- package/src/font/index.d.ts +10 -0
- package/src/font/index.js +10 -0
- package/src/font/measure.d.ts +16 -0
- package/src/font/measure.js +201 -0
- package/src/font/moves.d.ts +42 -0
- package/src/font/moves.js +263 -0
- package/src/forms/discs.d.ts +20 -0
- package/src/forms/discs.js +114 -0
- package/src/forms/emblems.d.ts +22 -0
- package/src/forms/emblems.js +109 -0
- package/src/forms/flow.d.ts +21 -0
- package/src/forms/flow.js +80 -0
- package/src/forms/folds.d.ts +18 -0
- package/src/forms/folds.js +147 -0
- package/src/forms/geo.d.ts +46 -0
- package/src/forms/geo.js +233 -0
- package/src/forms/hooks.d.ts +40 -0
- package/src/forms/hooks.js +204 -0
- package/src/forms/index.d.ts +22 -0
- package/src/forms/index.js +24 -0
- package/src/forms/jog.d.ts +33 -0
- package/src/forms/jog.js +162 -0
- package/src/forms/lines.d.ts +32 -0
- package/src/forms/lines.js +233 -0
- package/src/forms/ref.d.ts +41 -0
- package/src/forms/ref.js +246 -0
- package/src/forms/shatter.d.ts +40 -0
- package/src/forms/shatter.js +341 -0
- package/src/forms/slats.d.ts +35 -0
- package/src/forms/slats.js +78 -0
- package/src/forms/swash.d.ts +23 -0
- package/src/forms/swash.js +74 -0
- package/src/forms/tech.d.ts +40 -0
- package/src/forms/tech.js +293 -0
- package/src/forms/tracks.d.ts +36 -0
- package/src/forms/tracks.js +121 -0
- package/src/geometry/index.d.ts +1 -0
- package/src/geometry/index.js +1 -0
- package/src/geometry/mitre.d.ts +8 -0
- package/src/geometry/mitre.js +64 -0
- package/src/geometry/modular.d.ts +1 -1
- package/src/index.js +2 -1
- package/src/iso/index.d.ts +5 -0
- package/src/iso/index.js +13 -0
- package/src/iso/objects.d.ts +168 -0
- package/src/iso/objects.js +1042 -0
- package/src/iso/planes.d.ts +27 -0
- package/src/iso/planes.js +138 -0
- package/src/iso/voxels.d.ts +43 -0
- package/src/iso/voxels.js +255 -0
- package/src/letters/blackletter.d.ts +31 -0
- package/src/letters/blackletter.js +199 -0
- package/src/letters/dotstroke.d.ts +16 -0
- package/src/letters/dotstroke.js +77 -0
- package/src/letters/echo.d.ts +42 -0
- package/src/letters/echo.js +246 -0
- package/src/letters/frame.d.ts +6 -0
- package/src/letters/frame.js +14 -0
- package/src/letters/index.d.ts +16 -0
- package/src/letters/index.js +18 -0
- package/src/letters/inktrap.d.ts +66 -0
- package/src/letters/inktrap.js +244 -0
- package/src/letters/mono.d.ts +145 -0
- package/src/letters/mono.js +684 -0
- package/src/letters/pillar.d.ts +31 -0
- package/src/letters/pillar.js +257 -0
- package/src/letters/serif.d.ts +109 -0
- package/src/letters/serif.js +666 -0
- package/src/letters/stair.d.ts +8 -0
- package/src/letters/stair.js +43 -0
- package/src/letters/stem.d.ts +29 -0
- package/src/letters/stem.js +205 -0
- package/src/objects/animals.d.ts +105 -0
- package/src/objects/animals.js +469 -0
- package/src/objects/bio.d.ts +14 -0
- package/src/objects/bio.js +76 -0
- package/src/objects/devglyphs.d.ts +34 -0
- package/src/objects/devglyphs.js +226 -0
- package/src/objects/devices.d.ts +31 -0
- package/src/objects/devices.js +114 -0
- package/src/objects/faces.d.ts +94 -0
- package/src/objects/faces.js +344 -0
- package/src/objects/food.d.ts +12 -0
- package/src/objects/food.js +74 -0
- package/src/objects/index.d.ts +13 -0
- package/src/objects/index.js +18 -0
- package/src/objects/rosettes.d.ts +14 -0
- package/src/objects/rosettes.js +64 -0
- package/src/objects/sport.d.ts +8 -0
- package/src/objects/sport.js +37 -0
- package/src/objects/weapons.d.ts +31 -0
- package/src/objects/weapons.js +179 -0
- package/src/parts/index.d.ts +2 -0
- package/src/parts/index.js +2 -0
- package/src/parts/pixels.d.ts +11 -0
- package/src/parts/pixels.js +74 -0
- package/src/parts/run.js +4 -2
- package/src/parts/span.d.ts +13 -0
- package/src/parts/span.js +100 -0
- package/src/pixels/dither.d.ts +78 -0
- package/src/pixels/dither.js +399 -0
- package/src/pixels/emote.d.ts +6 -0
- package/src/pixels/emote.js +76 -0
- package/src/pixels/index.d.ts +7 -0
- package/src/pixels/index.js +8 -0
- package/src/sdf/fields.js +135 -0
- package/src/sdf/gap.js +42 -0
- package/src/sdf/index.d.ts +58 -0
- package/src/sdf/index.js +14 -0
- package/src/sdf/ops.js +102 -0
- package/src/sdf/trace.js +90 -0
- package/src/sdf/y2k.d.ts +12 -0
- package/src/sdf/y2k.js +80 -0
- package/src/skill/bundle.js +1 -1
- package/src/util/vec.d.ts +14 -0
- package/src/util/vec.js +61 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export * from './geo.js';
|
|
2
|
+
export * from './discs.js';
|
|
3
|
+
export * from './hooks.js';
|
|
4
|
+
export * from './jog.js';
|
|
5
|
+
export * from './tech.js';
|
|
6
|
+
export * from './emblems.js';
|
|
7
|
+
export * from './shatter.js';
|
|
8
|
+
export * from './slats.js';
|
|
9
|
+
export * from './flow.js';
|
|
10
|
+
export * from './folds.js';
|
|
11
|
+
export * from './swash.js';
|
|
12
|
+
export * from './ref.js';
|
|
13
|
+
export * from './tracks.js';
|
|
14
|
+
export * from './lines.js';
|
|
15
|
+
export type { Hook } from './hooks.js';
|
|
16
|
+
export type { JogArm } from './jog.js';
|
|
17
|
+
export type { Rail, Shard, ShardMove } from './shatter.js';
|
|
18
|
+
export type { Slat, SlatRowOptions, SlatEdges } from './slats.js';
|
|
19
|
+
export type { FlowOptions } from './flow.js';
|
|
20
|
+
export type { SwashOptions } from './swash.js';
|
|
21
|
+
export type { StripeEdge } from './ref.js';
|
|
22
|
+
export type { TrackKit } from './tracks.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @logomarks/core/forms
|
|
3
|
+
*
|
|
4
|
+
* The constructions the Bold, Line and Track families are drawn with, on the 512 canvas with the house gap: needle
|
|
5
|
+
* gaps, knobs and split discs (geo), one disc cut so a moved piece keeps its gap (discs), capsules whose ends curl round
|
|
6
|
+
* a neighbour (hooks), jogged bursts and chamfer checkers (jog), sparkles, ribbons and keyed chevrons (tech), the split
|
|
7
|
+
* emblem's horizon and grounds (emblems), a form breaking into shards (shatter), turning slats, flow grids, fold seams
|
|
8
|
+
* and straps, swashes, rotation, stripes, guilloche and speed letters (ref), the Track editions' kit (tracks), and soft
|
|
9
|
+
* blobs, Rail lines, chamfered paths and interlocking overlaps (lines).
|
|
10
|
+
*/
|
|
11
|
+
export * from './geo.js';
|
|
12
|
+
export * from './discs.js';
|
|
13
|
+
export * from './hooks.js';
|
|
14
|
+
export * from './jog.js';
|
|
15
|
+
export * from './tech.js';
|
|
16
|
+
export * from './emblems.js';
|
|
17
|
+
export * from './shatter.js';
|
|
18
|
+
export * from './slats.js';
|
|
19
|
+
export * from './flow.js';
|
|
20
|
+
export * from './folds.js';
|
|
21
|
+
export * from './swash.js';
|
|
22
|
+
export * from './ref.js';
|
|
23
|
+
export * from './tracks.js';
|
|
24
|
+
export * from './lines.js';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Geom, Point, Polygon, Shape } from '../geometry/types.js';
|
|
2
|
+
|
|
3
|
+
/** One jogged arm's options. */
|
|
4
|
+
export interface JogArm {
|
|
5
|
+
r0?: number;
|
|
6
|
+
rb?: number;
|
|
7
|
+
breaks?: readonly number[];
|
|
8
|
+
R: number;
|
|
9
|
+
w: number;
|
|
10
|
+
g: number;
|
|
11
|
+
shift: number;
|
|
12
|
+
scarf?: number;
|
|
13
|
+
innerShift?: number;
|
|
14
|
+
cx?: number;
|
|
15
|
+
cy?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** The point `along` the axis at deg from (cx, cy), stepped `side` square to it. */
|
|
19
|
+
export function axisAt(deg: number, along: number, side?: number, cx?: number, cy?: number): Point;
|
|
20
|
+
/** A bar on the axis at deg from a0 to a1, w wide, `side` off the axis, its ends slanted by cut0 / cut1. */
|
|
21
|
+
export function axisBar(deg: number, a0: number, a1: number, w: number, side?: number, o?: { cut0?: number; cut1?: number; cx?: number; cy?: number }): Polygon;
|
|
22
|
+
/** One jogged arm: runs broken by g and stepped sideways. */
|
|
23
|
+
export function jogArm(deg: number, o: JogArm): Shape;
|
|
24
|
+
/** n jogged arms round the centre, the first at deg0. */
|
|
25
|
+
export function jogBurst(n: number, deg0: number, o: JogArm, each?: (i: number) => Partial<JogArm>): Shape;
|
|
26
|
+
/** A polygon with its corners cut at 45° by c (one or one per corner). */
|
|
27
|
+
export function chamfer(pts: readonly Point[], c: number | readonly number[]): Polygon;
|
|
28
|
+
/** A box from its corners, chamfered by c. */
|
|
29
|
+
export function chamferBox(x0: number, y0: number, x1: number, y1: number, c?: number | readonly number[]): Polygon;
|
|
30
|
+
/** A checker's dark cells, corner meetings cut so two squares are parted by exactly g. */
|
|
31
|
+
export function checkerCells(x0: number, y0: number, p: number, cols: number, rows: number, g: number, o?: { parity?: 0 | 1; outer?: number; skip?: (i: number, j: number) => boolean }): Shape;
|
|
32
|
+
/** Throws unless a stands at least g (less 0.5) clear of b. */
|
|
33
|
+
export function assertClear(a: Geom, b: Geom, g: number, what?: string): void;
|
package/src/forms/jog.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jog bursts and chamfer checkers. A jog burst is straight bars radiating from a centre, each broken part way along and
|
|
3
|
+
* its outer run stepped sideways, parallel to itself (a splice jog), so the burst looks mis-registered; the break is a gap
|
|
4
|
+
* g along the arm (square, or a scarf cut at an angle, measured square to the cut), so the two runs are parted by exactly
|
|
5
|
+
* the house gap wherever they face each other. A chamfer checker is a heavy slab with 45° chamfered corners and a small
|
|
6
|
+
* checker of chamfered squares tucked into a corner: two dark squares that meet corner to corner have that corner cut at
|
|
7
|
+
* 45° with legs of g/√2, so they are parted by exactly g and stay full size. Angles are degrees, y down. From the
|
|
8
|
+
* Logomarks tools (jogparts.mjs, batch 244, "Bold · Jog" and "Bold · Chamfer").
|
|
9
|
+
*/
|
|
10
|
+
import { C, U, I, poly, grow } from '../geometry/index.js';
|
|
11
|
+
|
|
12
|
+
/** @typedef {import('../geometry/types.js').Point} Point */
|
|
13
|
+
/** @typedef {import('../geometry/types.js').Polygon} Polygon */
|
|
14
|
+
/** @typedef {import('../geometry/types.js').Shape} Shape */
|
|
15
|
+
/** @typedef {import('../geometry/types.js').Geom} Geom */
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The point at distance `along` on the axis at angle `deg` from (cx, cy), stepped `side` square to it.
|
|
19
|
+
*
|
|
20
|
+
* @param {number} deg The axis's angle.
|
|
21
|
+
* @param {number} along The distance along it.
|
|
22
|
+
* @param {number} [side=0] The step square to it.
|
|
23
|
+
* @param {number} [cx=C] The axis's origin.
|
|
24
|
+
* @param {number} [cy=C] The axis's origin.
|
|
25
|
+
* @returns {Point} The point.
|
|
26
|
+
*/
|
|
27
|
+
export const axisAt = (deg, along, side = 0, cx = C, cy = C) => {
|
|
28
|
+
const a = (deg * Math.PI) / 180, c = Math.cos(a), s = Math.sin(a);
|
|
29
|
+
return [cx + c * along - s * side, cy + s * along + c * side];
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* A bar on the axis at `deg` from `a0` to `a1` along it, `w` wide, centred `side` off the axis. `cut0` / `cut1` slant its
|
|
34
|
+
* ends: the end line turned that many degrees off square (positive leans the end forward on the +side).
|
|
35
|
+
*
|
|
36
|
+
* @param {number} deg The axis's angle.
|
|
37
|
+
* @param {number} a0 Where the bar starts along it.
|
|
38
|
+
* @param {number} a1 Where it ends.
|
|
39
|
+
* @param {number} w Its width.
|
|
40
|
+
* @param {number} [side=0] Its offset from the axis.
|
|
41
|
+
* @param {{ cut0?: number, cut1?: number, cx?: number, cy?: number }} [o] The end slants and the axis's origin.
|
|
42
|
+
* @returns {Polygon} The bar.
|
|
43
|
+
*/
|
|
44
|
+
export const axisBar = (deg, a0, a1, w, side = 0, { cut0 = 0, cut1 = 0, cx = C, cy = C } = {}) => {
|
|
45
|
+
const t0 = Math.tan((cut0 * Math.PI) / 180) * (w / 2), t1 = Math.tan((cut1 * Math.PI) / 180) * (w / 2);
|
|
46
|
+
const P = (al, sd) => axisAt(deg, al, sd, cx, cy);
|
|
47
|
+
return poly([P(a0 - t0, side - w / 2), P(a1 - t1, side - w / 2), P(a1 + t1, side + w / 2), P(a0 + t0, side + w / 2)]);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* One arm of a jog burst.
|
|
52
|
+
*
|
|
53
|
+
* @typedef {object} JogArm
|
|
54
|
+
* @property {number} [r0] Where the inner run starts (0: the centre).
|
|
55
|
+
* @property {number} [rb] The break (or give `breaks`).
|
|
56
|
+
* @property {readonly number[]} [breaks] Several breaks, each run stepped `shift` further than the last.
|
|
57
|
+
* @property {number} R Where the outer run ends.
|
|
58
|
+
* @property {number} w The bar's width.
|
|
59
|
+
* @property {number} g The gap at each break.
|
|
60
|
+
* @property {number} shift Each run's step square to the axis (positive is clockwise on screen).
|
|
61
|
+
* @property {number} [scarf] The breaks' slant, degrees off square; the gap stays g square to the cut.
|
|
62
|
+
* @property {number} [innerShift] The inner run's own step.
|
|
63
|
+
* @property {number} [cx] The centre.
|
|
64
|
+
* @property {number} [cy] The centre.
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* One jogged arm on the axis at `deg`: an inner run from `r0` to the break at `rb`, a gap g, and an outer run on to `R`
|
|
69
|
+
* stepped `shift` square to the axis. A slanted face stepped sideways slides along the axis by shift·tan, so each later
|
|
70
|
+
* run starts that much further on.
|
|
71
|
+
*
|
|
72
|
+
* @param {number} deg The axis's angle.
|
|
73
|
+
* @param {JogArm} o The arm.
|
|
74
|
+
* @returns {Shape} The arm.
|
|
75
|
+
*/
|
|
76
|
+
export const jogArm = (deg, { r0 = 0, rb, breaks = [rb], R, w, g, shift, scarf = 0, innerShift = 0, cx = C, cy = C }) => {
|
|
77
|
+
const a = (scarf * Math.PI) / 180, half = g / 2 / Math.cos(a), t = Math.tan(a); // the axial half-gap that leaves g square to a slanted cut
|
|
78
|
+
const runs = []; let from = r0, side = innerShift;
|
|
79
|
+
breaks.forEach((b, i) => { runs.push(axisBar(deg, from, b - half, w, side, { cut0: i ? scarf : 0, cut1: scarf, cx, cy })); side += shift; from = b + half + shift * t; });
|
|
80
|
+
runs.push(axisBar(deg, from, R, w, side, { cut0: scarf, cx, cy }));
|
|
81
|
+
return U(...runs);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* n jogged arms round the centre, the first at `deg0`; `each(i)` may override any jogArm option per arm (a shift's sign, a
|
|
86
|
+
* break radius). The inner runs meet at the centre when r0 is 0.
|
|
87
|
+
*
|
|
88
|
+
* @param {number} n The number of arms.
|
|
89
|
+
* @param {number} deg0 The first arm's angle.
|
|
90
|
+
* @param {JogArm} o Every arm's options.
|
|
91
|
+
* @param {(i: number) => Partial<JogArm>} [each] Per-arm overrides.
|
|
92
|
+
* @returns {Shape} The burst.
|
|
93
|
+
*/
|
|
94
|
+
export const jogBurst = (n, deg0, o, each = () => ({})) => U(...[...Array(n)].map((_, i) => jogArm(deg0 + (i * 360) / n, { ...o, ...each(i) })));
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* A polygon with each corner cut at 45° (along the line square to its bisector) by `c` along both edges: a number for
|
|
98
|
+
* every corner or one per corner, 0 keeping it sharp. Points run in any order; corners are cut symmetrically.
|
|
99
|
+
*
|
|
100
|
+
* @param {readonly Point[]} pts The corners.
|
|
101
|
+
* @param {number | readonly number[]} c The cut.
|
|
102
|
+
* @returns {Polygon} The chamfered polygon.
|
|
103
|
+
*/
|
|
104
|
+
export const chamfer = (pts, c) => {
|
|
105
|
+
const n = pts.length, cs = Array.isArray(c) ? c : pts.map(() => c), out = [];
|
|
106
|
+
for (let i = 0; i < n; i++) {
|
|
107
|
+
const p = pts[i], a = pts[(i - 1 + n) % n], b = pts[(i + 1) % n], k = cs[i];
|
|
108
|
+
if (!k) { out.push(p); continue; }
|
|
109
|
+
const ua = [(a[0] - p[0]) / Math.hypot(a[0] - p[0], a[1] - p[1]), (a[1] - p[1]) / Math.hypot(a[0] - p[0], a[1] - p[1])];
|
|
110
|
+
const ub = [(b[0] - p[0]) / Math.hypot(b[0] - p[0], b[1] - p[1]), (b[1] - p[1]) / Math.hypot(b[0] - p[0], b[1] - p[1])];
|
|
111
|
+
out.push([p[0] + ua[0] * k, p[1] + ua[1] * k], [p[0] + ub[0] * k, p[1] + ub[1] * k]);
|
|
112
|
+
}
|
|
113
|
+
return poly(out);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* A box from its corners, its corners (top-left, top-right, bottom-right, bottom-left) chamfered by c.
|
|
118
|
+
*
|
|
119
|
+
* @param {number} x0 Left.
|
|
120
|
+
* @param {number} y0 Top.
|
|
121
|
+
* @param {number} x1 Right.
|
|
122
|
+
* @param {number} y1 Bottom.
|
|
123
|
+
* @param {number | readonly number[]} [c=0] The chamfer, for every corner or one per corner.
|
|
124
|
+
* @returns {Polygon} The box.
|
|
125
|
+
*/
|
|
126
|
+
export const chamferBox = (x0, y0, x1, y1, c = 0) => chamfer([[x0, y0], [x1, y0], [x1, y1], [x0, y1]], c);
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* A checker of `cols` × `rows` cells of size p from (x0, y0): the dark cells (where (i + j) % 2 === parity, less any
|
|
130
|
+
* `skip(i, j)`) as full squares, each corner that meets another dark square cut at 45° with legs of g/√2, so two squares
|
|
131
|
+
* meeting corner to corner are parted by exactly g; `outer` chamfers every other corner too.
|
|
132
|
+
*
|
|
133
|
+
* @param {number} x0 Left.
|
|
134
|
+
* @param {number} y0 Top.
|
|
135
|
+
* @param {number} p The cell size.
|
|
136
|
+
* @param {number} cols Cells across.
|
|
137
|
+
* @param {number} rows Cells down.
|
|
138
|
+
* @param {number} g The gap.
|
|
139
|
+
* @param {{ parity?: 0 | 1, outer?: number, skip?: (i: number, j: number) => boolean }} [o] Which cells are dark, the
|
|
140
|
+
* free corners' chamfer (0: sharp) and cells to leave out.
|
|
141
|
+
* @returns {Shape} The checker.
|
|
142
|
+
*/
|
|
143
|
+
export const checkerCells = (x0, y0, p, cols, rows, g, { parity = 0, outer = 0, skip = () => false } = {}) => {
|
|
144
|
+
const dark = (i, j) => i >= 0 && j >= 0 && i < cols && j < rows && (i + j) % 2 === parity && !skip(i, j), c = g / Math.SQRT2, out = [];
|
|
145
|
+
for (let j = 0; j < rows; j++) for (let i = 0; i < cols; i++) if (dark(i, j)) {
|
|
146
|
+
const cut = [[-1, -1], [1, -1], [1, 1], [-1, 1]].map(([di, dj]) => (dark(i + di, j + dj) ? c : outer));
|
|
147
|
+
out.push(chamferBox(x0 + i * p, y0 + j * p, x0 + (i + 1) * p, y0 + (j + 1) * p, cut));
|
|
148
|
+
}
|
|
149
|
+
return U(...out);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Throws unless every part of `a` stands at least g (less a half-unit tolerance) clear of `b`.
|
|
154
|
+
*
|
|
155
|
+
* @param {Geom} a One shape.
|
|
156
|
+
* @param {Geom} b The other.
|
|
157
|
+
* @param {number} g The gap.
|
|
158
|
+
* @param {string} [what='parts'] What to call them in the error.
|
|
159
|
+
* @returns {void}
|
|
160
|
+
* @throws {Error} When they come closer than the gap.
|
|
161
|
+
*/
|
|
162
|
+
export const assertClear = (a, b, g, what = 'parts') => { if (I(grow(a, g - 0.5), b).length) throw new Error(`${what} closer than the gap`); };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Point, Polygon, Shape, Geom } from '../geometry/types.js';
|
|
2
|
+
|
|
3
|
+
/** A blob: radius R wobbled by sine terms [k, amplitude, phase]; sy squashes it. */
|
|
4
|
+
export function blob(cx: number, cy: number, R: number, terms?: readonly (readonly number[])[], sy?: number, n?: number): Polygon;
|
|
5
|
+
/** The same blob grown by g all round. */
|
|
6
|
+
export function blobGrown(cx: number, cy: number, R: number, terms: readonly (readonly number[])[], sy?: number, g?: number): Polygon;
|
|
7
|
+
/** The Cairo pentagonal tiling on unit cells round the origin (t = 0.2257: every edge equal). */
|
|
8
|
+
export function cairo(t?: number): Point[][];
|
|
9
|
+
/** The polygons whose centres lie within r of (cx, cy). */
|
|
10
|
+
export function near<T extends readonly (readonly number[])[]>(list: readonly T[], cx: number, cy: number, r: number): T[];
|
|
11
|
+
/** A polyline with every inner corner cut at 45° by c (or one per inner corner). */
|
|
12
|
+
export function chamPath(pts: readonly Point[], c: number | readonly number[]): Point[];
|
|
13
|
+
/** A Rail: two tracks w wide, pitch apart, along an open polyline, corners mitred, ends flat. */
|
|
14
|
+
export function railsOf(pts: readonly Point[], w?: number, pitch?: number): Shape;
|
|
15
|
+
/** A closed Rail round a regular n-gon whose centre path has inradius r. */
|
|
16
|
+
export function railRing(cx: number, cy: number, r: number, n: number, deg?: number, w?: number, pitch?: number): Shape;
|
|
17
|
+
/** A closed Rail along any polygon, corners mitred. */
|
|
18
|
+
export function railLoop(pts: readonly Point[], w?: number, pitch?: number): Shape;
|
|
19
|
+
/** Points from a to b bowed sideways by k times the length at the middle. */
|
|
20
|
+
export function bow(a: readonly number[], b: readonly number[], k: number, n?: number): Point[];
|
|
21
|
+
/** A slot g wide leaving the canvas centre at angle a, from `from` to `to` along it. */
|
|
22
|
+
export function slot(a: number, g: number, from?: number, to?: number): Polygon;
|
|
23
|
+
/** The two half-planes either side of the line p→q: [left, right]. */
|
|
24
|
+
export function halves(p: readonly number[], q: readonly number[]): Polygon[];
|
|
25
|
+
/** Shapes in a chain along p→q, each in front of the next on the left and behind it on the right, gap g. */
|
|
26
|
+
export function lockChain(list: readonly Geom[], p: readonly number[], q: readonly number[], g?: number): Shape;
|
|
27
|
+
/** a and b overlapped: a in front left of p→q, b in front right of it. */
|
|
28
|
+
export function lockAlong(a: Geom, b: Geom, p: readonly number[], q: readonly number[], g?: number): Shape;
|
|
29
|
+
/** The least distance between two shapes' outline points. */
|
|
30
|
+
export function gapBetween(a: Geom, b: Geom): number;
|
|
31
|
+
/** A part slid along deg until it sits exactly g clear of `fixed`. */
|
|
32
|
+
export function slideToGap(fixed: Geom, part: Geom, deg: number, g: number, far?: number): Geom;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lines, blobs, rails and locks several families share: soft blobs, the Cairo tiling and a centre picker, chamfered
|
|
3
|
+
* polylines, Rails (two tracks along a line, corners mitred sharp), bowed lines and the Y of gaps, interlocking overlaps
|
|
4
|
+
* (each shape in front of the next on one side of a line and behind it on the other), and placing one part exactly a
|
|
5
|
+
* gap clear of another.
|
|
6
|
+
* From the Logomarks tools (shared.mjs's generic half: batches 14, 101, 116, 117, 163, 166, 169, 178, 187, 255).
|
|
7
|
+
*/
|
|
8
|
+
import { C, TAU, U, D, I, X, poly, sample, stroke, grown, regular, move, GAP as G, mitreOffset as mitreOff } from '../geometry/index.js';
|
|
9
|
+
|
|
10
|
+
/* ---------- soft blobs ---------- */
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A blob: radius R wobbled by sine terms, each [k, amplitude, phase] (r = 1 + Σ a·sin(k·t + p)); sy squashes it
|
|
14
|
+
* vertically.
|
|
15
|
+
*
|
|
16
|
+
* @param {number} cx The centre.
|
|
17
|
+
* @param {number} cy The centre.
|
|
18
|
+
* @param {number} R The radius.
|
|
19
|
+
* @param {readonly (readonly number[])[]} [terms=[]] The wobble's terms.
|
|
20
|
+
* @param {number} [sy=1] The vertical squash.
|
|
21
|
+
* @param {number} [n=360] Points round it.
|
|
22
|
+
* @returns {import('../geometry/types.js').Polygon} The blob.
|
|
23
|
+
*/
|
|
24
|
+
export const blob = (cx, cy, R, terms = [], sy = 1, n = 360) =>
|
|
25
|
+
poly(sample((t) => { let r = 1; for (const [k, a, p] of terms) r += a * Math.sin(k * t + p); return [cx + R * r * Math.cos(t), cy + sy * R * r * Math.sin(t)]; }, 0, TAU, n).slice(0, -1));
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The same blob grown by g all round (near enough), to cut a gap round it.
|
|
29
|
+
*
|
|
30
|
+
* @param {number} cx The centre.
|
|
31
|
+
* @param {number} cy The centre.
|
|
32
|
+
* @param {number} R The radius.
|
|
33
|
+
* @param {readonly (readonly number[])[]} terms The wobble's terms.
|
|
34
|
+
* @param {number} [sy=1] The vertical squash.
|
|
35
|
+
* @param {number} [g=GAP] How far it grows.
|
|
36
|
+
* @returns {import('../geometry/types.js').Polygon} The grown blob.
|
|
37
|
+
*/
|
|
38
|
+
export const blobGrown = (cx, cy, R, terms, sy = 1, g = G) => blob(cx, cy, R + g, terms, sy);
|
|
39
|
+
|
|
40
|
+
/* ---------- tiling ---------- */
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The Cairo pentagonal tiling, equilateral, on a 7 × 7 block of unit cells round the origin: square cells alternately
|
|
44
|
+
* crossed by a short horizontal or vertical bar, each bar end joined to the two nearest corners.
|
|
45
|
+
*
|
|
46
|
+
* @param {number} [t=0.2257] The bar's share (0.2257: every edge the same length).
|
|
47
|
+
* @returns {import('../geometry/types.js').Point[][]} The pentagons.
|
|
48
|
+
*/
|
|
49
|
+
export const cairo = (t = 0.2257) => {
|
|
50
|
+
const out = [];
|
|
51
|
+
for (let i = -3; i <= 3; i++) for (let j = -3; j <= 3; j++) {
|
|
52
|
+
if ((i + j) % 2 === 0) out.push([[i + t, j + 0.5], [i + 1 - t, j + 0.5], [i + 1, j + 1], [i + 0.5, j + 1 + t], [i, j + 1]], [[i + t, j + 0.5], [i, j], [i + 0.5, j - t], [i + 1, j], [i + 1 - t, j + 0.5]]);
|
|
53
|
+
else out.push([[i + 0.5, j + t], [i + 0.5, j + 1 - t], [i, j + 1], [i - t, j + 0.5], [i, j]], [[i + 0.5, j + t], [i + 1, j], [i + 1 + t, j + 0.5], [i + 1, j + 1], [i + 0.5, j + 1 - t]]);
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The polygons whose centres (the mean of their points) lie within r of (cx, cy).
|
|
60
|
+
*
|
|
61
|
+
* @template {readonly (readonly number[])[]} T
|
|
62
|
+
* @param {readonly T[]} list The polygons, as point lists.
|
|
63
|
+
* @param {number} cx The centre.
|
|
64
|
+
* @param {number} cy The centre.
|
|
65
|
+
* @param {number} r The reach.
|
|
66
|
+
* @returns {T[]} Those near it.
|
|
67
|
+
*/
|
|
68
|
+
export const near = (list, cx, cy, r) => list.filter((pl) => { const m = pl.reduce((a, p) => [a[0] + p[0] / pl.length, a[1] + p[1] / pl.length], [0, 0]); return Math.hypot(m[0] - cx, m[1] - cy) <= r; });
|
|
69
|
+
|
|
70
|
+
/* ---------- chamfered and mitred lines ---------- */
|
|
71
|
+
|
|
72
|
+
const add2 = (a, b) => [a[0] + b[0], a[1] + b[1]], sub2 = (a, b) => [a[0] - b[0], a[1] - b[1]], mul2 = (a, k) => [a[0] * k, a[1] * k];
|
|
73
|
+
const unit2 = (v) => { const l = Math.hypot(...v) || 1; return [v[0] / l, v[1] / l]; };
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* A polyline with every inner corner cut at 45° by c.
|
|
77
|
+
*
|
|
78
|
+
* @param {readonly import('../geometry/types.js').Point[]} pts The polyline.
|
|
79
|
+
* @param {number | readonly number[]} c The cut (or one per inner corner; 0 keeps the corner).
|
|
80
|
+
* @returns {import('../geometry/types.js').Point[]} The chamfered polyline.
|
|
81
|
+
*/
|
|
82
|
+
export const chamPath = (pts, c) => {
|
|
83
|
+
const out = [pts[0]];
|
|
84
|
+
for (let i = 1; i < pts.length - 1; i++) {
|
|
85
|
+
const k = Array.isArray(c) ? c[i - 1] : c, p = pts[i];
|
|
86
|
+
if (!k) { out.push(p); continue; } const u = unit2(sub2(p, pts[i - 1])), v = unit2(sub2(pts[i + 1], p)); out.push(sub2(p, mul2(u, k)), add2(p, mul2(v, k)));
|
|
87
|
+
}
|
|
88
|
+
out.push(pts.at(-1)); return out;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A Rail: two tracks w wide, pitch apart, along an open polyline, every corner mitred sharp and the ends cut flat (the
|
|
93
|
+
* Duo's 26 and 48 by default).
|
|
94
|
+
*
|
|
95
|
+
* @param {readonly import('../geometry/types.js').Point[]} pts The centre line.
|
|
96
|
+
* @param {number} [w=26] Each track's width.
|
|
97
|
+
* @param {number} [pitch=48] Track to track, centre to centre.
|
|
98
|
+
* @returns {import('../geometry/types.js').Shape} The Rail.
|
|
99
|
+
*/
|
|
100
|
+
export const railsOf = (pts, w = 26, pitch = 48) => U(stroke(mitreOff(pts, pitch / 2), w, { chunk: 4 }), stroke(mitreOff(pts, -pitch / 2), w, { chunk: 4 }));
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* A closed Rail: the two tracks (w wide, pitch apart) of a regular n-gon whose centre path has inradius r, corners
|
|
104
|
+
* mitred sharp.
|
|
105
|
+
*
|
|
106
|
+
* @param {number} cx The centre.
|
|
107
|
+
* @param {number} cy The centre.
|
|
108
|
+
* @param {number} r The centre path's inradius.
|
|
109
|
+
* @param {number} n Sides.
|
|
110
|
+
* @param {number} [deg=-90] The first corner's angle.
|
|
111
|
+
* @param {number} [w=26] Each track's width.
|
|
112
|
+
* @param {number} [pitch=48] Track to track.
|
|
113
|
+
* @returns {import('../geometry/types.js').Shape} The Rail ring.
|
|
114
|
+
*/
|
|
115
|
+
export const railRing = (cx, cy, r, n, deg = -90, w = 26, pitch = 48) => {
|
|
116
|
+
const ng = (ri) => regular(cx, cy, ri / Math.cos(Math.PI / n), n, deg);
|
|
117
|
+
return U(D(ng(r + pitch / 2 + w / 2), ng(r + pitch / 2 - w / 2)), D(ng(r - pitch / 2 + w / 2), ng(r - pitch / 2 - w / 2)));
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* A closed Rail along any polygon (concave too), every corner mitred sharp.
|
|
122
|
+
*
|
|
123
|
+
* @param {readonly import('../geometry/types.js').Point[]} pts The centre path (open: no repeated point).
|
|
124
|
+
* @param {number} [w=26] Each track's width.
|
|
125
|
+
* @param {number} [pitch=48] Track to track.
|
|
126
|
+
* @returns {import('../geometry/types.js').Shape} The Rail loop.
|
|
127
|
+
*/
|
|
128
|
+
export const railLoop = (pts, w = 26, pitch = 48) => {
|
|
129
|
+
const off = (d) => poly(mitreOff([pts.at(-1), ...pts, pts[0]], d).slice(1, -1));
|
|
130
|
+
return U(X(off(pitch / 2 + w / 2), off(pitch / 2 - w / 2)), X(off(-pitch / 2 + w / 2), off(-pitch / 2 - w / 2)));
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/* ---------- bows, slots and locks ---------- */
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Points from a to b bowed sideways at the middle by k times the length (a parabola; k < 0 bows to the right of a→b,
|
|
137
|
+
* y down).
|
|
138
|
+
*
|
|
139
|
+
* @param {readonly number[]} a The start.
|
|
140
|
+
* @param {readonly number[]} b The end.
|
|
141
|
+
* @param {number} k The bow, as a share of the length.
|
|
142
|
+
* @param {number} [n=24] Steps.
|
|
143
|
+
* @returns {import('../geometry/types.js').Point[]} The bowed line.
|
|
144
|
+
*/
|
|
145
|
+
export const bow = (a, b, k, n = 24) => {
|
|
146
|
+
const dx = b[0] - a[0], dy = b[1] - a[1], L = Math.hypot(dx, dy), nx = dy / L, ny = -dx / L;
|
|
147
|
+
return Array.from({ length: n + 1 }, (_, i) => { const t = i / n, h = k * L * 4 * t * (1 - t); return [a[0] + dx * t + nx * h, a[1] + dy * t + ny * h]; });
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* A slot g wide leaving the canvas centre at angle a, from `from` to `to` along it (three of them make the Y of gaps
|
|
152
|
+
* that splits a cube).
|
|
153
|
+
*
|
|
154
|
+
* @param {number} a The angle (degrees, y down).
|
|
155
|
+
* @param {number} g The width.
|
|
156
|
+
* @param {number} [from=-40] Where it starts along the ray.
|
|
157
|
+
* @param {number} [to=400] Where it ends.
|
|
158
|
+
* @returns {import('../geometry/types.js').Polygon} The slot.
|
|
159
|
+
*/
|
|
160
|
+
export const slot = (a, g, from = -40, to = 400) => {
|
|
161
|
+
const u = [Math.cos((a * Math.PI) / 180), Math.sin((a * Math.PI) / 180)], v = [-u[1] * g / 2, u[0] * g / 2];
|
|
162
|
+
return poly([[C + u[0] * from + v[0], C + u[1] * from + v[1]], [C + u[0] * to + v[0], C + u[1] * to + v[1]], [C + u[0] * to - v[0], C + u[1] * to - v[1]], [C + u[0] * from - v[0], C + u[1] * from - v[1]]]);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The two half-planes either side of the line p→q (as large squares).
|
|
167
|
+
*
|
|
168
|
+
* @param {readonly number[]} p A point on the line.
|
|
169
|
+
* @param {readonly number[]} q Another.
|
|
170
|
+
* @returns {import('../geometry/types.js').Polygon[]} [left, right].
|
|
171
|
+
*/
|
|
172
|
+
export const halves = (p, q) => {
|
|
173
|
+
const d = [q[0] - p[0], q[1] - p[1]], L = Math.hypot(...d), u = [(d[0] / L) * 2000, (d[1] / L) * 2000], n = [-u[1], u[0]];
|
|
174
|
+
return [1, -1].map((k) => poly([[p[0] - u[0], p[1] - u[1]], [p[0] + u[0], p[1] + u[1]], [p[0] + u[0] + k * n[0], p[1] + u[1] + k * n[1]], [p[0] - u[0] + k * n[0], p[1] - u[1] + k * n[1]]]));
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Shapes in a chain along the line p→q, each overlapping the next: every shape is in front of the next on the left of
|
|
179
|
+
* the line and behind it on the right (a cyclic lock, link by link), the gap g round every crossing.
|
|
180
|
+
*
|
|
181
|
+
* @param {readonly import('../geometry/types.js').Geom[]} list The links, in order.
|
|
182
|
+
* @param {readonly number[]} p A point on the line.
|
|
183
|
+
* @param {readonly number[]} q Another.
|
|
184
|
+
* @param {number} [g=GAP] The gap.
|
|
185
|
+
* @returns {import('../geometry/types.js').Shape} The chain.
|
|
186
|
+
*/
|
|
187
|
+
export const lockChain = (list, p, q, g = G) => {
|
|
188
|
+
const [left, right] = halves(p, q);
|
|
189
|
+
return U(...list.map((s, k) => D(s, ...(list[k + 1] ? [grown(I(list[k + 1], right), g)] : []), ...(k ? [grown(I(list[k - 1], left), g)] : []))));
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Two shapes overlapped, a in front on the left of the line p→q, b in front on its right.
|
|
194
|
+
*
|
|
195
|
+
* @param {import('../geometry/types.js').Geom} a The first.
|
|
196
|
+
* @param {import('../geometry/types.js').Geom} b The second.
|
|
197
|
+
* @param {readonly number[]} p A point on the line.
|
|
198
|
+
* @param {readonly number[]} q Another.
|
|
199
|
+
* @param {number} [g=GAP] The gap.
|
|
200
|
+
* @returns {import('../geometry/types.js').Shape} The pair.
|
|
201
|
+
*/
|
|
202
|
+
export const lockAlong = (a, b, p, q, g = G) => lockChain([a, b], p, q, g);
|
|
203
|
+
|
|
204
|
+
/* ---------- placing a part a gap clear ---------- */
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* The least distance between two shapes, measured between their outline points (dense outlines give it to a fraction).
|
|
208
|
+
*
|
|
209
|
+
* @param {import('../geometry/types.js').Geom} a One shape.
|
|
210
|
+
* @param {import('../geometry/types.js').Geom} b The other.
|
|
211
|
+
* @returns {number} The distance.
|
|
212
|
+
*/
|
|
213
|
+
export const gapBetween = (a, b) => {
|
|
214
|
+
const B = U(b).flat(2); let m = Infinity;
|
|
215
|
+
for (const p of U(a).flat(2)) for (const q of B) { const d = (p[0] - q[0]) ** 2 + (p[1] - q[1]) ** 2; if (d < m) m = d; } return Math.sqrt(m);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* A part slid along `deg` (0 = right, 90 = down) until it sits exactly g clear of `fixed`, by bisection between 0 and
|
|
220
|
+
* `far`.
|
|
221
|
+
*
|
|
222
|
+
* @param {import('../geometry/types.js').Geom} fixed What it must clear.
|
|
223
|
+
* @param {import('../geometry/types.js').Geom} part The part.
|
|
224
|
+
* @param {number} deg The direction (degrees).
|
|
225
|
+
* @param {number} g The gap.
|
|
226
|
+
* @param {number} [far=400] The farthest it may go.
|
|
227
|
+
* @returns {import('../geometry/types.js').Geom} The moved part.
|
|
228
|
+
*/
|
|
229
|
+
export const slideToGap = (fixed, part, deg, g, far = 400) => {
|
|
230
|
+
const u = [Math.cos((deg * Math.PI) / 180), Math.sin((deg * Math.PI) / 180)];
|
|
231
|
+
const at = (t) => move(part, u[0] * t, u[1] * t); let lo = 0, hi = far;
|
|
232
|
+
for (let i = 0; i < 30; i++) { const t = (lo + hi) / 2; if (gapBetween(fixed, at(t)) < g) lo = t; else hi = t; } return at(hi);
|
|
233
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Point, Polygon, Shape, Geom } from '../geometry/types.js';
|
|
2
|
+
|
|
3
|
+
/** A stripe's edges: each a number or a function of x (y down). */
|
|
4
|
+
export type StripeEdge = number | ((x: number) => number);
|
|
5
|
+
|
|
6
|
+
/** n copies of a shape turned evenly round (cx, cy), the first turned deg0. */
|
|
7
|
+
export function turns(g: Geom, n: number, deg0?: number, cx?: number, cy?: number): Geom[];
|
|
8
|
+
/** A cyclic overlap: every unit cut free by the next one round, crossings parted by g. */
|
|
9
|
+
export function cyclic(units: readonly Geom[], g: number): Shape;
|
|
10
|
+
/** A chevron: two arms w wide meeting at an apex at the origin, opening `spread` degrees, pointing along +x. */
|
|
11
|
+
export function chevron(L: number, w: number, spread: number): Polygon;
|
|
12
|
+
/** Stripes as bands between [top, bottom] edges, sampled every dx from x0 to x1. */
|
|
13
|
+
export function stripeBands(list: readonly (readonly StripeEdge[])[], o?: { x0?: number; x1?: number; dx?: number }): Shape;
|
|
14
|
+
/** n even stripes across [a, b] with a gap gs: [top, bottom] each. */
|
|
15
|
+
export function evenBands(n: number, a: number, b: number, gs: number): [number, number][];
|
|
16
|
+
/** n stripes across [a, b] with a fixed gap gs, widths in the ratio r0 : r1 first to last. */
|
|
17
|
+
export function thinning(n: number, a: number, b: number, gs: number, r0: number, r1: number): [number, number][];
|
|
18
|
+
/** n stripes w wide along a polyline, `pitch` apart square to every run, corners mitred. */
|
|
19
|
+
export function stripesAlong(pts: readonly Point[], n: number, w: number, pitch: number): Shape;
|
|
20
|
+
/** A stripe list shifted by f(x). */
|
|
21
|
+
export function shifted(list: readonly (readonly number[])[], f: (x: number) => number): ((x: number) => number)[][];
|
|
22
|
+
/** Stripes drawn level, then turned deg about the centre. */
|
|
23
|
+
export function turned(list: readonly (readonly StripeEdge[])[], deg: number, o?: { x0?: number; x1?: number; dx?: number }): Shape;
|
|
24
|
+
/** A ring whose radius waves k times round: r = R + A·cos(k·t + ph). */
|
|
25
|
+
export function waveRing(R: number, A: number, k: number, ph?: number, n?: number, cx?: number, cy?: number): Point[];
|
|
26
|
+
/** A glyph slanted deg degrees (tops lean right) about the line y = cy. */
|
|
27
|
+
export function italic(g: Geom, deg?: number, cy?: number): Geom;
|
|
28
|
+
/** A sharp notch: the triangle from edge points a and b to the tip t, `out` past the edge. */
|
|
29
|
+
export function wedge(a: readonly number[], b: readonly number[], t: Point, out?: number): Polygon;
|
|
30
|
+
/** The speed capitals' box: cap line, baseline, left and right edges, stem and bar. */
|
|
31
|
+
export const SPEED_BOX: { T0: number; B0: number; L0: number; R0: number; SW: number; BW: number };
|
|
32
|
+
/** A bar from y0 to y1, across the speed box unless told. */
|
|
33
|
+
export function hz(y0: number, y1: number, x0?: number, x1?: number): Polygon;
|
|
34
|
+
/** An upright from x0 to x1, the speed box's height unless told. */
|
|
35
|
+
export function vt(x0: number, x1: number, y0?: number, y1?: number): Polygon;
|
|
36
|
+
/** The speed slit: a wedge bw wide entering at a, its tip len along dir. */
|
|
37
|
+
export function slit(a: readonly number[], len: number, dir?: number, bw?: number): Polygon;
|
|
38
|
+
/** A speed letter: the glyph less its slit, slanted 17°. */
|
|
39
|
+
export function speed(glyph: Geom, cut: Geom): Geom;
|
|
40
|
+
/** The speed slab capitals (N M Z K X S R A), each returning the upright glyph. */
|
|
41
|
+
export const SPEED_GLYPHS: Record<string, () => Geom>;
|