@pieai/swimmer-avatar-kit 0.1.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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +23 -0
  3. package/README.md +125 -0
  4. package/UPSTREAM.md +47 -0
  5. package/dist/avatar.d.ts +2 -0
  6. package/dist/avatar.js +69 -0
  7. package/dist/avatar.js.map +1 -0
  8. package/dist/index.d.ts +3 -0
  9. package/dist/index.js +3 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/materials.d.ts +20 -0
  12. package/dist/materials.js +61 -0
  13. package/dist/materials.js.map +1 -0
  14. package/dist/react-three-fiber.d.ts +16 -0
  15. package/dist/react-three-fiber.js +52 -0
  16. package/dist/react-three-fiber.js.map +1 -0
  17. package/dist/recipe.d.ts +18 -0
  18. package/dist/recipe.js +118 -0
  19. package/dist/recipe.js.map +1 -0
  20. package/dist/types.d.ts +62 -0
  21. package/dist/types.js +2 -0
  22. package/dist/types.js.map +1 -0
  23. package/package.json +102 -0
  24. package/upstream-lock.json +38 -0
  25. package/vendor/kindergrimm/LICENSE +24 -0
  26. package/vendor/kindergrimm/src/gloss/catmullClark.js +129 -0
  27. package/vendor/kindergrimm/src/gloss/gface.d.ts +25 -0
  28. package/vendor/kindergrimm/src/gloss/gface.js +226 -0
  29. package/vendor/kindergrimm/src/gloss/gform.js +201 -0
  30. package/vendor/kindergrimm/src/gloss/ghair.js +534 -0
  31. package/vendor/kindergrimm/src/gloss/glayout.js +365 -0
  32. package/vendor/kindergrimm/src/gloss/gmedia.d.ts +28 -0
  33. package/vendor/kindergrimm/src/gloss/gmedia.js +470 -0
  34. package/vendor/kindergrimm/src/gloss/gpalette.d.ts +10 -0
  35. package/vendor/kindergrimm/src/gloss/gpalette.js +205 -0
  36. package/vendor/kindergrimm/src/gloss/gparts/blush.js +57 -0
  37. package/vendor/kindergrimm/src/gloss/gparts/body.js +47 -0
  38. package/vendor/kindergrimm/src/gloss/gparts/brows.js +55 -0
  39. package/vendor/kindergrimm/src/gloss/gparts/crest.js +88 -0
  40. package/vendor/kindergrimm/src/gloss/gparts/eyes.js +357 -0
  41. package/vendor/kindergrimm/src/gloss/gparts/frame.js +225 -0
  42. package/vendor/kindergrimm/src/gloss/gparts/hair.js +100 -0
  43. package/vendor/kindergrimm/src/gloss/gparts/hat.js +163 -0
  44. package/vendor/kindergrimm/src/gloss/gparts/index.js +33 -0
  45. package/vendor/kindergrimm/src/gloss/gparts/mark.js +139 -0
  46. package/vendor/kindergrimm/src/gloss/gparts/mouth.js +170 -0
  47. package/vendor/kindergrimm/src/gloss/gparts/nose.js +63 -0
  48. package/vendor/kindergrimm/src/gloss/gparts/specs.js +124 -0
  49. package/vendor/kindergrimm/src/gloss/grig.d.ts +41 -0
  50. package/vendor/kindergrimm/src/gloss/grig.js +286 -0
  51. package/vendor/kindergrimm/src/gloss/gshape.js +421 -0
  52. package/vendor/kindergrimm/src/gloss/gspecies.d.ts +10 -0
  53. package/vendor/kindergrimm/src/gloss/gspecies.js +338 -0
  54. package/vendor/kindergrimm/src/gloss/gtexture.js +406 -0
  55. package/vendor/kindergrimm/src/rng.d.ts +11 -0
  56. package/vendor/kindergrimm/src/rng.js +27 -0
