@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,66 @@
|
|
|
1
|
+
import type { Geom, Point, Polygon, Shape } from '../geometry/types.js';
|
|
2
|
+
|
|
3
|
+
export { sub, add, mul } from '../util/vec.js';
|
|
4
|
+
|
|
5
|
+
/** A concave join of a letter, as joins() finds it. */
|
|
6
|
+
export interface TrapJoin {
|
|
7
|
+
/** The corner. */
|
|
8
|
+
p: readonly number[];
|
|
9
|
+
/** The unit directions along its two edges, away from it. */
|
|
10
|
+
a: number[];
|
|
11
|
+
b: number[];
|
|
12
|
+
/** The angle between them, degrees. */
|
|
13
|
+
ang: number;
|
|
14
|
+
/** Whether it is a counter's corner. */
|
|
15
|
+
hole: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** The notch's sizes and the angles it traps. */
|
|
18
|
+
export interface TrapOptions {
|
|
19
|
+
m?: number;
|
|
20
|
+
hw?: number;
|
|
21
|
+
d?: number;
|
|
22
|
+
dMax?: number;
|
|
23
|
+
lo?: number;
|
|
24
|
+
hi?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** The stem weight: 116. */
|
|
28
|
+
export const TRAP_S: number;
|
|
29
|
+
/** The diagonal weight, square to the stroke: 112. */
|
|
30
|
+
export const TRAP_SD: number;
|
|
31
|
+
/** The bar weight: 104. */
|
|
32
|
+
export const TRAP_SB: number;
|
|
33
|
+
/** The band between the cap line and the baseline, wider than the canvas. */
|
|
34
|
+
export function trapBand(y0?: number, y1?: number): Polygon;
|
|
35
|
+
/** The unit vector along a. */
|
|
36
|
+
export function unit(a: readonly number[]): Point;
|
|
37
|
+
/** A straight stroke a → b, w wide square to it, run ext past both ends. */
|
|
38
|
+
export function str(a: readonly number[], b: readonly number[], w?: number, ext?: number): Polygon;
|
|
39
|
+
/** The horizontal width of a slanted stroke w wide running dx across over dy down. */
|
|
40
|
+
export function hOf(dx: number, dy: number, w: number): number;
|
|
41
|
+
/** A diagonal whose left edge runs (xt, y0) → (xb, y1), w wide square to it. */
|
|
42
|
+
export function para(xt: number, xb: number, w?: number, y0?: number, y1?: number): Polygon;
|
|
43
|
+
/** A shape mirrored about x = C. */
|
|
44
|
+
export function mir(s: Geom): Shape;
|
|
45
|
+
/** A shape turned upside down about y = C. */
|
|
46
|
+
export function flipY(s: Geom): Shape;
|
|
47
|
+
/** The angle (degrees) round circle (c, r) where the tangent from p touches it, on side ±1. */
|
|
48
|
+
export function tangentDeg(c: readonly number[], r: number, p: readonly number[], side: number): number;
|
|
49
|
+
/** The notch's sizes and the counter angles it traps. */
|
|
50
|
+
export const TRAP: { m: number; hw: number; d: number; dMax: number; lo: number; hi: number };
|
|
51
|
+
/** A point dist along a ring from vertex i, walking by step (±1). */
|
|
52
|
+
export function walk(r: readonly (readonly number[])[], i: number, step: number, dist: number): readonly number[];
|
|
53
|
+
/** The angle between two unit vectors, degrees. */
|
|
54
|
+
export function angle(a: readonly number[], b: readonly number[]): number;
|
|
55
|
+
/** Every concave join whose counter angle is between lo and hi degrees. */
|
|
56
|
+
export function joins(shape: Shape, lo?: number, hi?: number): TrapJoin[];
|
|
57
|
+
/** The wedge cut at one join, carrying its tip. */
|
|
58
|
+
export function notch(join: Pick<TrapJoin, 'p' | 'a' | 'b' | 'ang'>, o?: TrapOptions): Polygon & { tip: number[] };
|
|
59
|
+
/** Traps whose tips come nearer than this: the second is dropped. */
|
|
60
|
+
export const TIP_GAP: number;
|
|
61
|
+
/** Every picked join cut by the notch, a counter's corners first. */
|
|
62
|
+
export function traps(shape: Geom, pick?: (join: TrapJoin) => boolean, o?: TrapOptions): Shape;
|
|
63
|
+
/** A pick: the joins within 60 of any point. */
|
|
64
|
+
export function near(...pts: (readonly number[])[]): (join: { p: readonly number[] }) => boolean;
|
|
65
|
+
/** A pick: the joins not within 60 of any point. */
|
|
66
|
+
export function not(...pts: (readonly number[])[]): (join: { p: readonly number[] }) => boolean;
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ink Trap letters: a heavy, tight grotesque letter or numeral drawn geometrically (never from a font), stems TRAP_S,
|
|
3
|
+
* diagonals TRAP_SD square to the stroke, bars TRAP_SB, square terminals, cap 448 (the 448 frame's T, B, H) and never
|
|
4
|
+
* wider, whose concave joins are cut by ONE notch shape: a sharp wedge from a mouth on the corner's two edges to a tip past
|
|
5
|
+
* the corner along its bisector (`notch()`). `traps()` cuts it at every join `joins()` finds from the outline (counter
|
|
6
|
+
* angles 12–100°), a counter's corners first, leaving out any trap whose tip comes within TIP_GAP of one already cut.
|
|
7
|
+
* From the Logomarks tools (inktrapparts.mjs, batches 338 and 339).
|
|
8
|
+
*/
|
|
9
|
+
import { C, U, D, rect, poly, mapPts } from '../geometry/index.js';
|
|
10
|
+
import { sub, add, mul, shoelace as sArea } from '../util/vec.js';
|
|
11
|
+
import { T, B } from './frame.js';
|
|
12
|
+
|
|
13
|
+
export { sub, add, mul } from '../util/vec.js';
|
|
14
|
+
|
|
15
|
+
/* ---------- The letter build ---------- */
|
|
16
|
+
|
|
17
|
+
/** The stem weight: 116. */
|
|
18
|
+
export const TRAP_S = 116;
|
|
19
|
+
|
|
20
|
+
/** The diagonal weight, square to the stroke: 112. */
|
|
21
|
+
export const TRAP_SD = 112;
|
|
22
|
+
|
|
23
|
+
/** The bar weight: 104. */
|
|
24
|
+
export const TRAP_SB = 104;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The band between the cap line and the baseline: strokes are clipped to it, so their ends sit flat on the lines.
|
|
28
|
+
*
|
|
29
|
+
* @param {number} [y0] The top line (T).
|
|
30
|
+
* @param {number} [y1] The bottom line (B).
|
|
31
|
+
* @returns {import('../geometry/types.js').Polygon} A rectangle wider than the canvas.
|
|
32
|
+
*/
|
|
33
|
+
export const trapBand = (y0 = T, y1 = B) => rect(-600, y0, 1712, y1 - y0);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A vector's length.
|
|
37
|
+
*
|
|
38
|
+
* @param {readonly number[]} a The vector.
|
|
39
|
+
* @returns {number} Its length.
|
|
40
|
+
*/
|
|
41
|
+
const len = (a) => Math.hypot(a[0], a[1]);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The unit vector along a.
|
|
45
|
+
*
|
|
46
|
+
* @param {readonly number[]} a The vector.
|
|
47
|
+
* @returns {import('../geometry/types.js').Point} It, at length 1.
|
|
48
|
+
*/
|
|
49
|
+
export const unit = (a) => mul(a, 1 / len(a));
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A straight stroke along the centre line a → b, w wide square to it, run `ext` past both ends (clip it afterwards).
|
|
53
|
+
*
|
|
54
|
+
* @param {readonly number[]} a The start of the centre line.
|
|
55
|
+
* @param {readonly number[]} b Its end.
|
|
56
|
+
* @param {number} [w] The width, square to the stroke (TRAP_SD).
|
|
57
|
+
* @param {number} [ext=0] How far it runs on past both ends.
|
|
58
|
+
* @returns {import('../geometry/types.js').Polygon} The stroke.
|
|
59
|
+
*/
|
|
60
|
+
export const str = (a, b, w = TRAP_SD, ext = 0) => {
|
|
61
|
+
const dx = b[0] - a[0], dy = b[1] - a[1], L = Math.hypot(dx, dy), ux = dx / L, uy = dy / L,
|
|
62
|
+
nx = -uy * w / 2, ny = ux * w / 2, A = [a[0] - ux * ext, a[1] - uy * ext], E = [b[0] + ux * ext, b[1] + uy * ext];
|
|
63
|
+
return poly([[A[0] + nx, A[1] + ny], [E[0] + nx, E[1] + ny], [E[0] - nx, E[1] - ny], [A[0] - nx, A[1] - ny]]);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The horizontal width of a slanted stroke w wide (square to it) that runs dx across over dy down.
|
|
68
|
+
*
|
|
69
|
+
* @param {number} dx How far it runs across.
|
|
70
|
+
* @param {number} dy How far it runs down.
|
|
71
|
+
* @param {number} w Its width, square to it.
|
|
72
|
+
* @returns {number} Its width along a horizontal.
|
|
73
|
+
*/
|
|
74
|
+
export const hOf = (dx, dy, w) => w * Math.hypot(dx, dy) / dy;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* A diagonal from the cap line to the baseline whose LEFT edge runs (xt, y0) → (xb, y1), w wide square to it.
|
|
78
|
+
*
|
|
79
|
+
* @param {number} xt The left edge's x on the top line.
|
|
80
|
+
* @param {number} xb Its x on the bottom line.
|
|
81
|
+
* @param {number} [w] The width, square to the stroke (TRAP_SD).
|
|
82
|
+
* @param {number} [y0] The top line (T).
|
|
83
|
+
* @param {number} [y1] The bottom line (B).
|
|
84
|
+
* @returns {import('../geometry/types.js').Polygon} The diagonal.
|
|
85
|
+
*/
|
|
86
|
+
export const para = (xt, xb, w = TRAP_SD, y0 = T, y1 = B) => {
|
|
87
|
+
const h = hOf(xb - xt, y1 - y0, w);
|
|
88
|
+
return poly([[xt, y0], [xt + h, y0], [xb + h, y1], [xb, y1]]);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A shape mirrored about the letter's centre line x = C.
|
|
93
|
+
*
|
|
94
|
+
* @param {import('../geometry/types.js').Geom} s The shape.
|
|
95
|
+
* @returns {import('../geometry/types.js').Shape} It, mirrored.
|
|
96
|
+
*/
|
|
97
|
+
export const mir = (s) => U(mapPts(s, ([x, y]) => [2 * C - x, y]));
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* A shape turned upside down about y = C.
|
|
101
|
+
*
|
|
102
|
+
* @param {import('../geometry/types.js').Geom} s The shape.
|
|
103
|
+
* @returns {import('../geometry/types.js').Shape} It, flipped.
|
|
104
|
+
*/
|
|
105
|
+
export const flipY = (s) => U(mapPts(s, ([x, y]) => [x, 2 * C - y]));
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Where the tangent from p touches the circle (centre c, radius r), on the side `side`, as an angle round the circle.
|
|
109
|
+
*
|
|
110
|
+
* @param {readonly number[]} c The circle's centre.
|
|
111
|
+
* @param {number} r Its radius.
|
|
112
|
+
* @param {readonly number[]} p The point outside it.
|
|
113
|
+
* @param {number} side +1 or −1.
|
|
114
|
+
* @returns {number} The touching point's angle, in degrees.
|
|
115
|
+
*/
|
|
116
|
+
export const tangentDeg = (c, r, p, side) => {
|
|
117
|
+
const dv = sub(p, c), L = len(dv), phi = Math.atan2(dv[1], dv[0]);
|
|
118
|
+
return (phi + side * Math.acos(r / L)) * 180 / Math.PI;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/* ---------- The ink trap ---------- */
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The notch, one for the whole family: its mouth sits m along each edge from the corner (at least hw either side of the
|
|
125
|
+
* bisector), its tip d past the corner along the bisector, d growing as the corner sharpens (to dMax). Joins whose counter
|
|
126
|
+
* angle is between lo and hi degrees are trapped. Even so the acute crotches of V, N, M, W and Y only deepen their
|
|
127
|
+
* counters at 32 px: the trap reads where the join is 45–100°.
|
|
128
|
+
*/
|
|
129
|
+
export const TRAP = { m: 60, hw: 36, d: 84, dMax: 150, lo: 12, hi: 100 };
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* A point `dist` along a ring from vertex i, walking by `step` (+1 or −1): the edge's direction away from a corner.
|
|
133
|
+
*
|
|
134
|
+
* @param {readonly (readonly number[])[]} r The ring.
|
|
135
|
+
* @param {number} i The vertex.
|
|
136
|
+
* @param {number} step +1 or −1.
|
|
137
|
+
* @param {number} dist How far to walk.
|
|
138
|
+
* @returns {readonly number[]} The point.
|
|
139
|
+
*/
|
|
140
|
+
export const walk = (r, i, step, dist) => {
|
|
141
|
+
const n = r.length;
|
|
142
|
+
let j = i, left = dist, p = r[i];
|
|
143
|
+
for (let k = 0; k < n; k++) {
|
|
144
|
+
const q = r[(j + step + n) % n], L = len(sub(q, p));
|
|
145
|
+
if (L >= left) return add(p, mul(unit(sub(q, p)), left));
|
|
146
|
+
left -= L; p = q; j = (j + step + n) % n;
|
|
147
|
+
}
|
|
148
|
+
return p;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The angle between two unit vectors.
|
|
153
|
+
*
|
|
154
|
+
* @param {readonly number[]} a The first.
|
|
155
|
+
* @param {readonly number[]} b The second.
|
|
156
|
+
* @returns {number} The angle, degrees.
|
|
157
|
+
*/
|
|
158
|
+
export const angle = (a, b) => Math.acos(Math.max(-1, Math.min(1, a[0] * b[0] + a[1] * b[1]))) * 180 / Math.PI;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Every concave join of a shape (a corner where the ink turns in: a crotch, or a counter's corner) whose counter angle is
|
|
162
|
+
* between lo and hi degrees.
|
|
163
|
+
*
|
|
164
|
+
* @param {import('../geometry/types.js').Shape} shape The letter.
|
|
165
|
+
* @param {number} [lo] The smallest counter angle (TRAP.lo).
|
|
166
|
+
* @param {number} [hi] The largest (TRAP.hi).
|
|
167
|
+
* @returns {{ p: readonly number[], a: number[], b: number[], ang: number, hole: boolean }[]} Each join: its corner, the
|
|
168
|
+
* unit directions along its two edges (away from it), the angle between them, and whether it is a counter's.
|
|
169
|
+
*/
|
|
170
|
+
export const joins = (shape, lo = TRAP.lo, hi = TRAP.hi) => {
|
|
171
|
+
const out = [];
|
|
172
|
+
for (const pg of shape) pg.forEach((r0, ri) => {
|
|
173
|
+
const r = (r0.length > 1 && r0[0][0] === r0.at(-1)[0] && r0[0][1] === r0.at(-1)[1]) ? r0.slice(0, -1) : r0, n = r.length, sg = Math.sign(sArea(r));
|
|
174
|
+
for (let i = 0; i < n; i++) {
|
|
175
|
+
const p = r[i], pv = r[(i - 1 + n) % n], nx = r[(i + 1) % n];
|
|
176
|
+
const e1 = sub(p, pv), e2 = sub(nx, p);
|
|
177
|
+
if (len(e1) < 1e-6 || len(e2) < 1e-6) continue;
|
|
178
|
+
const ringConvex = Math.sign(e1[0] * e2[1] - e1[1] * e2[0]) === sg, reflex = ri === 0 ? !ringConvex : ringConvex;
|
|
179
|
+
if (!reflex) continue;
|
|
180
|
+
const ang0 = angle(unit(sub(pv, p)), unit(sub(nx, p)));
|
|
181
|
+
if (ang0 < lo || ang0 > hi) continue;
|
|
182
|
+
const a = unit(sub(walk(r, i, -1, 18), p)), b = unit(sub(walk(r, i, 1, 18), p));
|
|
183
|
+
out.push({ p, a, b, ang: angle(a, b), hole: ri > 0 });
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
return out;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* The notch at one join: a sharp wedge from its mouth on the two edges to its tip, into the ink.
|
|
191
|
+
*
|
|
192
|
+
* @param {{ p: readonly number[], a: readonly number[], b: readonly number[], ang: number }} join The join (joins()).
|
|
193
|
+
* @param {{ m?: number, hw?: number, d?: number, dMax?: number }} [o] TRAP's sizes, any of them changed.
|
|
194
|
+
* @returns {import('../geometry/types.js').Polygon & { tip: number[] }} The wedge, carrying its tip.
|
|
195
|
+
*/
|
|
196
|
+
export const notch = ({ p, a, b, ang }, o = {}) => {
|
|
197
|
+
const { m, hw, d, dMax } = { ...TRAP, ...o }, al = ang / 2 * Math.PI / 180;
|
|
198
|
+
const u = unit(mul(add(a, b), -1)), v = [-u[1], u[0]], mm = Math.max(m, hw / Math.sin(al)), hb = mm * Math.sin(al) + 2;
|
|
199
|
+
const back = sub(p, mul(u, mm * Math.cos(al) + 2)), dd = Math.max(d, Math.min(dMax, d * Math.sqrt(Math.SQRT1_2 / Math.sin(al))));
|
|
200
|
+
const tip = add(p, mul(u, dd));
|
|
201
|
+
return Object.assign(poly([add(back, mul(v, hb)), tip, sub(back, mul(v, hb))]), { tip });
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Two traps whose tips come nearer than this bite through the stroke between them (the bar under P's or R's bowl, trapped
|
|
206
|
+
* from the counter above and the corner below): the second is dropped.
|
|
207
|
+
*/
|
|
208
|
+
export const TIP_GAP = 70;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* The family's one move: every join picked (all of them, or those `pick` keeps) cut by the ink-trap notch, a counter's
|
|
212
|
+
* corners first, any trap whose tip would meet one already cut left out.
|
|
213
|
+
*
|
|
214
|
+
* @param {import('../geometry/types.js').Geom} shape The letter.
|
|
215
|
+
* @param {(join: { p: readonly number[], a: number[], b: number[], ang: number, hole: boolean }) => boolean} [pick] Which
|
|
216
|
+
* joins to trap (all by default; see near() and not()).
|
|
217
|
+
* @param {{ m?: number, hw?: number, d?: number, dMax?: number, lo?: number, hi?: number }} [o] TRAP's sizes and angles,
|
|
218
|
+
* any of them changed.
|
|
219
|
+
* @returns {import('../geometry/types.js').Shape} The trapped letter.
|
|
220
|
+
*/
|
|
221
|
+
export const traps = (shape, pick = () => true, o = {}) => {
|
|
222
|
+
const s = U(shape), js = joins(s, o.lo, o.hi).filter(pick), ns = [];
|
|
223
|
+
for (const j of [...js.filter((j) => j.hole), ...js.filter((j) => !j.hole)]) {
|
|
224
|
+
const n = notch(j, o); // a counter's traps first
|
|
225
|
+
if (ns.every((k) => len(sub(k.tip, n.tip)) >= TIP_GAP)) ns.push(n);
|
|
226
|
+
}
|
|
227
|
+
return ns.length ? D(s, ...ns) : s;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* A pick for traps(): keep the joins within 60 of any of the given points.
|
|
232
|
+
*
|
|
233
|
+
* @param {...(readonly number[])} pts The points.
|
|
234
|
+
* @returns {(join: { p: readonly number[] }) => boolean} The pick.
|
|
235
|
+
*/
|
|
236
|
+
export const near = (...pts) => ({ p }) => pts.some(([x, y]) => Math.hypot(p[0] - x, p[1] - y) < 60);
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* A pick for traps(): drop the joins within 60 of any of the given points.
|
|
240
|
+
*
|
|
241
|
+
* @param {...(readonly number[])} pts The points.
|
|
242
|
+
* @returns {(join: { p: readonly number[] }) => boolean} The pick.
|
|
243
|
+
*/
|
|
244
|
+
export const not = (...pts) => ({ p }) => !pts.some(([x, y]) => Math.hypot(p[0] - x, p[1] - y) < 60);
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { Geom, Point, Polygon, Shape } from '../geometry/types.js';
|
|
2
|
+
import type { BadgeKind } from '../geometry/badges.js';
|
|
3
|
+
|
|
4
|
+
/** The Fused stem width: 84. */
|
|
5
|
+
export const W: number;
|
|
6
|
+
/** The Fused cap line: y 76. */
|
|
7
|
+
export const T0: number;
|
|
8
|
+
/** The Fused baseline: y 436. */
|
|
9
|
+
export const T1: number;
|
|
10
|
+
/** Halfway between the cap line and the baseline. */
|
|
11
|
+
export const MID: number;
|
|
12
|
+
/** A Fused stem whose left edge is x. */
|
|
13
|
+
export function stem(x: number, y0?: number, y1?: number): Polygon;
|
|
14
|
+
/** A Fused bar from x0 to x1 whose top edge is y. */
|
|
15
|
+
export function hbar(x0: number, x1: number, y: number): Polygon;
|
|
16
|
+
/** A shape clipped flat at the cap and base lines. */
|
|
17
|
+
export function clipH(g: Geom, y0?: number, y1?: number): Shape;
|
|
18
|
+
/** A D-shaped bowl to the right of a stem whose left edge is x. */
|
|
19
|
+
export function bowl(x: number, y0: number, y1: number, reach?: number): Shape;
|
|
20
|
+
/** A straight run of points, about one every 8. */
|
|
21
|
+
export function L_(a: readonly number[], b: readonly number[], n?: number): Point[];
|
|
22
|
+
/** A circular arc's points (degrees, 0 right, 90 down). */
|
|
23
|
+
export function A_(cx: number, cy: number, r: number, a0: number, a1: number): Point[];
|
|
24
|
+
/** Runs joined end to start. */
|
|
25
|
+
export function J_(...p: Point[][]): Point[];
|
|
26
|
+
/** A polyline with the in-between points of its straight runs dropped. */
|
|
27
|
+
export function straighten(p: readonly Point[]): Point[];
|
|
28
|
+
/** A polyline trimmed by d from its start (or its end). */
|
|
29
|
+
export function trim(p: readonly Point[], d: number, atEnd?: boolean): Point[];
|
|
30
|
+
/** The two tracks' width: 26. */
|
|
31
|
+
export const TW: number;
|
|
32
|
+
/** The two tracks' pitch: 48. */
|
|
33
|
+
export const PITCH: number;
|
|
34
|
+
/** Two tracks along a filleted centreline. */
|
|
35
|
+
export function track2(pts: readonly Point[], r?: number | number[], o?: { n?: number; pitch?: number; w?: number; closed?: boolean }): Shape;
|
|
36
|
+
/** Three tracks (22 wide, 44 apart) along a filleted centreline. */
|
|
37
|
+
export function track3(pts: readonly Point[], r?: number | number[]): Shape;
|
|
38
|
+
/** A monoline stroke with square ends. */
|
|
39
|
+
export function mline(pts: readonly Point[], w: number): Shape;
|
|
40
|
+
/** An ellipse arc's points (degrees). */
|
|
41
|
+
export function E_(cx: number, cy: number, rx: number, ry: number, a0: number, a1: number): Point[];
|
|
42
|
+
/** The top ring of a Fused S's centreline. */
|
|
43
|
+
export function sTop(sx: number, rx: number, a0?: number, ry?: number): Point[];
|
|
44
|
+
/** The lower ring of a Fused S's centreline. */
|
|
45
|
+
export function sLow(sx: number, rx: number, a1?: number, ry?: number): Point[];
|
|
46
|
+
/** A centreline stroked solid, square ends. */
|
|
47
|
+
export function solid(pts: readonly Point[], w?: number): Shape;
|
|
48
|
+
/** A Fused A's two legs, clipped to the cap. */
|
|
49
|
+
export function aLegs(ax: number, w: number): Shape;
|
|
50
|
+
/** A Fused M: two stems and a V meeting in one point. */
|
|
51
|
+
export function mLetter(x: number, xr: number, v?: number): Shape;
|
|
52
|
+
/** A Fused A's crossbar, kept inside the legs. */
|
|
53
|
+
export function aBar(ax: number, aw: number, y: number): Shape;
|
|
54
|
+
/** The Badge letters' monoline width: 52. */
|
|
55
|
+
export const BT: number;
|
|
56
|
+
/** The Badge letters' centreline cap height: 212. */
|
|
57
|
+
export const BH: number;
|
|
58
|
+
/** A Badge J's centreline. */
|
|
59
|
+
export function bJ(x: number, r?: number, H?: number): Point[];
|
|
60
|
+
/** A Badge bowl's centreline off a stem. */
|
|
61
|
+
export function bBowl(x: number, y0: number, y1: number, w: number): Point[];
|
|
62
|
+
/** A Badge B's centrelines. */
|
|
63
|
+
export function bB(x: number, w?: number, H?: number, k?: number): Point[][];
|
|
64
|
+
/** A Badge M's centreline. */
|
|
65
|
+
export function bM(x: number, w?: number, H?: number, v?: number): Point[][];
|
|
66
|
+
/** A Badge H's centrelines. */
|
|
67
|
+
export function bH(x: number, w?: number, H?: number): Point[][];
|
|
68
|
+
/** A Badge S's centreline. */
|
|
69
|
+
export function bS(sx: number, rx?: number, a0?: number, a1?: number, H?: number): Point[][];
|
|
70
|
+
/** A Badge C's centreline. */
|
|
71
|
+
export function bC(cx: number, r?: number, a0?: number, a1?: number): Point[][];
|
|
72
|
+
/** A Badge P's centrelines. */
|
|
73
|
+
export function bP(x: number, w?: number, H?: number): Point[][];
|
|
74
|
+
/** A Badge R's centrelines. */
|
|
75
|
+
export function bR(x: number, w?: number, H?: number): Point[][];
|
|
76
|
+
/** A Badge A's centrelines. */
|
|
77
|
+
export function bA(ax: number, w?: number, H?: number, bar?: number): Point[][];
|
|
78
|
+
/** A Badge D's centreline. */
|
|
79
|
+
export function bD(x: number, w?: number, H?: number): Point[][];
|
|
80
|
+
/** The top-bar ligature: a bar from the J's left across to x1, and the J. */
|
|
81
|
+
export function bJbar(jx: number, x1: number, r?: number): Point[][];
|
|
82
|
+
/** Centrelines as one monoline, square ends, clipped at the cap and base lines. */
|
|
83
|
+
export function mono(parts: readonly (readonly Point[])[], T?: number, H?: number): Shape;
|
|
84
|
+
/** The pair as a monoline knocked out of a house badge. */
|
|
85
|
+
export function badge(kind: BadgeKind, parts: readonly (readonly Point[])[]): Shape;
|
|
86
|
+
/** The Fan cell: 140. */
|
|
87
|
+
export const FC: number;
|
|
88
|
+
/** The Fan gutter: 26. */
|
|
89
|
+
export const FG: number;
|
|
90
|
+
/** A grid of Fan tiles, centred ('S', '.', 'tl', 'tr', 'bl', 'br'). */
|
|
91
|
+
export function fan(rows: readonly (readonly string[])[]): Shape;
|
|
92
|
+
/** A Fused C opening off a stem. */
|
|
93
|
+
export function cStem(x: number, reach?: number, a?: number, y0?: number, y1?: number): Shape;
|
|
94
|
+
/** bowl() kept to the right of its stem's left edge. */
|
|
95
|
+
export function monoBowl(x: number, y0: number, y1: number, reach?: number): Shape;
|
|
96
|
+
/** A Fused A whose right leg stands upright as a stem. */
|
|
97
|
+
export function aUp(sx: number): Shape;
|
|
98
|
+
/** The half-plane right of the line a → b. */
|
|
99
|
+
export function rightOf(a: readonly number[], b: readonly number[]): Polygon;
|
|
100
|
+
/** aUp()'s left leg, foot to apex. */
|
|
101
|
+
export function aUpLeg(sx: number): [Point, Point];
|
|
102
|
+
/** A Fused J's hook off a stem. */
|
|
103
|
+
export function jHook(jx: number): Shape;
|
|
104
|
+
/** The Track cap height: 360. */
|
|
105
|
+
export const TH: number;
|
|
106
|
+
/** The Track S's ring radius. */
|
|
107
|
+
export const TR: number;
|
|
108
|
+
/** Half a two-track band: 37. */
|
|
109
|
+
export const TB2: number;
|
|
110
|
+
/** How far a track's round end reaches past its centreline: 13. */
|
|
111
|
+
export const TCAP: number;
|
|
112
|
+
/** A Track S entered from the left along the base. */
|
|
113
|
+
export function sFromBase(sx: number, r?: number, end?: number): Point[];
|
|
114
|
+
/** A Track S leaving to the right along the cap line. */
|
|
115
|
+
export function sToTop(sx: number, r?: number, start?: number): Point[];
|
|
116
|
+
/** A Track M, foot to foot. */
|
|
117
|
+
export function tM(x: number, w?: number, v?: number): Point[];
|
|
118
|
+
/** A Track R's stem and bowl. */
|
|
119
|
+
export function tRbowl(x: number, bw?: number, bh?: number): Point[];
|
|
120
|
+
/** A Track R's free-standing leg. */
|
|
121
|
+
export function tRleg(x: number, kx?: number, lx?: number, bh?: number): Point[];
|
|
122
|
+
/** A Track house A, foot to foot. */
|
|
123
|
+
export function tA(x: number, aw?: number, k?: number): Point[];
|
|
124
|
+
/** The house A's bar, a gap short of both legs. */
|
|
125
|
+
export function tAbar(x: number, aw?: number, y?: number): Point[];
|
|
126
|
+
/** A Track J, hook tip to stem top. */
|
|
127
|
+
export function tJ(x: number, rj?: number): Point[];
|
|
128
|
+
/** The lowercase x-height: 176. */
|
|
129
|
+
export const XH: number;
|
|
130
|
+
/** A Fused n-arch from the x-height to the base. */
|
|
131
|
+
export function arch(xl: number, rA?: number, xh?: number, both?: boolean): Shape;
|
|
132
|
+
/** A Fused lowercase e. */
|
|
133
|
+
export function eLow(cx: number, cy: number, r: number, open?: number[]): Shape;
|
|
134
|
+
/** Puff letters: balloon strokes. */
|
|
135
|
+
export function puff(...strokes: Point[][]): Shape;
|
|
136
|
+
/** The Linked band's width: 46. */
|
|
137
|
+
export const LBW: number;
|
|
138
|
+
/** The Linked gap at a crossing: 12. */
|
|
139
|
+
export const LBG: number;
|
|
140
|
+
/** A polyline densified to a step. */
|
|
141
|
+
export function dense(pts: readonly Point[], step?: number, closed?: boolean): Point[];
|
|
142
|
+
/** Woven letters. */
|
|
143
|
+
export function linked(strands: readonly (readonly [readonly Point[], boolean?])[], w?: number, g?: number): Shape;
|
|
144
|
+
/** A finished shape with its spike vertices and zero-area rings dropped, every ring closed. */
|
|
145
|
+
export function clean(shape: Shape): Shape;
|