@logomarks/core 0.1.11 → 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 +22 -0
- package/README.md +7 -0
- package/package.json +47 -4
- 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/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/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/index.d.ts +2 -0
- package/src/sdf/index.js +2 -0
- package/src/sdf/y2k.d.ts +12 -0
- package/src/sdf/y2k.js +80 -0
- package/src/util/vec.d.ts +14 -0
- package/src/util/vec.js +61 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shatter: a solid form whose one side breaks into a few angular shards that drift off, smaller as they go, every part a
|
|
3
|
+
* house gap from the next. The shards are irregular triangles from a seeded strip (so seed Math.random per mark), not a
|
|
4
|
+
* grid of squares. A mark is made by `breakOff()`:
|
|
5
|
+
*
|
|
6
|
+
* 1. `shardStrip(inner, outer, o)`: the zone that breaks off is a band between two rails, the inner one (the crack, or a
|
|
7
|
+
* ring's inside) and the outer one (the form's own outline), each a function of s in 0…1 (`rail()`, `bandRails()`).
|
|
8
|
+
* Nodes go down both rails in turn, seeded and stepping down in size, and every three in a row make a triangle: a strip
|
|
9
|
+
* of shards that tiles the band, the outer side of each still on the form's outline, so the eye completes the form.
|
|
10
|
+
* 2. `cutBack()`: the body is the form less the band, cut back half a gap along the crack by a mitred band (its edge stays
|
|
11
|
+
* sharp); every shard is inset half a gap (`inset()`), so the strip starts as a mosaic with every part a gap apart.
|
|
12
|
+
* 3. `drift(body, shards, o)`: each shard is turned, shrunk about its centre and moved by its place along the strip
|
|
13
|
+
* (`spread()`: out from a point inside the form), nearest the body first, and pushed on along its move until it is a
|
|
14
|
+
* gap clear of the body and of every shard before it. A mark keeps no shard under 28 across once fitted.
|
|
15
|
+
*
|
|
16
|
+
* From the Logomarks tools (shatterparts.mjs, batch 231, "Bold · Shatter", and batch 241).
|
|
17
|
+
*/
|
|
18
|
+
import { U, D, I, poly, mapPts, bar, soften, mitreBand as mitred, mitreOffset as mitreOff } from '../geometry/index.js';
|
|
19
|
+
|
|
20
|
+
/** @typedef {import('../geometry/types.js').Point} Point */
|
|
21
|
+
/** @typedef {import('../geometry/types.js').Ring} Ring */
|
|
22
|
+
/** @typedef {import('../geometry/types.js').Polygon} Polygon */
|
|
23
|
+
/** @typedef {import('../geometry/types.js').Shape} Shape */
|
|
24
|
+
/** @typedef {import('../geometry/types.js').Geom} Geom */
|
|
25
|
+
/** @typedef {(s: number) => Point} Rail */
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A shard of a strip.
|
|
29
|
+
*
|
|
30
|
+
* @typedef {object} Shard
|
|
31
|
+
* @property {Ring} r Its ring.
|
|
32
|
+
* @property {number} s Its place along the strip, 0…1.
|
|
33
|
+
* @property {'inner' | 'outer'} base The rail its long side lies on.
|
|
34
|
+
* @property {Point} [c] Its centre (drift() adds it).
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A shard's move in drift().
|
|
39
|
+
*
|
|
40
|
+
* @typedef {object} ShardMove
|
|
41
|
+
* @property {number} [k] Its shrink about its centre (1: none).
|
|
42
|
+
* @property {number} [deg] Its turn, degrees.
|
|
43
|
+
* @property {readonly number[]} [d] Its move [dx, dy].
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/* ---------- measures ---------- */
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The pieces of a shape as single polygons ([outer, ...holes]).
|
|
50
|
+
*
|
|
51
|
+
* @param {Geom} s The shape.
|
|
52
|
+
* @returns {Shape} The polygons.
|
|
53
|
+
*/
|
|
54
|
+
const polys = (s) => (s.length && typeof s[0][0][0] === 'number' ? [s] : s);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The area of a ring.
|
|
58
|
+
*
|
|
59
|
+
* @param {readonly Point[]} r The ring.
|
|
60
|
+
* @returns {number} Its area.
|
|
61
|
+
*/
|
|
62
|
+
const area = (r) => Math.abs(r.reduce((s, p, i) => { const q = r[(i + 1) % r.length]; return s + p[0] * q[1] - q[0] * p[1]; }, 0) / 2);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The centre of mass of a ring.
|
|
66
|
+
*
|
|
67
|
+
* @param {readonly Point[]} r The ring.
|
|
68
|
+
* @returns {Point} Its centroid.
|
|
69
|
+
*/
|
|
70
|
+
const centroid = (r) => {
|
|
71
|
+
let a = 0, x = 0, y = 0;
|
|
72
|
+
r.forEach((p, i) => { const q = r[(i + 1) % r.length], c = p[0] * q[1] - q[0] * p[1]; a += c; x += (p[0] + q[0]) * c; y += (p[1] + q[1]) * c; });
|
|
73
|
+
return [x / (3 * a), y / (3 * a)];
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* A ring's width across its narrowest: over the edges every vertex lies behind (its hull edges), the least of each edge's
|
|
78
|
+
* greatest reach. A triangle's shortest altitude.
|
|
79
|
+
*
|
|
80
|
+
* @param {readonly Point[]} r The ring.
|
|
81
|
+
* @returns {number} The width.
|
|
82
|
+
*/
|
|
83
|
+
const minWidth = (r) => {
|
|
84
|
+
let best = Infinity;
|
|
85
|
+
r.forEach((p, i) => {
|
|
86
|
+
const q = r[(i + 1) % r.length], L = Math.hypot(q[0] - p[0], q[1] - p[1]); if (L < 1e-6) return;
|
|
87
|
+
let lo = 0, hi = 0; for (const v of r) { const d = ((q[0] - p[0]) * (v[1] - p[1]) - (q[1] - p[1]) * (v[0] - p[0])) / L; lo = Math.min(lo, d); hi = Math.max(hi, d); }
|
|
88
|
+
if (lo > -0.5 || hi < 0.5) best = Math.min(best, Math.max(-lo, hi));
|
|
89
|
+
});
|
|
90
|
+
return best;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The distance from the point p to the segment ab.
|
|
95
|
+
*
|
|
96
|
+
* @param {readonly number[]} p The point.
|
|
97
|
+
* @param {readonly number[]} a The segment's start.
|
|
98
|
+
* @param {readonly number[]} b Its end.
|
|
99
|
+
* @returns {number} The distance.
|
|
100
|
+
*/
|
|
101
|
+
const segDist = (p, a, b) => {
|
|
102
|
+
const dx = b[0] - a[0], dy = b[1] - a[1], L2 = dx * dx + dy * dy, t = L2 ? Math.max(0, Math.min(1, ((p[0] - a[0]) * dx + (p[1] - a[1]) * dy) / L2)) : 0;
|
|
103
|
+
return Math.hypot(p[0] - a[0] - t * dx, p[1] - a[1] - t * dy);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The gap between two shapes: 0 when they touch or overlap, else the least vertex-to-edge distance either way.
|
|
108
|
+
*
|
|
109
|
+
* @param {Geom} a One shape.
|
|
110
|
+
* @param {Geom} b The other.
|
|
111
|
+
* @returns {number} The gap.
|
|
112
|
+
*/
|
|
113
|
+
const gapBetween = (a, b) => {
|
|
114
|
+
if (I(a, b).length) return 0; let best = Infinity;
|
|
115
|
+
const rings = (s) => polys(s).flat();
|
|
116
|
+
for (const ra of rings(a)) for (const rb of rings(b)) for (const [P, Q] of [[ra, rb], [rb, ra]]) for (const p of P) for (let i = 0; i < Q.length; i++) best = Math.min(best, segDist(p, Q[i], Q[(i + 1) % Q.length]));
|
|
117
|
+
return best;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/* ---------- rails ---------- */
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The point t of the way from a to b.
|
|
124
|
+
*
|
|
125
|
+
* @param {readonly number[]} a From.
|
|
126
|
+
* @param {readonly number[]} b To.
|
|
127
|
+
* @param {number} t How far.
|
|
128
|
+
* @returns {Point} The point.
|
|
129
|
+
*/
|
|
130
|
+
const lerp = (a, b, t) => [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* A rail along a polyline, by arc length: s in 0…1 to a point.
|
|
134
|
+
*
|
|
135
|
+
* @param {readonly Point[]} pts The polyline.
|
|
136
|
+
* @returns {Rail} The rail.
|
|
137
|
+
*/
|
|
138
|
+
export const rail = (pts) => {
|
|
139
|
+
const L = pts.slice(1).map((p, i) => Math.hypot(p[0] - pts[i][0], p[1] - pts[i][1])), tot = L.reduce((a, b) => a + b, 0);
|
|
140
|
+
return (s) => { let d = Math.max(0, Math.min(1, s)) * tot; for (let i = 0; i < L.length; i++) { if (d <= L[i] || i === L.length - 1) return lerp(pts[i], pts[i + 1], Math.min(1, d / L[i])); d -= L[i]; } };
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The run of a closed outline from the vertex nearest p, forward, to the vertex nearest q.
|
|
145
|
+
*
|
|
146
|
+
* @param {readonly Point[]} ring The outline.
|
|
147
|
+
* @param {readonly number[]} p Where the run starts.
|
|
148
|
+
* @param {readonly number[]} q Where it ends.
|
|
149
|
+
* @returns {Point[]} The run.
|
|
150
|
+
*/
|
|
151
|
+
export const outlineRun = (ring, p, q) => {
|
|
152
|
+
const near = (t) => ring.reduce((b, v, i) => (Math.hypot(v[0] - t[0], v[1] - t[1]) < Math.hypot(ring[b][0] - t[0], ring[b][1] - t[1]) ? i : b), 0);
|
|
153
|
+
const i = near(p), j = near(q), out = []; for (let k = i; ; k = (k + 1) % ring.length) { out.push(ring[k]); if (k === j) break; } return out;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The two rails of a band w wide along a centreline, offset by mitres.
|
|
158
|
+
*
|
|
159
|
+
* @param {readonly Point[]} pts The centreline.
|
|
160
|
+
* @param {number} w The band's width.
|
|
161
|
+
* @returns {[Rail, Rail]} The left rail (y down), then the right.
|
|
162
|
+
*/
|
|
163
|
+
export const bandRails = (pts, w) => [rail(mitreOff(pts, w / 2)), rail(mitreOff(pts, -w / 2))];
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The place s on a rail nearest a point (to 1/400).
|
|
167
|
+
*
|
|
168
|
+
* @param {Rail} f The rail.
|
|
169
|
+
* @param {readonly number[]} p The point.
|
|
170
|
+
* @returns {number} The place.
|
|
171
|
+
*/
|
|
172
|
+
const closest = (f, p) => { let best = 0, d = Infinity; for (let q = 0; q <= 400; q++) { const v = f(q / 400), e = Math.hypot(v[0] - p[0], v[1] - p[1]); if (e < d) { d = e; best = q / 400; } } return best; };
|
|
173
|
+
|
|
174
|
+
/* ---------- the strip ---------- */
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* The shards of a band between two rails. n + 2 nodes alternate down the rails (outer first, at s = 0), each step `ratio`
|
|
178
|
+
* times the last, so the shards get smaller down the strip, and each inner node is jittered by up to `jit` of its step;
|
|
179
|
+
* the last two sit at s = 1 on both rails. Every three nodes in a row make a triangle, its rail sides following the rails'
|
|
180
|
+
* curves (`arcN` points), so the strip tiles the band; a triangle with no area (where the rails meet) is dropped.
|
|
181
|
+
*
|
|
182
|
+
* @param {Rail} inner The inner rail.
|
|
183
|
+
* @param {Rail} outer The outer rail.
|
|
184
|
+
* @param {{ n?: number, ratio?: number, jit?: number, rng?: () => number, arcN?: number }} [o] Shards (6), the step
|
|
185
|
+
* ratio (0.85), the jitter (0.3), the random source (Math.random) and the points along each rail side (80).
|
|
186
|
+
* @returns {Shard[]} The shards.
|
|
187
|
+
*/
|
|
188
|
+
export const shardStrip = (inner, outer, { n = 6, ratio = 0.85, jit = 0.3, rng = Math.random, arcN = 80 } = {}) => {
|
|
189
|
+
const steps = [...Array(n)].map((_, i) => ratio ** i), tot = steps.reduce((a, b) => a + b, 0), p = [0];
|
|
190
|
+
steps.forEach((d, i) => p.push(i === n - 1 ? 1 : p.at(-1) + d / tot)); p.push(1);
|
|
191
|
+
for (let j = 1; j < n; j++) p[j] += (rng() - 0.5) * 2 * jit * (steps[j - 1] / tot) * 0.5;
|
|
192
|
+
const ns = p.map((s, j) => ({ rail: j % 2 ? inner : outer, s }));
|
|
193
|
+
const run = (rail, s0, s1) => [...Array(arcN + 1)].map((_, q) => rail(s0 + ((s1 - s0) * q) / arcN));
|
|
194
|
+
return [...Array(n)].map((_, i) => { const [a, b, c] = ns.slice(i, i + 3); return { r: [...run(a.rail, a.s, c.s), b.rail(b.s)], s: (a.s + b.s + c.s) / 3, base: i % 2 ? 'inner' : 'outer' }; })
|
|
195
|
+
.filter((q) => area(q.r) > 1);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* A shard shrunk in by half a gap all round, each straight side's band run on 3g past its corners so an acute tip leaves
|
|
200
|
+
* no needle (a rail's short arc steps are not run on: on a hollow arc that would groove the shard), then opened by 3
|
|
201
|
+
* (soften) to clear any hairline left where an arc meets a side.
|
|
202
|
+
*
|
|
203
|
+
* @param {readonly Point[]} r The shard's ring.
|
|
204
|
+
* @param {number} g The gap.
|
|
205
|
+
* @returns {Ring | undefined} The inset ring (its largest piece), or nothing if it vanishes.
|
|
206
|
+
*/
|
|
207
|
+
export const inset = (r, g) => polys(D(poly(r), ...r.flatMap((p, i) => {
|
|
208
|
+
const q = r[(i + 1) % r.length], L = Math.hypot(q[0] - p[0], q[1] - p[1]);
|
|
209
|
+
if (L < 1e-6) return []; const e = L > 30 ? (3 * g) / L : 0, u = [(q[0] - p[0]) * e, (q[1] - p[1]) * e];
|
|
210
|
+
return [bar([p[0] - u[0], p[1] - u[1]], [q[0] + u[0], q[1] + u[1]], g)];
|
|
211
|
+
})))
|
|
212
|
+
.map((pg) => polys(soften([pg[0]], 0, 3))[0]?.[0]).filter(Boolean).sort((a, b) => area(b) - area(a))[0];
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* The body: the form less the band between the rails, cut back by half a gap along the crack. The crack runs from the
|
|
216
|
+
* outer rail's start to the first inner node `i0`, down the inner rail, and back out to the outer rail's end (where the
|
|
217
|
+
* rails part there), run on 40 past both ends so it cuts cleanly through the outline; it is a mitred band g wide, so its
|
|
218
|
+
* edge stays sharp. Slivers of form under 400 in area are dropped.
|
|
219
|
+
*
|
|
220
|
+
* @param {Geom} shape The form.
|
|
221
|
+
* @param {Rail} inner The inner rail.
|
|
222
|
+
* @param {Rail} outer The outer rail.
|
|
223
|
+
* @param {readonly number[]} i0 The first inner node.
|
|
224
|
+
* @param {number} g The gap.
|
|
225
|
+
* @param {number} [k=60] Points along each rail run.
|
|
226
|
+
* @returns {Shape} The body.
|
|
227
|
+
*/
|
|
228
|
+
export const cutBack = (shape, inner, outer, i0, g, k = 60) => {
|
|
229
|
+
const at = (f, s0, s1) => [...Array(k + 1)].map((_, q) => f(s0 + ((s1 - s0) * q) / k));
|
|
230
|
+
const o1 = outer(1), in1 = inner(1); let crack = [outer(0), ...at(inner, closest(inner, i0), 1)];
|
|
231
|
+
if (Math.hypot(o1[0] - in1[0], o1[1] - in1[1]) > 1) crack.push(o1);
|
|
232
|
+
crack = crack.filter((p, i) => !i || Math.hypot(p[0] - crack[i - 1][0], p[1] - crack[i - 1][1]) > 0.5);
|
|
233
|
+
const on = (a, b) => { const L = Math.hypot(b[0] - a[0], b[1] - a[1]); return [b[0] + ((b[0] - a[0]) / L) * 40, b[1] + ((b[1] - a[1]) / L) * 40]; };
|
|
234
|
+
const zone = poly([...crack, ...at(outer, 1, 0)]);
|
|
235
|
+
// the zone's outer side is a chord of a curved outline, so drop the hairline slivers of form it leaves outside the crack
|
|
236
|
+
return polys(D(shape, zone, mitred([on(crack[1], crack[0]), ...crack, on(crack.at(-2), crack.at(-1))], g))).filter((pg) => area(pg[0]) > 400);
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
/* ---------- the drift ---------- */
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* A shape shrunk by k about c, turned by deg and moved by (dx, dy).
|
|
243
|
+
*
|
|
244
|
+
* @param {Geom} s The shape.
|
|
245
|
+
* @param {readonly number[]} c The pivot.
|
|
246
|
+
* @param {number} k The shrink.
|
|
247
|
+
* @param {number} deg The turn, degrees.
|
|
248
|
+
* @param {readonly number[]} d The move.
|
|
249
|
+
* @returns {Geom} The shape, moved.
|
|
250
|
+
*/
|
|
251
|
+
const xf = (s, c, k, deg, [dx, dy]) => {
|
|
252
|
+
const a = (deg * Math.PI) / 180, co = Math.cos(a), si = Math.sin(a);
|
|
253
|
+
return mapPts(s, ([x, y]) => { const u = (x - c[0]) * k, v = (y - c[1]) * k; return [c[0] + u * co - v * si + dx, c[1] + u * si + v * co + dy]; });
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Lets the shards drift off the body, nearest first. `move(p)` gives each shard (its ring p.r, its place p.s, its centre
|
|
258
|
+
* p.c) its shrink, turn and move. A shard is pushed on along its move in steps until it sits at least g clear of the body
|
|
259
|
+
* and of every shard already placed; a shard under `min` across is dropped, so the strip thins out at its end.
|
|
260
|
+
*
|
|
261
|
+
* @param {Geom} body The body.
|
|
262
|
+
* @param {readonly Shard[]} shards The inset shards.
|
|
263
|
+
* @param {{ move: (p: Shard & { c: Point }) => ShardMove, g: number, step?: number, min: number }} o The move, the gap,
|
|
264
|
+
* the push step (4) and the least shard width.
|
|
265
|
+
* @returns {Geom[]} The placed shards.
|
|
266
|
+
*/
|
|
267
|
+
export const drift = (body, shards, { move, g, step = 4, min }) => {
|
|
268
|
+
const ranked = shards.map((p) => ({ ...p, c: centroid(p.r) })).sort((a, b) => a.s - b.s), placed = [];
|
|
269
|
+
ranked.forEach((p) => {
|
|
270
|
+
const { k = 1, deg = 0, d = [0, 0] } = move(p), L = Math.hypot(...d) || 1, u = [d[0] / L, d[1] / L];
|
|
271
|
+
let s, extra = 0; for (let t = 0; t < 80; t++) {
|
|
272
|
+
s = xf(poly(p.r), p.c, k, deg, [d[0] + u[0] * extra, d[1] + u[1] * extra]);
|
|
273
|
+
if (gapBetween(s, body) >= g && placed.every((q) => gapBetween(s, q) >= g)) break; extra += step;
|
|
274
|
+
}
|
|
275
|
+
if (minWidth(s[0]) >= min) placed.push(s);
|
|
276
|
+
});
|
|
277
|
+
return placed;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* A drift that spreads the shards away from a point inside the form (an exploded view of the broken edge): each moves out
|
|
282
|
+
* by `grow` [start, growth along the strip] times its distance from `from`, shrunk by `shrink` × its place, and turned up
|
|
283
|
+
* to ± `turn`° (seeded through Math.random, more further on).
|
|
284
|
+
*
|
|
285
|
+
* @param {readonly number[]} from The point they spread from.
|
|
286
|
+
* @param {readonly number[]} grow [start, growth along the strip].
|
|
287
|
+
* @param {number} [shrink=0.35] The shrink at the strip's end.
|
|
288
|
+
* @param {number} [turn=8] The most turn, degrees.
|
|
289
|
+
* @returns {(p: Shard & { c: Point }) => ShardMove} The move, for drift().
|
|
290
|
+
*/
|
|
291
|
+
export const spread = (from, grow, shrink = 0.35, turn = 8) => (p) => {
|
|
292
|
+
const t = p.s, e = grow[0] + grow[1] * t;
|
|
293
|
+
return { k: 1 - shrink * t, deg: (Math.random() - 0.5) * 2 * turn * (0.3 + t), d: [(p.c[0] - from[0]) * e, (p.c[1] - from[1]) * e] };
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/* ---------- the mark ---------- */
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* A form breaking on one side: the band between `inner` and `outer` splits into a strip of n shards (only those based on
|
|
300
|
+
* the outer rail are kept, unless `all`), the body is cut back, the shards are inset and drift by `move`. `min` scales
|
|
301
|
+
* with the gap, so no shard is under 28 across once the mark is fitted. Seeded through Math.random.
|
|
302
|
+
*
|
|
303
|
+
* @param {Geom} shape The form.
|
|
304
|
+
* @param {Rail} inner The inner rail.
|
|
305
|
+
* @param {Rail} outer The outer rail.
|
|
306
|
+
* @param {{ n?: number, ratio?: number, jit?: number, all?: boolean, move: (p: Shard & { c: Point }) => ShardMove, g: number, min?: number }} o
|
|
307
|
+
* Shards (6), the step ratio (0.8), the jitter (0.4), whether to keep every shard (true), the move, the gap and the least
|
|
308
|
+
* shard width (28/22 of g).
|
|
309
|
+
* @returns {Shape} The broken form.
|
|
310
|
+
*/
|
|
311
|
+
export const breakOff = (shape, inner, outer, { n = 6, ratio = 0.8, jit = 0.4, all = true, move, g, min = (28 / 22) * g }) => {
|
|
312
|
+
const sh = shardStrip(inner, outer, { n, ratio, jit, rng: Math.random }), body = cutBack(shape, inner, outer, sh[0].r.at(-1), g);
|
|
313
|
+
const kept = (all ? sh : sh.filter((p) => p.base === 'outer')).map((p) => ({ ...p, r: inset(p.r, g) })).filter((p) => p.r && area(p.r) > 1);
|
|
314
|
+
return U(body, ...drift(body, kept, { move, g, min }));
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* The house recipe: breakOff() with 6 shards, each step 0.8 of the last, all kept, spread out from `from` (the canvas
|
|
319
|
+
* centre) by 0.05 + 0.5 × their place along the strip, shrunk up to 35%, turned up to ±8°; any of it overridden by `o`.
|
|
320
|
+
*
|
|
321
|
+
* @param {Geom} shape The form.
|
|
322
|
+
* @param {Rail} inner The inner rail.
|
|
323
|
+
* @param {Rail} outer The outer rail.
|
|
324
|
+
* @param {{ from?: readonly number[], grow?: readonly number[], shrink?: number, turn?: number, n?: number, ratio?: number, jit?: number, all?: boolean, min?: number }} [o]
|
|
325
|
+
* Overrides.
|
|
326
|
+
* @returns {(g: number) => Shape} The build of the gap.
|
|
327
|
+
*/
|
|
328
|
+
export const houseBreak = (shape, inner, outer, o = {}) => (g) => {
|
|
329
|
+
const { from = [256, 256], grow = [0.05, 0.5], shrink = 0.35, turn = 8, ...rest } = o;
|
|
330
|
+
return breakOff(shape, inner, outer, { n: 6, ratio: 0.8, jit: 0.4, all: true, move: spread(from, grow, shrink, turn), ...rest, g });
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* The point k of the way from p to q.
|
|
335
|
+
*
|
|
336
|
+
* @param {readonly number[]} p From.
|
|
337
|
+
* @param {readonly number[]} q To.
|
|
338
|
+
* @param {number} k How far.
|
|
339
|
+
* @returns {Point} The point.
|
|
340
|
+
*/
|
|
341
|
+
export const lerpAt = (p, q, k) => [p[0] + (q[0] - p[0]) * k, p[1] + (q[1] - p[1]) * k];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Shape } from '../geometry/types.js';
|
|
2
|
+
|
|
3
|
+
/** One slab of a slat row: its turn (the sine, −1 to 1) and, optionally, its own width, height and centre shift. */
|
|
4
|
+
export interface Slat {
|
|
5
|
+
tau: number;
|
|
6
|
+
W?: number;
|
|
7
|
+
H?: number;
|
|
8
|
+
dy?: number;
|
|
9
|
+
}
|
|
10
|
+
/** A slat row's drawing: full width and height, perspective, gap, narrowest slab, centre and alignment. */
|
|
11
|
+
export interface SlatRowOptions {
|
|
12
|
+
W: number;
|
|
13
|
+
H: number;
|
|
14
|
+
k: number;
|
|
15
|
+
g: number;
|
|
16
|
+
wMin: number;
|
|
17
|
+
cx?: number;
|
|
18
|
+
cy?: number;
|
|
19
|
+
align?: 'centre' | 'base' | 'top';
|
|
20
|
+
}
|
|
21
|
+
/** A slab as its two edges, left then right: [x, top, bottom] each. */
|
|
22
|
+
export type SlatEdges = [number, number, number][];
|
|
23
|
+
|
|
24
|
+
/** The projected width of a slab of full width W turned by τ, never under wMin. */
|
|
25
|
+
export function turnWidth(W: number, tau: number, wMin: number): number;
|
|
26
|
+
/** A row of turning slabs side by side, every gap exactly g, centred on cx. */
|
|
27
|
+
export function slatRow(slabs: readonly Slat[], o: SlatRowOptions): SlatEdges[];
|
|
28
|
+
/** A slat row's slabs as one shape. */
|
|
29
|
+
export function quads(row: readonly (readonly (readonly number[])[])[]): Shape;
|
|
30
|
+
/** A slat row stood on its side as one shape ((x, y) becomes (y, x)). */
|
|
31
|
+
export function quadsT(row: readonly (readonly (readonly number[])[])[]): Shape;
|
|
32
|
+
/** n turns easing from a to b (sine ease). */
|
|
33
|
+
export function ease(n: number, a: number, b: number): number[];
|
|
34
|
+
/** n turns spaced evenly from a to b. */
|
|
35
|
+
export function lerpN(n: number, a: number, b: number): number[];
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning slats: a row of flat slabs, each seen turning on its own axis, so the row reads as one panel opening, a page
|
|
3
|
+
* turning or a fan. A slab turned by τ (the sine of its turn, −1 to 1) shows a projected width of W·cos and keeps its
|
|
4
|
+
* edges upright; its near edge grows and its far edge shrinks by the perspective k (half-heights H/2 · (1 ± k·τ)), so
|
|
5
|
+
* its top and bottom edges slope towards the far side; τ > 0 turns the right edge away. The gaps are upright strips, so
|
|
6
|
+
* they stay exactly g, and no slab comes out narrower than wMin (an edge-on slab is a sliver, never a hairline).
|
|
7
|
+
* From the Logomarks tools (slatparts.mjs, batch 236, "Bold · Slats").
|
|
8
|
+
*/
|
|
9
|
+
import { poly, U } from '../geometry/index.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The projected width of a slab of full width W turned by τ, never under wMin.
|
|
13
|
+
*
|
|
14
|
+
* @param {number} W The slab's full width.
|
|
15
|
+
* @param {number} tau The sine of its turn (−1 to 1).
|
|
16
|
+
* @param {number} wMin The narrowest it may show.
|
|
17
|
+
* @returns {number} Its projected width.
|
|
18
|
+
*/
|
|
19
|
+
export const turnWidth = (W, tau, wMin) => Math.max(wMin, W * Math.sqrt(Math.max(0, 1 - tau * tau)));
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A row of turning slabs, side by side with the gap g between every two, the row centred on x = cx. Each slab may carry
|
|
23
|
+
* its own full width W, height H and centre shift dy (a bowed row, or slabs shrinking along it); `align` stands every
|
|
24
|
+
* slab on one floor line ('base') or hangs it from one ('top').
|
|
25
|
+
*
|
|
26
|
+
* @param {readonly { tau: number, W?: number, H?: number, dy?: number }[]} slabs Each slab's turn, full width, full
|
|
27
|
+
* height and centre shift.
|
|
28
|
+
* @param {{ W: number, H: number, k: number, g: number, wMin: number, cx?: number, cy?: number, align?: 'centre' | 'base' | 'top' }} o
|
|
29
|
+
* The slabs' full width and height, the perspective k, the gap g, the narrowest slab wMin, the row's centre
|
|
30
|
+
* (cx, cy; 256, 256) and how the slabs line up (centre).
|
|
31
|
+
* @returns {[number, number, number][][]} Each slab as its two edges [[x, top, bottom], [x, top, bottom]], left then right.
|
|
32
|
+
*/
|
|
33
|
+
export function slatRow(slabs, { W, H, k, g, wMin, cx = 256, cy = 256, align = 'centre' }) {
|
|
34
|
+
const ws = slabs.map((s) => turnWidth(s.W ?? W, s.tau, wMin)), total = ws.reduce((a, b) => a + b, 0) + g * (slabs.length - 1);
|
|
35
|
+
let x = cx - total / 2;
|
|
36
|
+
return slabs.map((s, i) => {
|
|
37
|
+
const h = s.H ?? H, c = cy + (s.dy ?? 0), hl = (h / 2) * (1 + k * s.tau), hr = (h / 2) * (1 - k * s.tau);
|
|
38
|
+
const edge = (xx, hh) => (align === 'base' ? [xx, c + h / 2 - 2 * hh, c + h / 2] : align === 'top' ? [xx, c - h / 2, c - h / 2 + 2 * hh] : [xx, c - hh, c + hh]);
|
|
39
|
+
const out = [edge(x, hl), edge(x + ws[i], hr)]; x += ws[i] + g; return out;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A slat row's slabs as one shape.
|
|
45
|
+
*
|
|
46
|
+
* @param {readonly (readonly (readonly number[])[])[]} row The row (slatRow()).
|
|
47
|
+
* @returns {import('../geometry/types.js').Shape} The slabs.
|
|
48
|
+
*/
|
|
49
|
+
export const quads = (row) => U(...row.map(([[x0, t0, b0], [x1, t1, b1]]) => poly([[x0, t0], [x1, t1], [x1, b1], [x0, b0]])));
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A slat row stood on its side as one shape: every point (x, y) becomes (y, x), so upright slabs turning on a vertical
|
|
53
|
+
* axis become flat ones turning on a horizontal axis.
|
|
54
|
+
*
|
|
55
|
+
* @param {readonly (readonly (readonly number[])[])[]} row The row (slatRow()).
|
|
56
|
+
* @returns {import('../geometry/types.js').Shape} The slabs.
|
|
57
|
+
*/
|
|
58
|
+
export const quadsT = (row) => U(...row.map(([[x0, t0, b0], [x1, t1, b1]]) => poly([[t0, x0], [t1, x1], [b1, x1], [b0, x0]])));
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* n turns easing from a to b (a sine ease, so the turn speeds up through the middle).
|
|
62
|
+
*
|
|
63
|
+
* @param {number} n How many.
|
|
64
|
+
* @param {number} a The first.
|
|
65
|
+
* @param {number} b The last.
|
|
66
|
+
* @returns {number[]} The turns.
|
|
67
|
+
*/
|
|
68
|
+
export const ease = (n, a, b) => [...Array(n)].map((_, i) => a + ((b - a) * (1 - Math.cos((Math.PI * i) / (n - 1)))) / 2);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* n turns spaced evenly from a to b.
|
|
72
|
+
*
|
|
73
|
+
* @param {number} n How many.
|
|
74
|
+
* @param {number} a The first.
|
|
75
|
+
* @param {number} b The last.
|
|
76
|
+
* @returns {number[]} The turns.
|
|
77
|
+
*/
|
|
78
|
+
export const lerpN = (n, a, b) => [...Array(n)].map((_, i) => a + ((b - a) * i) / (n - 1));
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Point, Shape, Geom } from '../geometry/types.js';
|
|
2
|
+
|
|
3
|
+
/** A swash's width and balls: swell()'s w0, w1, peak and pow, the balls' radii r0 and r1, or a width profile. */
|
|
4
|
+
export interface SwashOptions {
|
|
5
|
+
w0?: number;
|
|
6
|
+
w1?: number;
|
|
7
|
+
peak?: number;
|
|
8
|
+
pow?: number;
|
|
9
|
+
r0?: number;
|
|
10
|
+
r1?: number;
|
|
11
|
+
width?: (t: number) => number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** The swash width profile: w0 at both ends swelling to w1 at `peak`. */
|
|
15
|
+
export function swell(o?: { w0?: number; w1?: number; peak?: number; pow?: number }): (t: number) => number;
|
|
16
|
+
/** A swash along a polyline, ball terminals r0 and r1. */
|
|
17
|
+
export function swashAlong(p: readonly Point[], o?: SwashOptions): Shape;
|
|
18
|
+
/** A swash along a turtle path (walk()). */
|
|
19
|
+
export function swash(at: Point, deg: number, steps: readonly (['L', number] | ['A', number, number])[], o?: SwashOptions): Shape;
|
|
20
|
+
/** A shape mirrored top to bottom across the canvas centre. */
|
|
21
|
+
export function flipY(g: Geom): Geom;
|
|
22
|
+
/** A quarter set into the top right and mirrored into four, each axis parted by g or joined. */
|
|
23
|
+
export function swashQuad(q: Geom, g: number, o?: { x?: 'gap' | 'join'; y?: 'gap' | 'join'; join?: number }): Shape;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Swashes: a calligraphic stroke along a turtle path or any polyline, thick in its bowl and thin at both ends, finished
|
|
3
|
+
* with ball terminals (walked as a turtle path, its curls turn smoothly); and the mirror layout that sets a quarter
|
|
4
|
+
* into four, each axis either joined or parted by exactly the gap. Drawn about the origin, y down.
|
|
5
|
+
* From the Logomarks tools (swashparts.mjs, batch 256).
|
|
6
|
+
*/
|
|
7
|
+
import { C, U, circle, walk, mapPts, flipX, move, bbox } from '../geometry/index.js';
|
|
8
|
+
import { dense, taper } from '../parts/index.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The swash's width profile: w0 at both ends swelling to w1 at `peak` (0 to 1 along it); `pow` sharpens the swell.
|
|
12
|
+
*
|
|
13
|
+
* @param {{ w0?: number, w1?: number, peak?: number, pow?: number }} [o] The end width (20), the widest (60), where it
|
|
14
|
+
* is widest (0.55) and the swell's power (1.4).
|
|
15
|
+
* @returns {(t: number) => number} The width at t (0 to 1 along the swash).
|
|
16
|
+
*/
|
|
17
|
+
export const swell = ({ w0 = 20, w1 = 60, peak = 0.55, pow = 1.4 } = {}) => {
|
|
18
|
+
const e = Math.log(0.5) / Math.log(peak);
|
|
19
|
+
return (t) => w0 + (w1 - w0) * Math.sin(Math.PI * t ** e) ** pow;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A swash along any polyline (dense, open): its width `width(t)` (t from 0 to 1 along it; swell(o) unless given), a ball
|
|
24
|
+
* of radius r0 on its start and r1 on its end (0: none).
|
|
25
|
+
*
|
|
26
|
+
* @param {readonly import('../geometry/types.js').Point[]} p The polyline.
|
|
27
|
+
* @param {{ w0?: number, w1?: number, peak?: number, r0?: number, r1?: number, pow?: number, width?: (t: number) => number }} [o]
|
|
28
|
+
* swell()'s options, the balls' radii (26, 28) and a width profile in its place.
|
|
29
|
+
* @returns {import('../geometry/types.js').Shape} The swash.
|
|
30
|
+
*/
|
|
31
|
+
export const swashAlong = (p, o = {}) => {
|
|
32
|
+
const { r0 = 26, r1 = 28, width = swell(o) } = o;
|
|
33
|
+
const parts = [taper(p, width)];
|
|
34
|
+
if (r0) parts.push(circle(...p[0], r0)); if (r1) parts.push(circle(...p.at(-1), r1));
|
|
35
|
+
return U(...parts);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A swash along a turtle path (the kit's walk(): from `at`, heading `deg`, through exact-radius turns), w0 wide at both
|
|
40
|
+
* ends swelling to w1 at `peak`, a ball of radius r0 on its start and r1 on its end (0: none); `width(t)` replaces the
|
|
41
|
+
* swell.
|
|
42
|
+
*
|
|
43
|
+
* @param {import('../geometry/types.js').Point} at The start.
|
|
44
|
+
* @param {number} deg The heading at the start (degrees, y down).
|
|
45
|
+
* @param {readonly (['L', number] | ['A', number, number])[]} steps The walk: runs and turns.
|
|
46
|
+
* @param {{ w0?: number, w1?: number, peak?: number, r0?: number, r1?: number, pow?: number, width?: (t: number) => number }} [o]
|
|
47
|
+
* swashAlong()'s options.
|
|
48
|
+
* @returns {import('../geometry/types.js').Shape} The swash.
|
|
49
|
+
*/
|
|
50
|
+
export const swash = (at, deg, steps, o = {}) => swashAlong(dense(walk(at, deg, steps), 2, false), o);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* A shape mirrored top to bottom across the canvas centre.
|
|
54
|
+
*
|
|
55
|
+
* @param {import('../geometry/types.js').Geom} g The shape.
|
|
56
|
+
* @returns {import('../geometry/types.js').Geom} Its mirror.
|
|
57
|
+
*/
|
|
58
|
+
export const flipY = (g) => mapPts(g, ([x, y]) => [x, 2 * C - y]);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* A quarter (drawn anywhere) set into the top right of the canvas and mirrored into four. Each axis is either parted by
|
|
62
|
+
* exactly g (the copies' nearest points are their points nearest the axis, so the gap is twice that distance) or joined
|
|
63
|
+
* (the copies overlap by `join`).
|
|
64
|
+
*
|
|
65
|
+
* @param {import('../geometry/types.js').Geom} q The quarter.
|
|
66
|
+
* @param {number} g The gap.
|
|
67
|
+
* @param {{ x?: 'gap' | 'join', y?: 'gap' | 'join', join?: number }} [o] What each axis does (gap) and the overlap (6).
|
|
68
|
+
* @returns {import('../geometry/types.js').Shape} The four quarters.
|
|
69
|
+
*/
|
|
70
|
+
export const swashQuad = (q, g, { x = 'gap', y = 'gap', join = 6 } = {}) => {
|
|
71
|
+
const [x0, , , y1] = bbox(U(q));
|
|
72
|
+
const m = move(q, (x === 'gap' ? C + g / 2 : C - join) - x0, (y === 'gap' ? C - g / 2 : C + join) - y1);
|
|
73
|
+
const half = U(m, flipX(m)); return U(half, flipY(half));
|
|
74
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Geom, Point, Polygon, Shape } from '../geometry/types.js';
|
|
2
|
+
|
|
3
|
+
export { box } from '../util/vec.js';
|
|
4
|
+
|
|
5
|
+
/** A parallelogram: bottom edge from (x, yb) w long, top edge h higher shifted dx. */
|
|
6
|
+
export function para(x: number, yb: number, w: number, h: number, dx: number): Polygon;
|
|
7
|
+
/** Points of the circle through a, m and b, from a through m to b. */
|
|
8
|
+
export function arc3(a: readonly number[], m: readonly number[], b: readonly number[], n?: number): Point[];
|
|
9
|
+
/** A concave star of n tips at radius R, its sides pulled towards radius rc. */
|
|
10
|
+
export function star(n: number, R: number, rc?: number, deg?: number, tips?: readonly number[]): Polygon;
|
|
11
|
+
/** A gap g wide across a star's arm at deg, d from the centre. */
|
|
12
|
+
export function armCut(deg: number, d: number, g: number): Polygon;
|
|
13
|
+
/** A folded ribbon B wide along pts, drawn as outlines t wide. */
|
|
14
|
+
export function ribbon(pts: readonly Point[], B: number, t: number): Shape;
|
|
15
|
+
/** Equal discs on a lattice of pitch p, the gap g between neighbours. */
|
|
16
|
+
export function beads(cells: readonly (readonly number[])[], g: number, p?: number): Shape;
|
|
17
|
+
/** √3 / 2. */
|
|
18
|
+
export const H3: number;
|
|
19
|
+
/** A bar w wide leaning `lean`° through (x, ym), cut flat at y0 and y1. */
|
|
20
|
+
export function slash(x: number, ym: number, w: number, lean: number, y0: number, y1: number): Polygon;
|
|
21
|
+
/** The lens marks' disc (radius 224, centred). */
|
|
22
|
+
export function DISC(): Polygon;
|
|
23
|
+
/** A lens gap from p to q, w wide at its middle. */
|
|
24
|
+
export function lensGap(p: readonly number[], q: readonly number[], w: number): Polygon;
|
|
25
|
+
/** A chevron: apex P, arms along A and B, w wide inward. */
|
|
26
|
+
export function techChevron(P: readonly number[], A: readonly number[], B: readonly number[], w: number, L?: number): Polygon;
|
|
27
|
+
/** A slab from a to b, its b end cut to a 90° point (a Keyed letter's key). */
|
|
28
|
+
export function pointed(a: readonly number[], b: readonly number[], w: number): Polygon;
|
|
29
|
+
/** The notched slab less the key grown by g, plus the key. */
|
|
30
|
+
export function keyed(notched: Geom, key: Geom, g: number): Shape;
|
|
31
|
+
/** The Kerf tile: a 448 square, corners rounded 78. */
|
|
32
|
+
export function TILE(): Polygon;
|
|
33
|
+
/** The folded ribbon letters' width (124). */
|
|
34
|
+
export const RB: number;
|
|
35
|
+
/** Their outline width once fitted (24). */
|
|
36
|
+
export const RT: number;
|
|
37
|
+
/** A folded ribbon letter along pts, as a build of the gap. */
|
|
38
|
+
export function rib(pts: readonly Point[]): (g: number) => Shape;
|
|
39
|
+
/** The four-point star the sparkles break. */
|
|
40
|
+
export function SPARK(): Polygon;
|