@@ -0,0 +1,338 @@
1
+ // ---------------------------------------------------------------
2
+ // THE CASTING — the third copy of the project's oldest idea, and on
3
+ // purpose the third copy of the code: the gloss generator shares an
4
+ // idea with the drawn and voxel ones, not a runtime.
5
+ //
6
+ // A species is a table of loaded dice per part id:
7
+ //
8
+ // { style: { bear: 90, none: 10 } } an object → weighted pick, and
9
+ // what you leave out CANNOT happen
10
+ // { size: [.9, 1.2] } an array → a number in range
11
+ // { teeth: .85 } a number → a probability
12
+ //
13
+ // Anything a profile does not mention keeps the part's own default, so
14
+ // a profile states only what makes that species different. A species
15
+ // may also bias the BODY form (sphere vs cube) — but never the palette
16
+ // or the material: what a character is made of is a separate lever, and a
17
+ // lavender bear is still a bear. The EARS do the species work, and
18
+ // after the eye patch and the muzzle were both cut they do nearly all
19
+ // of it — which is worth knowing before adding a species: if it cannot
20
+ // be told apart by its silhouette, it has nothing to be told apart by.
21
+ //
22
+ // A species may also NAME the body forms it may be poured as, and the
23
+ // two BENT ones (`rock`, `slime`) are where that matters most: a lumpy
24
+ // rock is a creature and a humanoid is not one, so the humanoid's
25
+ // profile lists the two forms a person can be.
26
+ //
27
+ // `wildcard` is the free roll — the sheet the lab had before species
28
+ // existed. It stays the biggest slice on purpose: the casting is there
29
+ // to make the compound characters (bunny, robot) arrive assembled, not
30
+ // to turn the generator into eight fixed characters.
31
+ // ---------------------------------------------------------------
32
+
33
+ // A species may also weight the STANCE (none / biped) the same way it
34
+ // weights the body form. Head-only stays everyone's biggest slice — it
35
+ // is the house look — and the lump species pin it, because a slime
36
+ // with legs contradicts what a slime is.
37
+
38
+ export const GSPECIES = {
39
+ wildcard: { label: 'wildcard', cast: {} },
40
+
41
+ bear: {
42
+ label: 'bear',
43
+ stance: { none: 56, biped: 44 },
44
+ cast: {
45
+ crest: { style: { bear: 90, none: 10 }, size: [.72, .95], spread: [.5, .72] },
46
+ eyes: { style: { bead: 38, pupil: 24, oval: 20, sleepy: 18 },
47
+ size: [.125, .165] },
48
+ nose: { style: { none: 84, button: 10, dot: 6 } },
49
+ mouth: { style: { smile: 30, open: 24, flat: 20, cat: 16, frown: 10 }, teeth: .3 },
50
+ brows: { style: { none: 90, flat: 10 } },
51
+ },
52
+ },
53
+
54
+ bunny: {
55
+ label: 'bunny',
56
+ stance: { none: 56, biped: 44 },
57
+ cast: {
58
+ body: { tall: [.94, 1] },
59
+ crest: { style: { bunny: 95, none: 5 }, size: [.82, 1] },
60
+ eyes: { style: { pupil: 32, bead: 20, googly: 16, slab: 16, happy: 16 } },
61
+ // the big front teeth: grins often, and the line mouths carry
62
+ // the single fang (teeth + tongue on a line is the fang roll)
63
+ mouth: { style: { grin: 26, smile: 24, cat: 20, open: 14, zig: 16 },
64
+ teeth: .75, tongue: .4 },
65
+ nose: { style: { none: 88, dot: 8, heart: 4 } },
66
+ },
67
+ },
68
+
69
+ cat: {
70
+ label: 'cat',
71
+ stance: { none: 65, biped: 35 },
72
+ cast: {
73
+ crest: { style: { cat: 95, none: 5 } },
74
+ // set WIDE, and winking more than anybody else
75
+ eyes: { style: { bead: 28, pupil: 22, sleepy: 18, happy: 16, cross: 16 },
76
+ wink: .15, x: [.54, .72] },
77
+ mouth: { style: { cat: 44, smile: 20, zig: 16, flat: 20 }, teeth: .5, tongue: .5 },
78
+ nose: { style: { none: 86, cat: 9, dot: 5 } },
79
+ brows: { style: { none: 94, sad: 6 } },
80
+ },
81
+ },
82
+
83
+ monster: {
84
+ label: 'monster',
85
+ body: { cube: 34, sphere: 26, rock: 24, slime: 16 },
86
+ cast: {
87
+ crest: { style: { horns: 68, stubs: 16, none: 16 } },
88
+ eyes: { style: { cross: 18, angry: 18, slab: 16, googly: 14, ring: 12,
89
+ spiral: 10, wobble: 12 }, wink: .1 },
90
+ mouth: { style: { grin: 30, open: 28, holler: 22, zig: 20 },
91
+ teeth: .85, tongue: .5, size: [.58, .85] },
92
+ brows: { style: { none: 50, angry: 34, thick: 16 } },
93
+ blush: { style: { none: 80, stripes: 20 } },
94
+ },
95
+ },
96
+
97
+ // THE LUMPS — a rock and a slime as whole characters, not just body
98
+ // forms a wildcard can roll. Same name as the form on purpose: the
99
+ // species IS the shape. Head only (the form's foot or base is
100
+ // already its bottom), no hair, no extras — a rock in spectacles is
101
+ // a gag, and one gag per sheet is the mark part's job, not a
102
+ // species'. The silhouette does all the species work here, so the
103
+ // crest stays off it too.
104
+ rock: {
105
+ label: 'rock',
106
+ body: { rock: 100 },
107
+ stance: { none: 100 },
108
+ cast: {
109
+ crest: { style: { none: 100 } },
110
+ // heavy-lidded and unimpressed: a rock has been here a while
111
+ eyes: { style: { bead: 30, sleepy: 24, pupil: 18, oval: 14, cross: 7, angry: 7 },
112
+ size: [.11, .15] },
113
+ mouth: { style: { flat: 30, frown: 22, smile: 18, zig: 16, open: 14 }, teeth: .15 },
114
+ nose: { style: { none: 100 } },
115
+ brows: { style: { none: 55, flat: 25, angry: 20 } },
116
+ blush: { style: { none: 75, oval: 15, round: 10 } },
117
+ specs: { style: { none: 100 } },
118
+ mark: { style: { none: 100 } },
119
+ },
120
+ },
121
+
122
+ slime: {
123
+ label: 'slime',
124
+ body: { slime: 100 },
125
+ stance: { none: 100 },
126
+ cast: {
127
+ crest: { style: { none: 100 } },
128
+ // wide awake and delighted about it — the drop shape reads sad
129
+ // on its own, so the face pulls the other way
130
+ eyes: { style: { googly: 22, bead: 20, happy: 16, pupil: 14, oval: 12,
131
+ wobble: 8, spiral: 8 } },
132
+ mouth: { style: { open: 26, smile: 24, grin: 18, cat: 12, zig: 10, holler: 10 },
133
+ tongue: .5, teeth: .3 },
134
+ nose: { style: { none: 100 } },
135
+ brows: { style: { none: 85, worry: 15 } },
136
+ blush: { style: { none: 55, round: 25, oval: 20 } },
137
+ specs: { style: { none: 100 } },
138
+ mark: { style: { none: 100 } },
139
+ },
140
+ },
141
+
142
+ humanoid: {
143
+ label: 'humanoid',
144
+ // A PERSON STANDS. Head-only is the house look for a creature, but
145
+ // a floating humanoid head with no body reads as a decapitation,
146
+ // not a character — so this is the one species pinned to biped.
147
+ stance: { biped: 100 },
148
+ // A humanoid is a CHARACTER like the rest of the sheet — a ball or a
149
+ // block with a face and a haircut. It had a modeled skull for a
150
+ // while and the skull was the wrong object. It is NOT a rock or a
151
+ // slime: those two are creature shapes, and naming the two forms a
152
+ // person may be is what keeps them off this species.
153
+ body: { sphere: 62, cube: 38 },
154
+ // the one species that names its own palette and material — see
155
+ // the note in `ensureGParams`. A humanoid is made of SKIN.
156
+ palette: 'skin',
157
+ material: 'skin',
158
+ cast: {
159
+ // near-square and softly cornered: the head is the stage the
160
+ // face and the hair are set on, so it stays out of the way
161
+ body: { wide: [1, 1.12], tall: [.92, 1], corner: [2.6, 3.6] },
162
+ // ALWAYS DRESSED. A skin-coloured torso on a person reads as
163
+ // naked, not as a blank — the one biped that cannot go bare.
164
+ frame: { dressed: 1 },
165
+ // NO CREST. Ears and horns are the other species' silhouette
166
+ // work; a humanoid's is its HAIR, which is a part of its own now
167
+ // (`hair.js`) and owns the whole crown. The three ink plates that
168
+ // stood in for hair here — tuft, mop, curl — were a placeholder
169
+ // and are not dealt to anybody any more.
170
+ crest: { style: { none: 100 } },
171
+ // HATS ARE HUMAN. The part's own default is `none` at 100, so a
172
+ // bear cannot turn up in a beanie; this is the only table that
173
+ // deals them. `beanie` and `band` are worn on a bare head (see
174
+ // `hatBare`), so between them they also carry most of the sheet's
175
+ // bald characters — which is why they are not the rarest here.
176
+ hat: { style: { none: 82, beanie: 6, band: 4, bow: 4, flower: 3, crown: 1 } },
177
+ // THE HAIRCUT, and it is most of the character. Weighted the way
178
+ // a room of people is: short and medium carry it, the very long
179
+ // cuts and the tied-up ones are the ones you notice. `bald` stays
180
+ // in at a few percent because a bald chibi is a real character
181
+ // and the skull is good enough to show off.
182
+ // Weighted so the SHEET has styling, not one haircut in fourteen
183
+ // lengths. The blunt bowl is deliberately small — it was most of
184
+ // the sheet, and a blunt fringe is the one shape that makes every
185
+ // head look like the same head. The upswept group and the media
186
+ // melena carry it instead, which is roughly how a room of people
187
+ // actually looks.
188
+ hair: { style: { midi: 8, layers: 7, bob: 7, wavy: 5, long: 5, hime: 2,
189
+ side: 8, pixie: 8, curtain: 7, quiff: 7, swept: 7,
190
+ crop: 6, curly: 5, spiky: 4, bowl: 3,
191
+ twin: 4, pony: 4, buns: 3, bald: 3 } },
192
+ // THE WHOLE CATALOGUE ROLLS. The orb is the humanoid's signature
193
+ // and keeps the biggest slice, but every other eye in the table
194
+ // is reachable — a face this simple gets its variety from the
195
+ // features, and casting three styles gave three characters.
196
+ // Only the placement is opinionated: chibi eyes sit LOW, at or
197
+ // under the centreline, down in the wide cheeks with all that
198
+ // cranium empty above — the opposite of the characters' upper-half
199
+ // rule, and it is what the reference sheets all do.
200
+ // Listed in full because a cast style table is EXCLUSIVE — what
201
+ // you leave out cannot happen — so "all of them, orb first" has
202
+ // to be written out rather than implied.
203
+ //
204
+ // THE PROPORTIONS ARE THE SPECIES, measured off the chibi
205
+ // reference sheet and checked against the build (`__probe` in
206
+ // the lab): the eye's CENTRE lands ~63% down the head and its
207
+ // top ~50%, which is what "frente despejada, ojos a mitad de
208
+ // cara" actually means — the forehead is the whole top half and
209
+ // the eyes hang off the midline. Each eye is ~26% of the head's
210
+ // width. Guessing these put the face too high and too small
211
+ // twice; the numbers came off the sheet in the end.
212
+ eyes: { style: { orb: 22, pupil: 12, bead: 7, googly: 7,
213
+ slab: 6, oval: 5,
214
+ sleepy: 5, happy: 5, box: 3, round: 3, square: 3,
215
+ sparkle: 2, diamond: 2, cross: 2,
216
+ crescent: 1, star: 1, heart: 1, ring: 1,
217
+ flower: 1, angry: 1, wobble: 1, spiral: 1 },
218
+ size: [.15, .185], x: [.58, .70], y: [-.38, -.24], lid: .3 },
219
+ // a face with a real nose more often than the characters get one
220
+ nose: { style: { none: 62, button: 14, dot: 12, cat: 4, heart: 4, beak: 2, snout: 2 },
221
+ warm: .6 },
222
+ // SMALL, and a line rather than a maw. Two jobs: the reference
223
+ // mouth is a stroke a fraction of an eye wide, and a small
224
+ // mouth also REACHES less — the layout pushes a mouth clear of
225
+ // the eyes, so a maw under eyes this big lands on the chin.
226
+ mouth: { style: { smile: 26, cat: 16, flat: 14, zig: 10, frown: 8, none: 12,
227
+ open: 8, grin: 6 },
228
+ y: [-.52, -.36], size: [.32, .5] },
229
+ // the most browed species on the sheet, and the only one where
230
+ // brows are the RULE rather than an anecdote: the reference
231
+ // faces nearly all have a pair, high on that clear forehead,
232
+ // and on a face this plain they carry the whole expression
233
+ brows: { style: { none: 30, flat: 20, round: 16, sad: 12, angry: 12, worry: 10 },
234
+ lift: [.3, .5] },
235
+ blush: { style: { round: 28, oval: 22, none: 50 } },
236
+ },
237
+ },
238
+
239
+ robot: {
240
+ label: 'robot',
241
+ body: { cube: 90, sphere: 10 },
242
+ stance: { none: 40, biped: 60 },
243
+ cast: {
244
+ body: { corner: [3.4, 5.5] },
245
+ // a robot's torso is its own chassis — the pour, chrome and all —
246
+ // not a jumper pulled over it
247
+ frame: { dressed: .1 },
248
+ crest: { style: { stubs: 52, none: 36, horns: 12 }, size: [.58, .8] },
249
+ eyes: { style: { box: 38, square: 26, slab: 20, ring: 16 }, lid: .15 },
250
+ // mostly bare: a robot's face is the grid grin and the screen
251
+ mouth: { style: { grin: 44, flat: 28, zig: 28 } },
252
+ nose: { style: { none: 90, dot: 10 } },
253
+ brows: { style: { none: 86, flat: 14 } },
254
+ blush: { style: { none: 85, round: 15 } },
255
+ },
256
+ },
257
+ };
258
+
259
+ export const GSPECIES_IDS = Object.keys(GSPECIES);
260
+
261
+ // how the sheet deals them. Wildcard stays the biggest slice — see the
262
+ // header — and the compound species split the rest about evenly.
263
+ // `panda` used to sit here. Its whole identity was the ink eye patch,
264
+ // so when that went it cast a bear with different weights — a species
265
+ // you cannot tell from another is not a species. Its share went back
266
+ // to the bear it had become. `pig` is gone too, and its share went
267
+ // back to the wildcard.
268
+ // The lumps are dealt THIN because their body forms also still turn up
269
+ // through the wildcard and the monster, so their species share plus
270
+ // those rolls is the real rate of geology on a sheet.
271
+ export const GSPECIES_WEIGHTS = [
272
+ ['wildcard', 32], ['bear', 16], ['bunny', 12], ['cat', 12],
273
+ ['monster', 10], ['humanoid', 9], ['robot', 8],
274
+ ['rock', 5], ['slime', 4],
275
+ ];
276
+
277
+ const wpick = (rng, pairs) => {
278
+ let t = 0; for (const p of pairs) t += p[1];
279
+ let x = rng.r(0, t);
280
+ for (const p of pairs) { if ((x -= p[1]) < 0) return p[0]; }
281
+ return pairs[pairs.length - 1][0];
282
+ };
283
+
284
+ /** the species' opinion on which body form, if it has one — otherwise
285
+ * the sheet's own weighted deal. Weighted on BOTH sides: a uniform
286
+ * default made the two bent forms 40% of a sheet. */
287
+ export function pickGBody(speciesId, rng, defaultPairs, { without = null } = {}) {
288
+ const w = GSPECIES[speciesId]?.body;
289
+ let pairs = w ? Object.entries(w) : defaultPairs;
290
+ // a PINNED stance excludes the modeled forms before the roll — a
291
+ // rock cannot take a frame, and a biped-pinned sheet full of
292
+ // head-only lumps is the filter quietly not filtering
293
+ if (without) {
294
+ pairs = pairs.filter(([id]) => !without(id));
295
+ // a species whose whole table is excluded (a rock pinned to biped)
296
+ // falls back to the sheet's deal, still filtered
297
+ if (!pairs.length) pairs = defaultPairs.filter(([id]) => !without(id));
298
+ }
299
+ return wpick(rng, pairs);
300
+ }
301
+
302
+ /** the same deal for the STANCE — head-only or biped. */
303
+ export function pickGStance(speciesId, rng, defaultPairs) {
304
+ const w = GSPECIES[speciesId]?.stance;
305
+ return wpick(rng, w ? Object.entries(w) : defaultPairs);
306
+ }
307
+
308
+ /**
309
+ * The casting helper handed to every part's gen(). Three questions,
310
+ * and in each the species wins if it has an opinion and the part's own
311
+ * default applies if it does not.
312
+ */
313
+ export function gcastingFor(speciesId) {
314
+ const SP = GSPECIES[speciesId] ?? GSPECIES.wildcard;
315
+ return partId => {
316
+ const t = SP.cast?.[partId] ?? {};
317
+ return {
318
+ species: speciesId,
319
+ /** one of a weighted list: C.pick(rng, 'style', DEFAULT_PAIRS) */
320
+ pick(rng, k, pairs) {
321
+ const w = t[k];
322
+ const list = (w && typeof w === 'object' && !Array.isArray(w))
323
+ ? Object.entries(w).filter(([, n]) => n > 0) : pairs;
324
+ return wpick(rng, list);
325
+ },
326
+ /** a number: C.range(rng, 'size', .9, 1.4) */
327
+ range(rng, k, lo, hi) {
328
+ const r = t[k];
329
+ return Array.isArray(r) ? rng.r(r[0], r[1]) : rng.r(lo, hi);
330
+ },
331
+ /** a yes/no: C.chance(rng, 'teeth', .6) */
332
+ chance(rng, k, p) {
333
+ const c = t[k];
334
+ return rng.chance(typeof c === 'number' ? c : p);
335
+ },
336
+ };
337
+ };
338
+ }