@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,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Faces and mascots. A face's features are drawn for a face of radius R round (0, 0), clustered up and to the right (a
|
|
3
|
+
* glance, never the centred stock smiley) and parted by the gap; frame() sets the eye radius and the mouth stroke,
|
|
4
|
+
* snapped to the gap when they come near it. A mascot is an everyday object as a character: two dot eyes knocked out
|
|
5
|
+
* (eyes()), stick limbs at least the house stroke (LIMB), headphones laid over a head. From the Logomarks tools
|
|
6
|
+
* (facesparts.mjs, batches 266, 273, 283, 293, 296, 305, 313).
|
|
7
|
+
*/
|
|
8
|
+
import { U, D, I, circle, rect, rr, poly, stroke, arcPts, linePts, rot, roundPoly, pill, grown, over, lens } from '../geometry/index.js';
|
|
9
|
+
import { spline } from '../parts/index.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* An open arc as a round-capped stroke.
|
|
13
|
+
*
|
|
14
|
+
* @param {number} cx The centre, x.
|
|
15
|
+
* @param {number} cy The centre, y.
|
|
16
|
+
* @param {number} r The radius.
|
|
17
|
+
* @param {number} a0 From, in degrees (0 = right, 90 = down).
|
|
18
|
+
* @param {number} a1 To, in degrees.
|
|
19
|
+
* @param {number} w The stroke.
|
|
20
|
+
* @returns {import('../geometry/types.js').Shape} The arc.
|
|
21
|
+
*/
|
|
22
|
+
export const arc = (cx, cy, r, a0, a1, w) => stroke(arcPts(cx, cy, r, a0, a1, 48), w, { roundCaps: true });
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A round-capped stroke along a smooth curve through pts (a straight one for two points).
|
|
26
|
+
*
|
|
27
|
+
* @param {readonly (readonly number[])[]} pts The knots.
|
|
28
|
+
* @param {number} w The stroke.
|
|
29
|
+
* @returns {import('../geometry/types.js').Shape} The limb.
|
|
30
|
+
*/
|
|
31
|
+
export const limb = (pts, w) => stroke(pts.length > 2 ? spline(pts, 16) : linePts(pts[0], pts[1], 12), w, { roundCaps: true });
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A half-disc hole with its flat side on top at y, centred on x, radius r (a grin).
|
|
35
|
+
*
|
|
36
|
+
* @param {number} x The centre, x.
|
|
37
|
+
* @param {number} y The flat side, y.
|
|
38
|
+
* @param {number} r The radius.
|
|
39
|
+
* @returns {import('../geometry/types.js').Shape} The grin.
|
|
40
|
+
*/
|
|
41
|
+
export const grinHole = (x, y, r) => I(circle(x, y, r, 160), rect(x - r - 2, y, 2 * r + 4, r + 4));
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A face's frame for radius R: the eye radius e, the mouth stroke w (and aw for shut eyes), each exactly the gap when it
|
|
45
|
+
* would come near it (so a small face's features read as the gap, not an uneven one), and the eyes' centres: a glance, up
|
|
46
|
+
* and to the right, the eyes parted by exactly the gap.
|
|
47
|
+
*
|
|
48
|
+
* @param {number} R The face's radius.
|
|
49
|
+
* @param {number} g The gap.
|
|
50
|
+
* @returns {import('./faces.js').FaceFrame} The frame.
|
|
51
|
+
*/
|
|
52
|
+
export const frame = (R, g) => {
|
|
53
|
+
const e = Math.max(0.15 * R, 15), w = 0.17 * R < 1.4 * g ? g : 0.17 * R, cx = 0.17 * R, ey = -0.27 * R;
|
|
54
|
+
return { e, w, aw: 0.9 * w < 1.4 * g ? g : 0.9 * w, ey, x1: cx - e - g / 2, x2: cx + e + g / 2, cx };
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The comma smile: a short hook starting under the right eye, turning down and back to the left.
|
|
59
|
+
*
|
|
60
|
+
* @param {number} R The face's radius.
|
|
61
|
+
* @param {number} g The gap.
|
|
62
|
+
* @param {import('./faces.js').FaceFrame} f The face's frame().
|
|
63
|
+
* @returns {import('../geometry/types.js').Shape} The smile.
|
|
64
|
+
*/
|
|
65
|
+
export const hook = (R, g, f) => arc(f.cx + 0.02 * R, f.ey + f.e + g + 0.1 * R, 0.26 * R, -20, 110, f.w);
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Where shut eyes sit: arcs are wider than dots, so their centres are parted for the arcs' ends to sit a gap apart.
|
|
69
|
+
*
|
|
70
|
+
* @param {import('./faces.js').FaceFrame} f The face's frame().
|
|
71
|
+
* @param {number} g The gap.
|
|
72
|
+
* @returns {number[]} The two centres, x.
|
|
73
|
+
*/
|
|
74
|
+
export const arcXs = (f, g) => { const h = f.e * 1.05 * Math.cos(Math.PI / 9) + f.aw / 2 + g / 2; return [f.cx - h, f.cx + h]; };
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* ^ : a closed, happy eye at (x, y).
|
|
78
|
+
*
|
|
79
|
+
* @param {number} x The eye, x.
|
|
80
|
+
* @param {number} y The eye, y.
|
|
81
|
+
* @param {import('./faces.js').FaceFrame} f The face's frame().
|
|
82
|
+
* @returns {import('../geometry/types.js').Shape} The eye.
|
|
83
|
+
*/
|
|
84
|
+
export const happyEye = (x, y, f) => arc(x, y + f.e * 0.55, f.e * 1.05, 200, 340, f.aw);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* ∪ : a closed, sleeping eye at (x, y).
|
|
88
|
+
*
|
|
89
|
+
* @param {number} x The eye, x.
|
|
90
|
+
* @param {number} y The eye, y.
|
|
91
|
+
* @param {import('./faces.js').FaceFrame} f The face's frame().
|
|
92
|
+
* @returns {import('../geometry/types.js').Shape} The eye.
|
|
93
|
+
*/
|
|
94
|
+
export const shutEye = (x, y, f) => arc(x, y - f.e * 0.45, f.e * 1.05, 20, 160, f.aw);
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Glance, the set's face: two dots and the comma smile.
|
|
98
|
+
*
|
|
99
|
+
* @param {number} R The face's radius.
|
|
100
|
+
* @param {number} g The gap.
|
|
101
|
+
* @returns {import('../geometry/types.js').Geom[]} The features.
|
|
102
|
+
*/
|
|
103
|
+
export const glance = (R, g) => { const f = frame(R, g); return [circle(f.x1, f.ey, f.e), circle(f.x2, f.ey, f.e), hook(R, g, f)]; };
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Laugh: both eyes shut happy over the wide mouth.
|
|
107
|
+
*
|
|
108
|
+
* @param {number} R The face's radius.
|
|
109
|
+
* @param {number} g The gap.
|
|
110
|
+
* @returns {import('../geometry/types.js').Shape[]} The features.
|
|
111
|
+
*/
|
|
112
|
+
export const laugh = (R, g) => { const f = frame(R, g), [a, b] = arcXs(f, g); return [happyEye(a, f.ey, f), happyEye(b, f.ey, f), grinHole(f.cx, f.ey + f.e + g, 0.36 * R)]; };
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* A five-point star, point up, outer radius r.
|
|
116
|
+
*
|
|
117
|
+
* @param {number} x The centre, x.
|
|
118
|
+
* @param {number} y The centre, y.
|
|
119
|
+
* @param {number} r The outer radius.
|
|
120
|
+
* @param {number} [k=0.48] The inner radius as a fraction of r.
|
|
121
|
+
* @returns {import('../geometry/types.js').Polygon} The star.
|
|
122
|
+
*/
|
|
123
|
+
export const star = (x, y, r, k = 0.48) => poly([...Array(10)].map((_, i) => { const a = (i * 36 - 90) * Math.PI / 180, q = i % 2 ? r * k : r; return [x + q * Math.cos(a), y + q * Math.sin(a)]; }));
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Kiss: both eyes shut happy over a puckered 3.
|
|
127
|
+
*
|
|
128
|
+
* @param {number} R The face's radius.
|
|
129
|
+
* @param {number} g The gap.
|
|
130
|
+
* @returns {import('../geometry/types.js').Shape[]} The features.
|
|
131
|
+
*/
|
|
132
|
+
export const kiss = (R, g) => {
|
|
133
|
+
const f = frame(R, g), [a, b] = arcXs(f, g), r = 0.075 * R + f.w / 4, y = f.ey + f.e + g + f.w / 2 + 0.1 * R + r, x = f.cx - 0.02 * R;
|
|
134
|
+
return [happyEye(a, f.ey, f), happyEye(b, f.ey, f), arc(x, y, r, -100, 90, f.w), arc(x, y + 2 * r, r, -90, 100, f.w)];
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Starstruck: two stars for eyes over the wide mouth.
|
|
139
|
+
*
|
|
140
|
+
* @param {number} R The face's radius.
|
|
141
|
+
* @param {number} g The gap.
|
|
142
|
+
* @returns {import('../geometry/types.js').Geom[]} The features.
|
|
143
|
+
*/
|
|
144
|
+
export const starstruck = (R, g) => {
|
|
145
|
+
const f = frame(R, g), r = f.e * 1.55, h = 0.95 * r + g / 2, y = f.ey - 0.02 * R;
|
|
146
|
+
return [star(f.cx - h, y, r), star(f.cx + h, y, r), grinHole(f.cx, y + 0.81 * r + g, 0.34 * R)];
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Hmm: one brow raised over the right eye, a short flat mouth pushed to the side.
|
|
151
|
+
*
|
|
152
|
+
* @param {number} R The face's radius.
|
|
153
|
+
* @param {number} g The gap.
|
|
154
|
+
* @returns {import('../geometry/types.js').Geom[]} The features.
|
|
155
|
+
*/
|
|
156
|
+
export const hmm = (R, g) => {
|
|
157
|
+
const f = frame(R, g), y = f.ey + f.e + g + f.w / 2 + 0.1 * R, ey = f.ey + 0.03 * R, bc = ey + 0.6 * f.e, rb = 1.6 * f.e + g + f.w / 2;
|
|
158
|
+
return [circle(f.x1, ey, f.e), circle(f.x2, ey, f.e), arc(f.x2 + 0.02 * R, bc, rb, 238, 302, f.w),
|
|
159
|
+
limb([[f.cx + 0.02 * R, y], [f.cx + 0.26 * R, y - 0.05 * R]], f.w)];
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Zipped: two dots over a zip, a slot with teeth across it and the pull hanging off its end.
|
|
164
|
+
*
|
|
165
|
+
* @param {number} R The face's radius.
|
|
166
|
+
* @param {number} g The gap.
|
|
167
|
+
* @returns {import('../geometry/types.js').Polygon[]} The features.
|
|
168
|
+
*/
|
|
169
|
+
export const zipped = (R, g) => {
|
|
170
|
+
const f = frame(R, g), y = f.ey + f.e + g + 0.18 * R, x0 = f.cx - 0.3 * R, x1 = f.cx + 0.22 * R, n = 4, p = 2 * g, t = 0.11 * R;
|
|
171
|
+
const xs = [...Array(n)].map((_, i) => x0 + g + i * p);
|
|
172
|
+
return [circle(f.x1, f.ey, f.e), circle(f.x2, f.ey, f.e), rect(x0, y - g / 2, x1 - x0, g), ...xs.map((x) => rect(x, y - t, g, 2 * t)),
|
|
173
|
+
rr(x1 - g * 0.2, y - g / 2, 0.14 * R, 0.2 * R, g * 0.6)];
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Dizzy: two spirals for eyes, a wavy mouth.
|
|
178
|
+
*
|
|
179
|
+
* @param {number} R The face's radius.
|
|
180
|
+
* @param {number} g The gap.
|
|
181
|
+
* @returns {import('../geometry/types.js').Shape[]} The features.
|
|
182
|
+
*/
|
|
183
|
+
export const dizzy = (R, g) => {
|
|
184
|
+
const f = frame(R, g), r = 0.27 * R, h = r + g / 2, y = f.ey + 0.04 * R;
|
|
185
|
+
const spiral = (x, s) => stroke([...Array(80)].map((_, i) => { const t = i / 79, a = s * (t * 1.75 * 2 * Math.PI) - Math.PI / 2, q = g * 0.25 + t * (r - g / 2 - g * 0.25);
|
|
186
|
+
return [x + q * Math.cos(a), y + q * Math.sin(a)]; }), g, { roundCaps: true });
|
|
187
|
+
const my = y + r + g + 0.12 * R, wave = [...Array(40)].map((_, i) => { const t = i / 39; return [f.cx - 0.24 * R + t * 0.48 * R, my + 0.045 * R * Math.sin(t * 4 * Math.PI)]; });
|
|
188
|
+
return [spiral(f.cx - h, 1), spiral(f.cx + h, 1), stroke(wave, g, { roundCaps: true })];
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/** A mascot's stick limbs: at least the house stroke. */
|
|
192
|
+
export const LIMB = 30;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Two dot eyes, parted by the gap, at (x, y): knock them out of a body.
|
|
196
|
+
*
|
|
197
|
+
* @param {number} g The gap.
|
|
198
|
+
* @param {number} x Their middle, x.
|
|
199
|
+
* @param {number} y Their middle, y.
|
|
200
|
+
* @param {number} [e=21] Their radius.
|
|
201
|
+
* @returns {import('../geometry/types.js').Polygon[]} The eyes.
|
|
202
|
+
*/
|
|
203
|
+
export const eyes = (g, x, y, e = 21) => [circle(x - e - g / 2, y, e), circle(x + e + g / 2, y, e)];
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Two stick legs under a body whose bottom is at y, centred on x.
|
|
207
|
+
*
|
|
208
|
+
* @param {number} x Their middle, x.
|
|
209
|
+
* @param {number} y The body's bottom.
|
|
210
|
+
* @param {number} [d=46] Each leg's distance from the middle.
|
|
211
|
+
* @param {number} [L=64] Their length below the body.
|
|
212
|
+
* @returns {import('../geometry/types.js').Shape[]} The legs.
|
|
213
|
+
*/
|
|
214
|
+
export const legs = (x, y, d = 46, L = 64) => [limb([[x - d, y - 10], [x - d, y + L]], LIMB), limb([[x + d, y - 10], [x + d, y + L]], LIMB)];
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* A mascot's pose: a waving arm on the left (up) and a hanging arm on the right, from shoulders at (±x, y).
|
|
218
|
+
*
|
|
219
|
+
* @param {number} x The shoulders' distance from the middle.
|
|
220
|
+
* @param {number} y The shoulders, y.
|
|
221
|
+
* @returns {import('../geometry/types.js').Shape[]} The arms.
|
|
222
|
+
*/
|
|
223
|
+
export const arms = (x, y) => [limb([[-x, y], [-x - 65, y - 25], [-x - 85, y - 95]], LIMB), limb([[x, y + 10], [x + 50, y + 55], [x + 52, y + 115]], LIMB)];
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* A body with two dot eyes knocked out at (x, y).
|
|
227
|
+
*
|
|
228
|
+
* @param {import('../geometry/types.js').Geom} body The body.
|
|
229
|
+
* @param {number} g The gap.
|
|
230
|
+
* @param {number} x The eyes' middle, x.
|
|
231
|
+
* @param {number} y The eyes' middle, y.
|
|
232
|
+
* @param {number} [e] Their radius (eyes()'s 21 when left out).
|
|
233
|
+
* @returns {import('../geometry/types.js').Shape} The body.
|
|
234
|
+
*/
|
|
235
|
+
export const eyed = (body, g, x, y, e) => D(body, ...eyes(g, x, y, e));
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* A waving arm from the shoulder (x, y), up and out to the side s (−1 left, 1 right).
|
|
239
|
+
*
|
|
240
|
+
* @param {number} x The shoulder, x.
|
|
241
|
+
* @param {number} y The shoulder, y.
|
|
242
|
+
* @param {number} [s=-1] The side.
|
|
243
|
+
* @returns {import('../geometry/types.js').Shape} The arm.
|
|
244
|
+
*/
|
|
245
|
+
export const waveArm = (x, y, s = -1) => limb([[x, y], [x + s * 65, y - 25], [x + s * 85, y - 95]], LIMB);
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* A hanging arm from the shoulder (x, y), down and out to the side s.
|
|
249
|
+
*
|
|
250
|
+
* @param {number} x The shoulder, x.
|
|
251
|
+
* @param {number} y The shoulder, y.
|
|
252
|
+
* @param {number} [s=1] The side.
|
|
253
|
+
* @returns {import('../geometry/types.js').Shape} The arm.
|
|
254
|
+
*/
|
|
255
|
+
export const hang = (x, y, s = 1) => limb([[x, y], [x + s * 50, y + 45], [x + s * 52, y + 105]], LIMB);
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* A pointed leaf from its base point (x, y), at deg (0 = right, −90 = up), length L and width W: a lemon's, a pineapple's
|
|
259
|
+
* crown, a carrot's top.
|
|
260
|
+
*
|
|
261
|
+
* @param {number} x The base, x.
|
|
262
|
+
* @param {number} y The base, y.
|
|
263
|
+
* @param {number} deg The direction, in degrees.
|
|
264
|
+
* @param {number} L The length.
|
|
265
|
+
* @param {number} W The width.
|
|
266
|
+
* @returns {import('../geometry/types.js').Shape} The leaf.
|
|
267
|
+
*/
|
|
268
|
+
export const leaf = (x, y, deg, L, W) => { const a = deg * Math.PI / 180; return lens(x + Math.cos(a) * L / 2, y + Math.sin(a) * L / 2, L, W, deg); };
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Headphones about (cx, cy): a band bw wide over the top along an ellipse rx × ry, from angle a0 round to a1 (degrees,
|
|
272
|
+
* 180 → 360 is the top half, y down), each end running straight down by `foot` (none when 0) into a cup cw wide and ch
|
|
273
|
+
* tall with round ends, centred under the end, its top at y = cy + top. Lay it over() the head.
|
|
274
|
+
*
|
|
275
|
+
* @param {{ rx: number, ry?: number, cx?: number, cy?: number, bw?: number, cw?: number, ch?: number, top?: number, foot?: number, a0?: number, a1?: number }} o The headphones.
|
|
276
|
+
* @returns {import('../geometry/types.js').Shape} The headphones.
|
|
277
|
+
*/
|
|
278
|
+
export const headphones = ({ rx, ry = rx, cx = 0, cy = 0, bw = 40, cw = 72, ch = 150, top = -20, foot = 0, a0 = 180, a1 = 360 }) => {
|
|
279
|
+
const pts = [...Array(91)].map((_, i) => { const t = ((a0 + ((a1 - a0) * i) / 90) * Math.PI) / 180; return [cx + rx * Math.cos(t), cy + ry * Math.sin(t)]; });
|
|
280
|
+
const [p, q] = [pts[0], pts[90]], run = foot > 0 ? [[[p[0], p[1] + foot]], [[q[0], q[1] + foot]]] : [[], []];
|
|
281
|
+
const cup = (x) => rr(x - cw / 2, cy + top, cw, ch, cw / 2);
|
|
282
|
+
return U(stroke([...run[0], ...pts, ...run[1]], bw), cup(p[0]), cup(q[0]));
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* ∪ : a shut, blissful eye (lids bowed down), e its size, w the lid's width.
|
|
287
|
+
*
|
|
288
|
+
* @param {number} x The eye, x.
|
|
289
|
+
* @param {number} y The eye, y.
|
|
290
|
+
* @param {number} e Its size.
|
|
291
|
+
* @param {number} w The lid's width.
|
|
292
|
+
* @returns {import('../geometry/types.js').Shape} The eye.
|
|
293
|
+
*/
|
|
294
|
+
export const lid = (x, y, e, w) => arc(x, y - e * 0.45, e * 1.05, 20, 160, w);
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* A focused eye for a gamer mascot: a disc with its top cut away on a slant falling towards the face's middle.
|
|
298
|
+
*
|
|
299
|
+
* @param {number} x The eye, x.
|
|
300
|
+
* @param {number} y The eye, y.
|
|
301
|
+
* @param {number} e Its radius.
|
|
302
|
+
* @param {number} s The side: −1 left, 1 right.
|
|
303
|
+
* @param {number} [deg=24] The slant, in degrees.
|
|
304
|
+
* @returns {import('../geometry/types.js').Shape} The eye.
|
|
305
|
+
*/
|
|
306
|
+
export const focusEye = (x, y, e, s, deg = 24) => I(circle(x, y, e, 120), rot(rect(x - 3 * e, y - 0.15 * e, 6 * e, 3 * e), s * -deg, x, y - 0.15 * e));
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* A nose: a rounded triangle point down, w across and h tall, its top at y.
|
|
310
|
+
*
|
|
311
|
+
* @param {number} x Its middle, x.
|
|
312
|
+
* @param {number} y Its top.
|
|
313
|
+
* @param {number} w Its width.
|
|
314
|
+
* @param {number} h Its height.
|
|
315
|
+
* @param {number} [r=0.3*w] The corner radius.
|
|
316
|
+
* @returns {import('../geometry/types.js').Polygon} The nose.
|
|
317
|
+
*/
|
|
318
|
+
export const nose = (x, y, w, h, r = 0.3 * w) => roundPoly([[x - w / 2, y], [x + w / 2, y], [x, y + h]], [r, r, 0.6 * r]);
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* A mic boom off a headphone cup: a stroke w wide through pts ending in a capsule mic m long and mw wide along its last run.
|
|
322
|
+
*
|
|
323
|
+
* @param {readonly (readonly number[])[]} pts The boom's knots, cup first.
|
|
324
|
+
* @param {number} w The boom's width.
|
|
325
|
+
* @param {number} [m=64] The mic's length.
|
|
326
|
+
* @param {number} [mw=46] The mic's width.
|
|
327
|
+
* @returns {import('../geometry/types.js').Shape} The boom and mic.
|
|
328
|
+
*/
|
|
329
|
+
export const micBoom = (pts, w, m = 64, mw = 46) => {
|
|
330
|
+
const a = pts.at(-2), b = pts.at(-1), L = Math.hypot(b[0] - a[0], b[1] - a[1]), u = [(b[0] - a[0]) / L, (b[1] - a[1]) / L];
|
|
331
|
+
return U(limb(pts, w), pill([b[0] - u[0] * (m / 2 - mw / 2), b[1] - u[1] * (m / 2 - mw / 2)], [b[0] + u[0] * (m / 2 - mw / 2), b[1] + u[1] * (m / 2 - mw / 2)], mw));
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* A head wearing the phones `P` (headphones()): the head cut round them by the gap; `front` (ears, tufts, a fin) sits in
|
|
336
|
+
* front of the band, which is cut round it by the gap and so runs behind it.
|
|
337
|
+
*
|
|
338
|
+
* @param {import('../geometry/types.js').Geom} head The head.
|
|
339
|
+
* @param {import('../geometry/types.js').Geom} P The headphones.
|
|
340
|
+
* @param {number} g The gap.
|
|
341
|
+
* @param {import('../geometry/types.js').Geom} [front] What stands in front of the band.
|
|
342
|
+
* @returns {import('../geometry/types.js').Shape} The head in headphones.
|
|
343
|
+
*/
|
|
344
|
+
export const wearFront = (head, P, g, front) => { const p = front ? D(P, grown(front, g)) : P; return front ? U(D(head, grown(p, g)), p, front) : over(head, p, g); };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Point, Polygon, Shape } from '../geometry/types.js';
|
|
2
|
+
|
|
3
|
+
/** A point r from (cx, cy) at deg (0 = right, 90 = down). */
|
|
4
|
+
export function at(r: number, deg: number, cx?: number, cy?: number): Point;
|
|
5
|
+
/** A closed smooth outline through a right half's knots, mirrored to the left. */
|
|
6
|
+
export function symmetric(half: readonly (readonly number[])[], n?: number): Polygon;
|
|
7
|
+
/** A wavy centreline from a to b. */
|
|
8
|
+
export function wave(a: readonly number[], b: readonly number[], amp: number, waves: number, n?: number, phase?: number): Point[];
|
|
9
|
+
/** A bowl seen from above: its body and its surface, parted by a ring of gap. */
|
|
10
|
+
export function bowlTop(g: number, o?: { rx?: number; ry?: number; depth?: number; top?: number; rim?: number; rimY?: number }): { body: Shape; surface: Polygon };
|
|
11
|
+
/** A tongue of flame h tall standing on (cx, cy), its point leaning. */
|
|
12
|
+
export function flameTongue(cx: number, cy: number, h: number, lean?: number): Polygon;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Food: the outlines and pieces dish marks share (a bowl seen from above, a symmetric outline, a wavy line, a tongue of
|
|
3
|
+
* flame). Every part is drawn about the origin (y down); a part that parts anything takes the gap g, so exact() keeps it
|
|
4
|
+
* 22 once fitted. From the Logomarks tools (foodparts.mjs, batches 159, 161, 165).
|
|
5
|
+
*/
|
|
6
|
+
import { D, poly, ellipse, sample, grown } from '../geometry/index.js';
|
|
7
|
+
import { splineClosed } from '../parts/index.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A point r from (cx, cy) at `deg` (0 = right, 90 = down).
|
|
11
|
+
*
|
|
12
|
+
* @param {number} r The distance.
|
|
13
|
+
* @param {number} deg The direction, in degrees.
|
|
14
|
+
* @param {number} [cx=0] The centre, x.
|
|
15
|
+
* @param {number} [cy=0] The centre, y.
|
|
16
|
+
* @returns {import('../geometry/types.js').Point} The point.
|
|
17
|
+
*/
|
|
18
|
+
export const at = (r, deg, cx = 0, cy = 0) => [cx + r * Math.cos((deg * Math.PI) / 180), cy + r * Math.sin((deg * Math.PI) / 180)];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A symmetric object: a closed smooth outline through a right half's knots (top to bottom, x ≥ 0), mirrored to the left.
|
|
22
|
+
*
|
|
23
|
+
* @param {readonly (readonly number[])[]} half The right half's knots, top to bottom.
|
|
24
|
+
* @param {number} [n=16] Spline points a span.
|
|
25
|
+
* @returns {import('../geometry/types.js').Polygon} The outline.
|
|
26
|
+
*/
|
|
27
|
+
export const symmetric = (half, n = 16) => poly(splineClosed([...half, ...half.slice(1, -1).reverse().map(([x, y]) => [-x, y])], n));
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A wavy centreline from a to b: `amp` either side, `waves` full periods, `phase` in periods.
|
|
31
|
+
*
|
|
32
|
+
* @param {readonly number[]} a The start.
|
|
33
|
+
* @param {readonly number[]} b The end.
|
|
34
|
+
* @param {number} amp The amplitude.
|
|
35
|
+
* @param {number} waves Full periods along it.
|
|
36
|
+
* @param {number} [n=80] Steps.
|
|
37
|
+
* @param {number} [phase=0] The phase, in periods.
|
|
38
|
+
* @returns {import('../geometry/types.js').Point[]} The line.
|
|
39
|
+
*/
|
|
40
|
+
export const wave = (a, b, amp, waves, n = 80, phase = 0) => {
|
|
41
|
+
const L = Math.hypot(b[0] - a[0], b[1] - a[1]), u = [(b[0] - a[0]) / L, (b[1] - a[1]) / L], v = [-u[1], u[0]];
|
|
42
|
+
return sample((t) => { const o = amp * Math.sin((t * waves + phase) * 2 * Math.PI); return [a[0] + u[0] * L * t + v[0] * o, a[1] + u[1] * L * t + v[1] * o]; }, 0, 1, n);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* A bowl seen a little from above: the mouth an ellipse rx × ry at y = top, the body the lower half of an ellipse
|
|
47
|
+
* rx × depth. Its surface (soup, rice, hummus) sits inside, parted from the lip by a ring of gap, `rim` wide at the sides
|
|
48
|
+
* and `rimY` at the back, so a dish can cut its toppings into the surface before joining the two.
|
|
49
|
+
*
|
|
50
|
+
* @param {number} g The gap.
|
|
51
|
+
* @param {{ rx?: number, ry?: number, depth?: number, top?: number, rim?: number, rimY?: number }} [o] The bowl.
|
|
52
|
+
* @returns {{ body: import('../geometry/types.js').Shape, surface: import('../geometry/types.js').Polygon }} The pieces.
|
|
53
|
+
*/
|
|
54
|
+
export const bowlTop = (g, { rx = 224, ry = 96, depth = 190, top = 0, rim = 30, rimY = 24 } = {}) => {
|
|
55
|
+
const outer = poly([...sample((t) => [rx * Math.cos(t), top + depth * Math.sin(t)], 0, Math.PI, 150), ...sample((t) => [rx * Math.cos(t), top + ry * Math.sin(t)], Math.PI, 2 * Math.PI, 150).slice(1, -1)]);
|
|
56
|
+
const surface = ellipse(0, top, rx - rim - g, ry - rimY - g, 0, 300);
|
|
57
|
+
return { body: D(outer, grown(surface, g)), surface };
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* A tongue of flame `h` tall standing on (cx, cy): a round foot rising to a point that leans `lean` (a fraction of h) to
|
|
62
|
+
* the right (left when negative), the fuller side bellying out under it.
|
|
63
|
+
*
|
|
64
|
+
* @param {number} cx The foot, x.
|
|
65
|
+
* @param {number} cy The foot, y.
|
|
66
|
+
* @param {number} h The height.
|
|
67
|
+
* @param {number} [lean=0.18] The lean, a fraction of h.
|
|
68
|
+
* @returns {import('../geometry/types.js').Polygon} The flame.
|
|
69
|
+
*/
|
|
70
|
+
export const flameTongue = (cx, cy, h, lean = 0.18) => {
|
|
71
|
+
const w = h * 0.4, s = Math.sign(lean) || 1;
|
|
72
|
+
return poly(splineClosed([[cx + lean * h, cy - h], [cx + s * w * 0.55, cy - h * 0.55], [cx + s * w * 0.62, cy - h * 0.2], [cx + s * w * 0.36, cy - h * 0.02], [cx, cy],
|
|
73
|
+
[cx - s * w * 0.36, cy - h * 0.02], [cx - s * w * 0.6, cy - h * 0.22], [cx - s * w * 0.4, cy - h * 0.5], [cx - s * w * 0.05, cy - h * 0.72]], 10));
|
|
74
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from './devices.js';
|
|
2
|
+
export * from './devglyphs.js';
|
|
3
|
+
export * from './weapons.js';
|
|
4
|
+
export * from './food.js';
|
|
5
|
+
export * from './sport.js';
|
|
6
|
+
export * from './bio.js';
|
|
7
|
+
export * from './faces.js';
|
|
8
|
+
export * from './animals.js';
|
|
9
|
+
export * from './rosettes.js';
|
|
10
|
+
export type { DeviceSize } from './devices.js';
|
|
11
|
+
export type { BladeKind } from './weapons.js';
|
|
12
|
+
export type { FaceFrame, HeadphoneOptions } from './faces.js';
|
|
13
|
+
export type { SidePhonesOptions } from './animals.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @logomarks/core/objects
|
|
3
|
+
*
|
|
4
|
+
* Objects the trade and character marks share, each drawn about the origin (y down) unless it says otherwise, a part
|
|
5
|
+
* that parts anything taking the gap g so exact() keeps it 22: devices (the phone slab and screen, windows, laptops,
|
|
6
|
+
* headsets) and the code glyphs knocked out of them, blades (knives, swords, axes, arrows), dishes, sport (the glove,
|
|
7
|
+
* pixel sprites), cells and strands, faces (features, mascot eyes and limbs, headphones), animals (the gecko build,
|
|
8
|
+
* two-track animals, bold outlines, tapers and seams) and rosettes.
|
|
9
|
+
*/
|
|
10
|
+
export * from './devices.js';
|
|
11
|
+
export * from './devglyphs.js';
|
|
12
|
+
export * from './weapons.js';
|
|
13
|
+
export * from './food.js';
|
|
14
|
+
export * from './sport.js';
|
|
15
|
+
export * from './bio.js';
|
|
16
|
+
export * from './faces.js';
|
|
17
|
+
export * from './animals.js';
|
|
18
|
+
export * from './rosettes.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Geom, Polygon, Shape } from '../geometry/types.js';
|
|
2
|
+
|
|
3
|
+
/** A ringed emblem's outer radius on the 512 canvas (224). */
|
|
4
|
+
export const RING_R: number;
|
|
5
|
+
/** The k-th of n directions round the centre, the first pointing up. */
|
|
6
|
+
export function dir(n: number, k: number, d0?: number): number;
|
|
7
|
+
/** A solid lens petal from r0 out to r0 + L, W wide, the k-th of n. */
|
|
8
|
+
export function lensPetal(n: number, k: number, L: number, W: number, r0?: number, d0?: number): Shape;
|
|
9
|
+
/** A shape's line: it less itself shrunk by w. */
|
|
10
|
+
export function outline(p: Geom, w: number): Shape;
|
|
11
|
+
/** n copies of a builder (k) => T. */
|
|
12
|
+
export function ring<T>(n: number, f: (k: number) => T): T[];
|
|
13
|
+
/** n lens petals as outlines in a ring of the same line, its outer edge R. */
|
|
14
|
+
export function lineRosette(n: number, W: number, w: number, R?: number): Shape;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rosettes: the line petals and rings festival emblems are drawn from. Directions are degrees, 0 = right, 90 = down; the
|
|
3
|
+
* first petal points up. From the Logomarks tools (rosetteparts.mjs, batches 252, 271).
|
|
4
|
+
*/
|
|
5
|
+
import { C, U, D, annulus, lens, polar, grow } from '../geometry/index.js';
|
|
6
|
+
|
|
7
|
+
/** A ringed emblem's outer radius on the 512 canvas. */
|
|
8
|
+
export const RING_R = 224;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The k-th of n directions round the centre, the first pointing up, all turned by d0.
|
|
12
|
+
*
|
|
13
|
+
* @param {number} n How many.
|
|
14
|
+
* @param {number} k Which.
|
|
15
|
+
* @param {number} [d0=0] The turn, in degrees.
|
|
16
|
+
* @returns {number} The direction, in degrees.
|
|
17
|
+
*/
|
|
18
|
+
export const dir = (n, k, d0 = 0) => -90 + d0 + (360 * k) / n;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A solid lens petal from radius r0 out to r0 + L, W wide, the k-th of n round the centre.
|
|
22
|
+
*
|
|
23
|
+
* @param {number} n How many petals.
|
|
24
|
+
* @param {number} k Which.
|
|
25
|
+
* @param {number} L Its length.
|
|
26
|
+
* @param {number} W Its width.
|
|
27
|
+
* @param {number} [r0=0] Where it starts, from the centre.
|
|
28
|
+
* @param {number} [d0=0] The turn, in degrees.
|
|
29
|
+
* @returns {import('../geometry/types.js').Shape} The petal.
|
|
30
|
+
*/
|
|
31
|
+
export const lensPetal = (n, k, L, W, r0 = 0, d0 = 0) => { const a = dir(n, k, d0); return lens(...polar(r0 + L / 2, a), L, W, a); };
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A petal's line: the shape less itself shrunk by w (a stroked closed outline would mitre sharp tips into spikes).
|
|
35
|
+
*
|
|
36
|
+
* @param {import('../geometry/types.js').Geom} p The shape.
|
|
37
|
+
* @param {number} w The line's width.
|
|
38
|
+
* @returns {import('../geometry/types.js').Shape} The line.
|
|
39
|
+
*/
|
|
40
|
+
export const outline = (p, w) => D(p, grow(p, -w));
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* n copies of a builder.
|
|
44
|
+
*
|
|
45
|
+
* @template T
|
|
46
|
+
* @param {number} n How many.
|
|
47
|
+
* @param {(k: number) => T} f The builder, given the copy's index.
|
|
48
|
+
* @returns {T[]} The copies.
|
|
49
|
+
*/
|
|
50
|
+
export const ring = (n, f) => [...Array(n)].map((_, k) => f(k));
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* n lens petals W wide as outlines w thick, overlapping their neighbours, in a ring of the same line whose outer edge is R.
|
|
54
|
+
*
|
|
55
|
+
* @param {number} n How many petals.
|
|
56
|
+
* @param {number} W Their width.
|
|
57
|
+
* @param {number} w The line's width.
|
|
58
|
+
* @param {number} [R=RING_R] The ring's outer edge.
|
|
59
|
+
* @returns {import('../geometry/types.js').Shape} The rosette.
|
|
60
|
+
*/
|
|
61
|
+
export const lineRosette = (n, W, w, R = RING_R) => {
|
|
62
|
+
const r = R - w / 2;
|
|
63
|
+
return U(annulus(C, C, r, w), ...[...Array(n)].map((_, k) => { const p = lensPetal(n, k, r, W); return D(p, grow(p, -w)); }));
|
|
64
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Polygon, Shape } from '../geometry/types.js';
|
|
2
|
+
|
|
3
|
+
/** A pixel sprite (rows of '#') centred on the origin, p a pixel. */
|
|
4
|
+
export function pixelSprite(rows: readonly string[], p?: number): Shape;
|
|
5
|
+
/** A smooth closed outline through a few knots. */
|
|
6
|
+
export function blobOf(pts: readonly (readonly number[])[], n?: number): Polygon;
|
|
7
|
+
/** A boxing glove hanging cuff up: fist, thumb and cuff parted by the gap. */
|
|
8
|
+
export function glove(g: number): Shape;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sport: the boxing glove, a smooth outline through knots and a pixel sprite centred on the origin. From the Logomarks
|
|
3
|
+
* tools (sportparts.mjs, batches 193, 229, 246).
|
|
4
|
+
*/
|
|
5
|
+
import { U, poly, ellipse, rr, over, move, pixels } from '../geometry/index.js';
|
|
6
|
+
import { splineClosed } from '../parts/index.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A pixel sprite (rows of '#'), p a pixel, centred on the origin (the kit's pixels() draws from the top-left corner).
|
|
10
|
+
*
|
|
11
|
+
* @param {readonly string[]} rows The rows.
|
|
12
|
+
* @param {number} [p=40] The pixel.
|
|
13
|
+
* @returns {import('../geometry/types.js').Shape} The sprite.
|
|
14
|
+
*/
|
|
15
|
+
export const pixelSprite = (rows, p = 40) => move(pixels(rows, p), (-rows[0].length * p) / 2, (-rows.length * p) / 2);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A smooth closed outline through a few knots.
|
|
19
|
+
*
|
|
20
|
+
* @param {readonly (readonly number[])[]} pts The knots.
|
|
21
|
+
* @param {number} [n=14] Spline points a span.
|
|
22
|
+
* @returns {import('../geometry/types.js').Polygon} The outline.
|
|
23
|
+
*/
|
|
24
|
+
export const blobOf = (pts, n = 14) => poly(splineClosed(pts, n));
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A boxing glove hanging cuff up, front on: the fist a rounded mitt, the thumb lobe on its inner (left) side parted off by
|
|
28
|
+
* the gap, the cuff a band parted off the top.
|
|
29
|
+
*
|
|
30
|
+
* @param {number} g The gap.
|
|
31
|
+
* @returns {import('../geometry/types.js').Shape} The glove.
|
|
32
|
+
*/
|
|
33
|
+
export const glove = (g) => {
|
|
34
|
+
const fist = blobOf([[-60, -80], [60, -96], [120, -40], [126, 60], [90, 150], [0, 176], [-90, 150], [-120, 70], [-104, -20]]);
|
|
35
|
+
const thumb = ellipse(-118, 30, 46, 74, 14, 160), cuff = rr(-78, -170, 150, 90 - g, 18);
|
|
36
|
+
return U(over(fist, thumb, g), cuff);
|
|
37
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Geom, Point, Polygon, Shape } from '../geometry/types.js';
|
|
2
|
+
|
|
3
|
+
/** A blade's outline kind. */
|
|
4
|
+
export type BladeKind = 'chef' | 'santoku' | 'drop' | 'cleaver';
|
|
5
|
+
|
|
6
|
+
/** A shape turned by deg about the origin, then moved by (dx, dy). */
|
|
7
|
+
export function place(s: Geom, deg: number, dx?: number, dy?: number): Shape;
|
|
8
|
+
/** A shape mirrored left to right about x = 0. */
|
|
9
|
+
export function flip(s: Geom): Shape;
|
|
10
|
+
/** A quadratic Bézier from p0 through c to p1, n + 1 points. */
|
|
11
|
+
export function bez(p0: readonly number[], c: readonly number[], p1: readonly number[], n?: number): Point[];
|
|
12
|
+
/** A blade's outline from the heel (x = 0) to the tip (x = L). */
|
|
13
|
+
export function bladePts(kind: BladeKind, L: number, H: number): Point[];
|
|
14
|
+
/** A knife's grip from x1 back L, h deep, its butt rounded. */
|
|
15
|
+
export function grip(x1: number, L: number, top: number, h: number, r?: number): Polygon;
|
|
16
|
+
/** A knife pointing right: blade and handle parted by the gap. */
|
|
17
|
+
export function knife(g: number, o?: { kind?: BladeKind; L?: number; H?: number; HL?: number; HH?: number; hy?: number }): { blade: Polygon; handle: Polygon };
|
|
18
|
+
/** A straight sword pointing up: blade, guard, hilt and pommel, each parted by the gap. */
|
|
19
|
+
export function sword(g: number, o?: { L?: number; W?: number; tip?: number; guard?: number[]; hilt?: number[]; pommel?: number; taper?: number }): { blade: Polygon; guard: Polygon; hilt: Polygon; pommel: Polygon };
|
|
20
|
+
/** Two copies of a shape crossed in an X at ±deg, the right-leaning one in front. */
|
|
21
|
+
export function crossed(s: Geom, deg: number, g: number): Shape;
|
|
22
|
+
/** A katana along a gentle curve: grip, guard and blade (or scabbard), parted by the gap. */
|
|
23
|
+
export function katana(g: number, o?: { sheathed?: boolean; L?: number; W?: number; tsukaL?: number; tsuba?: number[]; bend?: number }): { tsuka: Shape; tsuba: Shape; blade: Shape };
|
|
24
|
+
/** A felling axe pointing up: head and haft, parted by the gap. */
|
|
25
|
+
export function axe(g: number, o?: { W?: number; poll?: number; bit?: number; top?: number; bottom?: number; edge?: number }): { head: Polygon; haft: Polygon };
|
|
26
|
+
/** The axe as one mark, the head in front of the haft. */
|
|
27
|
+
export function axeCut(g: number, o?: { W?: number; poll?: number; bit?: number; top?: number; bottom?: number; edge?: number }): Shape;
|
|
28
|
+
/** An arrow pointing right from x0 to x1: shaft, head and vanes. */
|
|
29
|
+
export function arrow(g: number, x0: number, x1: number, o?: { w?: number; hl?: number; hw?: number; vl?: number; vw?: number }): { shaft: Polygon; head: Polygon; vanes: Shape };
|
|
30
|
+
/** Target rings about the origin: a bull and n rings, each a gap out. */
|
|
31
|
+
export function rings(g: number, o?: { r0?: number; w?: number; n?: number }): Shape;
|