@doan-labs/peek 1.1.0 → 2.0.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/README.md +45 -1
- package/dist/bloc.d.ts +2 -0
- package/dist/core.d.ts +9 -0
- package/dist/draw.d.ts +24 -2
- package/dist/index.d.ts +1 -9
- package/dist/index.js +589 -207
- package/dist/peek.svelte +75 -0
- package/dist/peek.svelte.d.ts +5 -0
- package/dist/svelte-peek.d.ts +14 -0
- package/dist/svelte-peek.js +2205 -0
- package/dist/svelte.d.ts +3 -0
- package/dist/svelte.js +1650 -0
- package/dist/svg.d.ts +3 -1
- package/dist/svg.js +546 -168
- package/dist/tables.d.ts +53 -0
- package/dist/vue.d.ts +17 -0
- package/dist/vue.js +2302 -0
- package/package.json +28 -5
- package/src/bloc.ts +326 -0
- package/src/core.ts +39 -0
- package/src/draw.ts +315 -122
- package/src/index.ts +1 -33
- package/src/peek.svelte +75 -0
- package/src/peek.svelte.d.ts +5 -0
- package/src/svelte-peek.ts +18 -0
- package/src/svelte.ts +3 -0
- package/src/svg.ts +15 -1
- package/src/tables.ts +47 -0
- package/src/vue.ts +133 -0
package/dist/svg.js
CHANGED
|
@@ -114,6 +114,34 @@ var COLORS = {
|
|
|
114
114
|
rose: { body: "#EFCAD7", deep: "#DFA4BA" },
|
|
115
115
|
aqua: { body: "#BDE1E5", deep: "#8FC7D1" }
|
|
116
116
|
};
|
|
117
|
+
var VARIANTS = ["peek", "haze", "bloc"];
|
|
118
|
+
var HAZE = {
|
|
119
|
+
lavender: { tint: "#C8B1F5", glow: "#F2EDFD" },
|
|
120
|
+
fog: { tint: "#9EC3F2", glow: "#E6F1FF" },
|
|
121
|
+
clay: { tint: "#F4AE93", glow: "#FEECE5" },
|
|
122
|
+
mint: { tint: "#A3DAB5", glow: "#E5F5EA" },
|
|
123
|
+
butter: { tint: "#E1CC7A", glow: "#F4F0E0" },
|
|
124
|
+
rose: { tint: "#F0A9C4", glow: "#FDEAF1" },
|
|
125
|
+
aqua: { tint: "#85D3DC", glow: "#E0F5F7" }
|
|
126
|
+
};
|
|
127
|
+
var WHEEL = [
|
|
128
|
+
"clay",
|
|
129
|
+
"butter",
|
|
130
|
+
"mint",
|
|
131
|
+
"aqua",
|
|
132
|
+
"fog",
|
|
133
|
+
"lavender",
|
|
134
|
+
"rose"
|
|
135
|
+
];
|
|
136
|
+
var BLOC = {
|
|
137
|
+
lavender: "#B093E5",
|
|
138
|
+
fog: "#7BA8DF",
|
|
139
|
+
clay: "#E38A66",
|
|
140
|
+
mint: "#82CB9B",
|
|
141
|
+
butter: "#E2CA6A",
|
|
142
|
+
rose: "#DF84A8",
|
|
143
|
+
aqua: "#5EC3CD"
|
|
144
|
+
};
|
|
117
145
|
var PARTS = {
|
|
118
146
|
eyes: ["oval", "bead", "ring"],
|
|
119
147
|
brows: ["arch", "bar", "wedge", "dash"],
|
|
@@ -339,6 +367,314 @@ var EXPRESSIONS = {
|
|
|
339
367
|
})
|
|
340
368
|
};
|
|
341
369
|
|
|
370
|
+
// src/bloc.ts
|
|
371
|
+
var RAD = Math.PI / 180;
|
|
372
|
+
var TRI = [
|
|
373
|
+
[170, 12.8],
|
|
374
|
+
[320, 312.8],
|
|
375
|
+
[20, 312.8]
|
|
376
|
+
];
|
|
377
|
+
var BODY = {
|
|
378
|
+
diamond: FACES.diamond,
|
|
379
|
+
semicircle: FACES.semicircle,
|
|
380
|
+
circle: FACES.circle,
|
|
381
|
+
triangle: {
|
|
382
|
+
body: { d: `M${TRI.map((p) => p.join(" ")).join("L")}Z` },
|
|
383
|
+
crown: { poly: TRI, r: 0 }
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
var strokeFor = (px) => px >= 96 ? 1 : px >= 56 ? 1.25 : px >= 40 ? 1.6 : px >= 30 ? 2 : 2.6;
|
|
387
|
+
var boostFor = (px) => px < 30 ? 1.25 : px < 56 ? 1.12 : 1;
|
|
388
|
+
var turn = (x, y, deg) => {
|
|
389
|
+
const c = Math.cos(deg * RAD);
|
|
390
|
+
const s = Math.sin(deg * RAD);
|
|
391
|
+
return [x * c - y * s, x * s + y * c];
|
|
392
|
+
};
|
|
393
|
+
var poly = (pts) => `M${pts.map(([x, y]) => `${f2(x)} ${f2(y)}`).join("L")}Z`;
|
|
394
|
+
function blocParts({ who, pose, o, defs, g }) {
|
|
395
|
+
const sh = FACES[who.face];
|
|
396
|
+
const { body, crown } = BODY[who.face];
|
|
397
|
+
const id = o.id;
|
|
398
|
+
const P = who.persona;
|
|
399
|
+
const spread = P.spread;
|
|
400
|
+
const st = strokeFor(o.size);
|
|
401
|
+
const up = boostFor(o.size);
|
|
402
|
+
const strong = BLOC[who.color];
|
|
403
|
+
const light = o.frame === "bone" || o.frame === "paper" ? COLORS[who.color].deep : COLORS[who.color].body;
|
|
404
|
+
const lineW = sh.brow.w * st;
|
|
405
|
+
defs.push(n("clipPath", "half", { id: `${id}-r` }, [
|
|
406
|
+
n("rect", "half.shape", { x: 170, y: -30, width: 200, height: 400 })
|
|
407
|
+
]));
|
|
408
|
+
const block = (key, fill, clip) => ("d" in body) ? n("path", key, { d: body.d, fill, "clip-path": clip }) : n("circle", key, {
|
|
409
|
+
cx: body.circle[0],
|
|
410
|
+
cy: body.circle[1],
|
|
411
|
+
r: body.circle[2],
|
|
412
|
+
fill,
|
|
413
|
+
"clip-path": clip
|
|
414
|
+
});
|
|
415
|
+
const eyeS = g("eyeS");
|
|
416
|
+
const r0 = Math.sqrt(sh.rx * sh.ry) * up;
|
|
417
|
+
const lid0 = g("lid");
|
|
418
|
+
const lidAsym = g("lidAsym");
|
|
419
|
+
const lower = clamp(g("lower"), 0, 1);
|
|
420
|
+
const pupil = g("pupil");
|
|
421
|
+
const gx = clamp(g("gx"), -1.1, 1.1);
|
|
422
|
+
const gy = clamp(g("gy"), -1.1, 1.1);
|
|
423
|
+
const tilt = g("lidTilt");
|
|
424
|
+
const bead = who.eyes === "bead";
|
|
425
|
+
const eyes = sh.eyes.map(([ex, ey], i) => {
|
|
426
|
+
const side = i === 0 ? -1 : 1;
|
|
427
|
+
const kk = `eye${i}`;
|
|
428
|
+
let lid = clamp(lid0 + (i === 0 ? lidAsym : -lidAsym * 0.3), 0, 1);
|
|
429
|
+
lid = lid + (1 - lid) * pose.blink;
|
|
430
|
+
const cut = clamp((lid - 0.1) / 0.9, 0, 1);
|
|
431
|
+
const r = bead ? r0 * 0.7 * eyeS * (0.4 + 0.6 * pupil) : r0 * eyeS;
|
|
432
|
+
const [tx, ty] = bead ? [ex + side * spread + gx * 6, ey + gy * 6] : [ex + side * spread, ey];
|
|
433
|
+
const a = side * tilt;
|
|
434
|
+
const edge = -r - 1 + cut * (2 * r + 2);
|
|
435
|
+
const floor = r + 1 - lower * r * 2.2;
|
|
436
|
+
const W = r + 2;
|
|
437
|
+
const lidAt = (x) => x * Math.tan(a * RAD) + edge / Math.cos(a * RAD);
|
|
438
|
+
const [t0, t1] = [lidAt(-W), lidAt(W)];
|
|
439
|
+
defs.push(n("clipPath", `${kk}.clip`, { id: `${id}-e${i}` }, [
|
|
440
|
+
n("path", `${kk}.clip.shape`, {
|
|
441
|
+
d: poly([
|
|
442
|
+
[-W, t0],
|
|
443
|
+
[W, t1],
|
|
444
|
+
[W, Math.max(floor, t1)],
|
|
445
|
+
[-W, Math.max(floor, t0)]
|
|
446
|
+
])
|
|
447
|
+
})
|
|
448
|
+
]));
|
|
449
|
+
const pr = sh.pr * pupil * up;
|
|
450
|
+
const px = gx * Math.max(0, r - pr) * 0.85;
|
|
451
|
+
const py = gy * Math.max(0, r - pr) * 0.75;
|
|
452
|
+
const rim = Math.min(r * 0.27 * Math.min(st, 1.6), r * 0.4);
|
|
453
|
+
const inner = bead ? [n("circle", `${kk}.bead`, { cx: 0, cy: 0, r: f2(r), fill: INK.ink })] : [
|
|
454
|
+
n("circle", `${kk}.white`, {
|
|
455
|
+
cx: 0,
|
|
456
|
+
cy: 0,
|
|
457
|
+
r: f2(r),
|
|
458
|
+
fill: INK.paper
|
|
459
|
+
}),
|
|
460
|
+
n("circle", `${kk}.pupil`, {
|
|
461
|
+
cx: f2(px),
|
|
462
|
+
cy: f2(py),
|
|
463
|
+
r: f2(pr),
|
|
464
|
+
fill: INK.ink
|
|
465
|
+
})
|
|
466
|
+
];
|
|
467
|
+
if (who.eyes === "ring")
|
|
468
|
+
inner.push(n("circle", `${kk}.rim`, {
|
|
469
|
+
cx: 0,
|
|
470
|
+
cy: 0,
|
|
471
|
+
r: f2(r - rim / 2),
|
|
472
|
+
fill: "none",
|
|
473
|
+
stroke: INK.ink,
|
|
474
|
+
"stroke-width": f2(rim)
|
|
475
|
+
}));
|
|
476
|
+
const co = smooth(0.86, 1, lid);
|
|
477
|
+
const yy = Math.min(edge, r) - lineW / 2;
|
|
478
|
+
return n("g", kk, { transform: `translate(${f2(tx)} ${f2(ty)})` }, [
|
|
479
|
+
n("g", `${kk}.inner`, { "clip-path": `url(#${id}-e${i})` }, inner),
|
|
480
|
+
n("path", `${kk}.closed`, {
|
|
481
|
+
d: `M${f2(-r * 0.95)} ${f2(yy)}H${f2(r * 0.95)}`,
|
|
482
|
+
transform: `rotate(${f2(a * 0.6)})`,
|
|
483
|
+
stroke: INK.ink,
|
|
484
|
+
"stroke-width": f2(lineW * 1.4),
|
|
485
|
+
opacity: co > 0.01 ? f2(co) : 0
|
|
486
|
+
})
|
|
487
|
+
]);
|
|
488
|
+
});
|
|
489
|
+
const bt = pose.browTilt;
|
|
490
|
+
const basym = g("browAsym");
|
|
491
|
+
const th = lineW * 2 * g("browW");
|
|
492
|
+
const hw = sh.eyes[1][0] - 170 + spread + Math.sqrt(sh.rx * sh.ry) * P.mul.eyeS * up;
|
|
493
|
+
const gap = smooth(0, 8, Math.abs(bt)) * th * 0.9;
|
|
494
|
+
const slant = P.add.browTilt * 0.3 - Math.atan(1.3 * basym / (2 * hw)) / RAD;
|
|
495
|
+
const y0 = sh.brow.y + g("browY") - (eyeS - 1) * sh.ry * 0.9 - 0.35 * basym;
|
|
496
|
+
const halves = (dy, len2 = 1) => [-1, 1].map((s2) => {
|
|
497
|
+
const [ox, oy] = turn(s2 * gap / 2, 0, slant);
|
|
498
|
+
const x0 = 170 + ox;
|
|
499
|
+
const yy = y0 + dy + oy;
|
|
500
|
+
const ang = slant + s2 * bt;
|
|
501
|
+
const [ux, uy] = turn(s2 * (hw * len2 - gap / 2), 0, ang);
|
|
502
|
+
const [nx, ny] = turn(0, th / 2, ang);
|
|
503
|
+
return [
|
|
504
|
+
[x0 - nx, yy - ny],
|
|
505
|
+
[x0 + ux - nx, yy + uy - ny],
|
|
506
|
+
[x0 + ux + nx, yy + uy + ny],
|
|
507
|
+
[x0 + nx, yy + ny]
|
|
508
|
+
];
|
|
509
|
+
});
|
|
510
|
+
const inside = (dy, len2 = 1) => halves(dy, len2).every((q) => q.every(([x, y]) => depth(crown, x, y) + 2 < 0));
|
|
511
|
+
let lo = 0;
|
|
512
|
+
let hi = 0;
|
|
513
|
+
if (!inside(0)) {
|
|
514
|
+
hi = 60;
|
|
515
|
+
for (let k = 0;k < 12; k++) {
|
|
516
|
+
const mid2 = (lo + hi) / 2;
|
|
517
|
+
if (inside(mid2))
|
|
518
|
+
hi = mid2;
|
|
519
|
+
else
|
|
520
|
+
lo = mid2;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
let len = 1;
|
|
524
|
+
if (!inside(hi)) {
|
|
525
|
+
let short = 0;
|
|
526
|
+
for (let k = 0;k < 12; k++) {
|
|
527
|
+
const mid2 = (short + len) / 2;
|
|
528
|
+
if (inside(hi, mid2))
|
|
529
|
+
short = mid2;
|
|
530
|
+
else
|
|
531
|
+
len = mid2;
|
|
532
|
+
}
|
|
533
|
+
len = short;
|
|
534
|
+
}
|
|
535
|
+
const bars = halves(hi, len).map((q, i) => n("path", `bar${i}`, { d: poly(q), fill: INK.ink }));
|
|
536
|
+
const m = sh.mouth;
|
|
537
|
+
const big = (who.face === "diamond" || who.face === "triangle" ? 1.25 : 1) * up;
|
|
538
|
+
const mw = m.w * Math.max(0.05, g("mw")) * big;
|
|
539
|
+
const mt = g("mt") * m.d * big;
|
|
540
|
+
const mb = g("mb") * m.d * big;
|
|
541
|
+
const mid = (mt + mb) / 2;
|
|
542
|
+
const kb = 4 / 3;
|
|
543
|
+
let d;
|
|
544
|
+
if (who.mouth === "poly") {
|
|
545
|
+
const s2 = Math.max(mw * 0.9, (mb - mt) / 2);
|
|
546
|
+
d = poly([
|
|
547
|
+
[-s2, mid],
|
|
548
|
+
[0, mid - s2],
|
|
549
|
+
[s2, mid],
|
|
550
|
+
[0, mid + s2]
|
|
551
|
+
]);
|
|
552
|
+
} else if (who.mouth === "box") {
|
|
553
|
+
const h = Math.max(mb - mt, lineW * 1.6) / 2;
|
|
554
|
+
const w = mw * 0.85;
|
|
555
|
+
d = poly([
|
|
556
|
+
[-w, mid - h],
|
|
557
|
+
[w, mid - h],
|
|
558
|
+
[w, mid + h],
|
|
559
|
+
[-w, mid + h]
|
|
560
|
+
]);
|
|
561
|
+
} else if (who.mouth === "line") {
|
|
562
|
+
const h = lineW * 1.4 / 2;
|
|
563
|
+
const w = mw * 1.5;
|
|
564
|
+
const y = mid * 0.5;
|
|
565
|
+
d = poly([
|
|
566
|
+
[-w, y - h],
|
|
567
|
+
[w, y - h],
|
|
568
|
+
[w, y + h],
|
|
569
|
+
[-w, y + h]
|
|
570
|
+
]);
|
|
571
|
+
} else {
|
|
572
|
+
d = `M${f2(-mw)} 0C${f2(-mw)} ${f2(mb * kb)} ${f2(mw)} ${f2(mb * kb)} ${f2(mw)} 0C${f2(mw)} ${f2(mt * kb)} ${f2(-mw)} ${f2(mt * kb)} ${f2(-mw)} 0Z`;
|
|
573
|
+
}
|
|
574
|
+
const mouth = n("path", "mouth", {
|
|
575
|
+
d,
|
|
576
|
+
transform: `translate(${f2(m.x + g("mx"))} ${f2(m.y + g("my"))}) rotate(${f2(g("mk"))})`,
|
|
577
|
+
fill: INK.ink
|
|
578
|
+
});
|
|
579
|
+
const hair = g("hair");
|
|
580
|
+
const s = seat(crown, Math.max(0, hair) * 8, 110 + Math.max(0, -hair) * 40);
|
|
581
|
+
const shape = who.trait === "square" ? n("rect", "trait.shape", { x: -21, y: -21, width: 42, height: 42 }) : who.trait === "dot" ? n("circle", "trait.shape", { cx: 0, cy: 0, r: 22 }) : who.trait === "ring" ? n("circle", "trait.shape", {
|
|
582
|
+
cx: 0,
|
|
583
|
+
cy: 0,
|
|
584
|
+
r: 17.5,
|
|
585
|
+
fill: "none",
|
|
586
|
+
stroke: strong,
|
|
587
|
+
"stroke-width": 13
|
|
588
|
+
}) : who.trait === "fin" ? n("path", "trait.shape", { d: "M-27 27V-27A54 54 0 0 1 27 27Z" }) : n("rect", "trait.shape", {
|
|
589
|
+
x: -16,
|
|
590
|
+
y: -30,
|
|
591
|
+
width: 32,
|
|
592
|
+
height: 60
|
|
593
|
+
});
|
|
594
|
+
const trait = n("g", "trait", {
|
|
595
|
+
transform: `translate(${f2(s.x)} ${f2(s.y + pose.lag)}) scale(${f2(up)})`,
|
|
596
|
+
fill: strong
|
|
597
|
+
}, [shape]);
|
|
598
|
+
return [
|
|
599
|
+
[block("body", strong), block("body.light", light, `url(#${id}-r)`), trait],
|
|
600
|
+
[...eyes, ...bars, mouth]
|
|
601
|
+
];
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// src/identity.ts
|
|
605
|
+
var STYLE = "peek";
|
|
606
|
+
var tidy = (s) => s.normalize("NFC").trim().replace(/\s+/g, " ").toLowerCase();
|
|
607
|
+
function fnv1a(str) {
|
|
608
|
+
let h = 2166136261;
|
|
609
|
+
for (const b of new TextEncoder().encode(str)) {
|
|
610
|
+
h ^= b;
|
|
611
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
612
|
+
}
|
|
613
|
+
return h >>> 0;
|
|
614
|
+
}
|
|
615
|
+
function mulberry32(a) {
|
|
616
|
+
return () => {
|
|
617
|
+
a = a + 1831565813 | 0;
|
|
618
|
+
let t = Math.imul(a ^ a >>> 15, 1 | a);
|
|
619
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
620
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
var LISTS = {
|
|
624
|
+
face: Object.keys(FACES),
|
|
625
|
+
color: Object.keys(COLORS),
|
|
626
|
+
...PARTS,
|
|
627
|
+
...ACCESSORIES
|
|
628
|
+
};
|
|
629
|
+
var AXES = Object.keys(LISTS);
|
|
630
|
+
function seed(name, axis, version = LATEST) {
|
|
631
|
+
return fnv1a(`${STYLE}@${version}:${axis}:${tidy(name)}`);
|
|
632
|
+
}
|
|
633
|
+
function identify(name, { version = LATEST } = {}) {
|
|
634
|
+
const lengths = VERSIONS[version];
|
|
635
|
+
if (!lengths)
|
|
636
|
+
throw new Error(`peek@${version} does not exist`);
|
|
637
|
+
const pick = (axis) => LISTS[axis][seed(name, axis, version) % lengths[axis]];
|
|
638
|
+
const rnd = mulberry32(seed(name, "persona", version));
|
|
639
|
+
const r = (a, b) => Math.round((a + rnd() * (b - a)) * 100) / 100;
|
|
640
|
+
const persona = {
|
|
641
|
+
spread: r(-7, 7),
|
|
642
|
+
blink: r(0.75, 1.4),
|
|
643
|
+
add: {
|
|
644
|
+
browY: r(-4, 4),
|
|
645
|
+
browTilt: r(-5, 5),
|
|
646
|
+
browArch: r(-0.2, 0.3),
|
|
647
|
+
rot: r(-3, 3),
|
|
648
|
+
hair: r(-0.2, 0.35),
|
|
649
|
+
gx: r(-0.12, 0.12),
|
|
650
|
+
my: r(-2, 3)
|
|
651
|
+
},
|
|
652
|
+
mul: {
|
|
653
|
+
eyeS: r(0.92, 1.08),
|
|
654
|
+
pupil: r(0.9, 1.12),
|
|
655
|
+
browW: r(0.85, 1.2),
|
|
656
|
+
mw: r(0.85, 1.18)
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
const key = tidy(name);
|
|
660
|
+
return {
|
|
661
|
+
key,
|
|
662
|
+
hash: fnv1a(key),
|
|
663
|
+
version,
|
|
664
|
+
face: pick("face"),
|
|
665
|
+
color: pick("color"),
|
|
666
|
+
eyes: pick("eyes"),
|
|
667
|
+
brows: pick("brows"),
|
|
668
|
+
mouth: pick("mouth"),
|
|
669
|
+
cheeks: pick("cheeks"),
|
|
670
|
+
trait: pick("trait"),
|
|
671
|
+
eyewear: pick("eyewear"),
|
|
672
|
+
headwear: pick("headwear"),
|
|
673
|
+
neckwear: pick("neckwear"),
|
|
674
|
+
persona
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
|
|
342
678
|
// src/draw.ts
|
|
343
679
|
var VB = [-30, -30, 400, 400];
|
|
344
680
|
var FLOOR = 370;
|
|
@@ -350,8 +686,8 @@ var smooth = (a, b, v) => {
|
|
|
350
686
|
};
|
|
351
687
|
var f2 = (v) => String(Math.round(v * 100) / 100);
|
|
352
688
|
var f3 = (v) => String(Math.round(v * 1000) / 1000);
|
|
353
|
-
var
|
|
354
|
-
var
|
|
689
|
+
var strokeFor2 = (px) => px >= 96 ? 1 : px >= 56 ? 1.25 : px >= 40 ? 1.6 : px >= 30 ? 2 : 2.4;
|
|
690
|
+
var RAD2 = Math.PI / 180;
|
|
355
691
|
var STEM = "M0 0Q3 -19 0 -36";
|
|
356
692
|
var LEAF0 = "M0 -33C-24 -30 -38 -52 -25 -53C-12 -54 -2 -43 0 -33Z";
|
|
357
693
|
var LEAF1 = "M0 -33C5 -54 28 -61 27 -48C26 -36 11 -31 0 -33Z";
|
|
@@ -383,18 +719,18 @@ function seat(crown, d, u) {
|
|
|
383
719
|
const at = ([x, y], b2) => ({
|
|
384
720
|
x: x + R * Math.sin(b2),
|
|
385
721
|
y: y - R * Math.cos(b2),
|
|
386
|
-
a: b2 /
|
|
722
|
+
a: b2 / RAD2
|
|
387
723
|
});
|
|
388
724
|
const P = crown.poly;
|
|
389
725
|
if (P.length === 1)
|
|
390
726
|
return at(P[0], R ? u / R : 0);
|
|
391
|
-
const
|
|
727
|
+
const n2 = P.length;
|
|
392
728
|
const dir = u >= 0 ? 1 : -1;
|
|
393
729
|
let left = Math.abs(u);
|
|
394
730
|
let i = 0;
|
|
395
731
|
let b = 0;
|
|
396
|
-
for (let step = 0;step <
|
|
397
|
-
const j = (i + dir +
|
|
732
|
+
for (let step = 0;step < n2; step++) {
|
|
733
|
+
const j = (i + dir + n2) % n2;
|
|
398
734
|
const [x0, y0] = P[i];
|
|
399
735
|
const [x1, y1] = P[j];
|
|
400
736
|
const ex = x1 - x0;
|
|
@@ -414,7 +750,7 @@ function seat(crown, d, u) {
|
|
|
414
750
|
return {
|
|
415
751
|
x: x0 + ex / L * left + R * nx,
|
|
416
752
|
y: y0 + ey / L * left + R * ny,
|
|
417
|
-
a: be /
|
|
753
|
+
a: be / RAD2
|
|
418
754
|
};
|
|
419
755
|
left -= L;
|
|
420
756
|
i = j;
|
|
@@ -441,7 +777,7 @@ function depth(crown, x, y) {
|
|
|
441
777
|
}
|
|
442
778
|
return (out <= 0 ? out : near) - crown.r;
|
|
443
779
|
}
|
|
444
|
-
var RING_U = 158 * 35 *
|
|
780
|
+
var RING_U = 158 * 35 * RAD2;
|
|
445
781
|
function ringWalk(sh) {
|
|
446
782
|
const ex = sh.eyes[1][0] + sh.brow.hw + 7;
|
|
447
783
|
const top = sh.brow.y - 30;
|
|
@@ -475,14 +811,11 @@ function prune(node) {
|
|
|
475
811
|
}
|
|
476
812
|
function draw(who, pose, o) {
|
|
477
813
|
const sh = FACES[who.face];
|
|
478
|
-
const { body: fill, deep } = COLORS[who.color];
|
|
479
814
|
const P = who.persona;
|
|
480
815
|
const mul = P.mul;
|
|
481
816
|
const add = P.add;
|
|
482
817
|
const g = (c) => pose[c] * (mul[c] ?? 1) + (add[c] ?? 0);
|
|
483
|
-
const spread = P.spread;
|
|
484
818
|
const id = o.id;
|
|
485
|
-
const st = strokeFor(o.size);
|
|
486
819
|
const { baseY, R } = frameOf(sh);
|
|
487
820
|
const defs = [];
|
|
488
821
|
const VBr = { x: VB[0], y: VB[1], width: VB[2], height: VB[3] };
|
|
@@ -576,7 +909,165 @@ function draw(who, pose, o) {
|
|
|
576
909
|
const B = sh.bottom;
|
|
577
910
|
const ty = baseY - alt * R;
|
|
578
911
|
const sc = (v) => Math.round(v * 1e5) / 1e5;
|
|
579
|
-
const
|
|
912
|
+
const [front, back] = (o.variant === "bloc" ? blocParts : peekParts)({
|
|
913
|
+
who,
|
|
914
|
+
pose,
|
|
915
|
+
o,
|
|
916
|
+
defs,
|
|
917
|
+
g
|
|
918
|
+
});
|
|
919
|
+
const off = (dx, dy) => riso ? `translate(${f2(dx * k * upp)} ${f2(dy * k * upp)})` : undefined;
|
|
920
|
+
const av = n("g", "av", {
|
|
921
|
+
transform: `translate(${f2(x)} ${f2(ty)}) rotate(${f2(rot)} 170 ${B}) translate(170 ${B}) scale(${sc(sx)} ${sc(sy)}) translate(-170 ${-B})`,
|
|
922
|
+
filter: dim > 0.01 ? `url(#${id}-dim)` : undefined
|
|
923
|
+
}, [
|
|
924
|
+
n("g", "plateA", {
|
|
925
|
+
transform: off(-0.55, 0.45),
|
|
926
|
+
filter: riso ? `url(#${id}-pA)` : undefined
|
|
927
|
+
}, front),
|
|
928
|
+
n("g", "plateB", {
|
|
929
|
+
transform: off(0.6, -0.5),
|
|
930
|
+
filter: riso ? `url(#${id}-pB)` : undefined
|
|
931
|
+
}, back)
|
|
932
|
+
]);
|
|
933
|
+
const scene = [];
|
|
934
|
+
if (o.frame !== "none") {
|
|
935
|
+
const bg = o.frame === "ink" ? INK.ink : o.frame === "bone" ? INK.bone : INK.paper;
|
|
936
|
+
scene.push(n("rect", "bg", { ...VBr, fill: bg }));
|
|
937
|
+
const th = clamp(g("thing"), 0, 1);
|
|
938
|
+
const dots = [];
|
|
939
|
+
for (let j = 0;j < 3; j++)
|
|
940
|
+
for (let i = 0;i < 3; i++) {
|
|
941
|
+
const q = j * 3 + i;
|
|
942
|
+
const kk = smooth(q / 12, q / 12 + 0.35, th);
|
|
943
|
+
dots.push(n("circle", `lattice.${q}`, {
|
|
944
|
+
cx: 312 + i * 18,
|
|
945
|
+
cy: 2 + j * 18,
|
|
946
|
+
r: f2(1.2 + 2.2 * kk),
|
|
947
|
+
opacity: f3(kk * 0.55)
|
|
948
|
+
}));
|
|
949
|
+
}
|
|
950
|
+
scene.push(n("g", "lattice", { fill: o.frame === "ink" ? INK.bone : INK.ink }, dots));
|
|
951
|
+
}
|
|
952
|
+
scene.push(av);
|
|
953
|
+
if (grainy)
|
|
954
|
+
scene.push(n("rect", "grain", {
|
|
955
|
+
...VBr,
|
|
956
|
+
filter: `url(#${id}-grain)`,
|
|
957
|
+
opacity: o.frame === "ink" ? 0.3 : 0.16,
|
|
958
|
+
"pointer-events": "none"
|
|
959
|
+
}));
|
|
960
|
+
const root = n("svg", "root", {
|
|
961
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
962
|
+
viewBox: VB.join(" "),
|
|
963
|
+
width: o.size,
|
|
964
|
+
height: o.size,
|
|
965
|
+
role: o.title === false ? undefined : "img",
|
|
966
|
+
"aria-label": o.title === false ? undefined : o.title,
|
|
967
|
+
"aria-hidden": o.title === false ? "true" : undefined
|
|
968
|
+
}, [
|
|
969
|
+
n("defs", "defs", {}, defs),
|
|
970
|
+
n("g", "frame", { "clip-path": `url(#${id}-f)` }, scene)
|
|
971
|
+
]);
|
|
972
|
+
return o.live ? root : prune(root) ?? root;
|
|
973
|
+
}
|
|
974
|
+
var HAZE_LEVEL = {
|
|
975
|
+
medium: {
|
|
976
|
+
deep: [0.502, 0.224],
|
|
977
|
+
glow: [0.749, 0.337],
|
|
978
|
+
tint: [0.4, 0.5],
|
|
979
|
+
tintStops: [
|
|
980
|
+
[0, 0.878],
|
|
981
|
+
[0.15, 0.878],
|
|
982
|
+
[0.618, 0.396]
|
|
983
|
+
]
|
|
984
|
+
},
|
|
985
|
+
light: {
|
|
986
|
+
deep: [0.298, 0.133],
|
|
987
|
+
glow: [0.549, 0.247],
|
|
988
|
+
tint: [0.31, 0.387],
|
|
989
|
+
tintStops: [
|
|
990
|
+
[0, 0.549],
|
|
991
|
+
[0.55, 0.247]
|
|
992
|
+
]
|
|
993
|
+
}
|
|
994
|
+
};
|
|
995
|
+
function hazePaint(who, o) {
|
|
996
|
+
const sh = FACES[who.face];
|
|
997
|
+
const id = o.id;
|
|
998
|
+
const L = HAZE_LEVEL[o.size < 32 ? "light" : "medium"];
|
|
999
|
+
const xs = sh.crown.poly.map((p) => p[0]);
|
|
1000
|
+
const ys = sh.crown.poly.map((p) => p[1]);
|
|
1001
|
+
const x0 = Math.min(...xs) - sh.crown.r;
|
|
1002
|
+
const y0 = Math.min(...ys) - sh.crown.r;
|
|
1003
|
+
const W = Math.max(...xs) + sh.crown.r - x0;
|
|
1004
|
+
const H = sh.bottom - y0;
|
|
1005
|
+
const rnd = mulberry32(fnv1a(`haze:${who.key ?? ""}`));
|
|
1006
|
+
const warm = rnd() < 0.5;
|
|
1007
|
+
const left = rnd() < 0.5;
|
|
1008
|
+
const t = rnd();
|
|
1009
|
+
const tintX = 0.04 + 0.174 * t;
|
|
1010
|
+
const tintY = 0.649 + 0.202 * t;
|
|
1011
|
+
const glowX = 0.38 - 0.12 * rnd();
|
|
1012
|
+
const glowY = 0.12 + 0.099 * rnd();
|
|
1013
|
+
const deepX = 0.251 - 0.151 * rnd();
|
|
1014
|
+
const tintA = 90 - 180 * rnd();
|
|
1015
|
+
const at = (inset, onLeft) => x0 + W * (onLeft ? inset : 1 - inset);
|
|
1016
|
+
const i = WHEEL.indexOf(who.color);
|
|
1017
|
+
const near = WHEEL[(i + (warm ? WHEEL.length - 1 : 1)) % WHEEL.length];
|
|
1018
|
+
const { body, deep } = COLORS[who.color];
|
|
1019
|
+
const blob = (k, color, [cx, cy, a, rx, ry], stops) => n("radialGradient", `haze.${k}`, {
|
|
1020
|
+
id: `${id}-h${k[0]}`,
|
|
1021
|
+
gradientUnits: "userSpaceOnUse",
|
|
1022
|
+
cx: 0,
|
|
1023
|
+
cy: 0,
|
|
1024
|
+
r: 1,
|
|
1025
|
+
gradientTransform: `translate(${f2(cx)} ${f2(cy)}) rotate(${f2(a)}) scale(${f2(rx)} ${f2(ry)})`
|
|
1026
|
+
}, [...stops, [1, 0]].map(([at2, op], j) => n("stop", `haze.${k}.${j}`, {
|
|
1027
|
+
offset: at2,
|
|
1028
|
+
"stop-color": color,
|
|
1029
|
+
"stop-opacity": op
|
|
1030
|
+
})));
|
|
1031
|
+
const fade = ([a, b]) => [
|
|
1032
|
+
[0, a],
|
|
1033
|
+
[0.55, b]
|
|
1034
|
+
];
|
|
1035
|
+
const blobs = [
|
|
1036
|
+
blob("deep", deep, [at(deepX, !left), y0 + H * 0.95, 0, W * 0.5, W * 0.35], fade(L.deep)),
|
|
1037
|
+
blob("tint", HAZE[near].tint, [at(tintX, left), y0 + H * tintY, tintA, W * L.tint[0], W * L.tint[1]], L.tintStops),
|
|
1038
|
+
blob("glow", HAZE[who.color].glow, [at(glowX, !left), y0 + H * glowY, 0, W * 0.475, W * 0.356], fade(L.glow))
|
|
1039
|
+
];
|
|
1040
|
+
const shape = (key, paint) => ("d" in sh.body) ? n("path", key, {
|
|
1041
|
+
d: sh.body.d,
|
|
1042
|
+
fill: paint,
|
|
1043
|
+
stroke: sh.body.stroke ? paint : undefined,
|
|
1044
|
+
"stroke-width": sh.body.stroke,
|
|
1045
|
+
"stroke-linejoin": sh.body.stroke ? "round" : undefined
|
|
1046
|
+
}) : n("circle", key, {
|
|
1047
|
+
cx: sh.body.circle[0],
|
|
1048
|
+
cy: sh.body.circle[1],
|
|
1049
|
+
r: sh.body.circle[2],
|
|
1050
|
+
fill: paint
|
|
1051
|
+
});
|
|
1052
|
+
return [
|
|
1053
|
+
...blobs,
|
|
1054
|
+
n("g", "haze", { id: `${id}-hz` }, [
|
|
1055
|
+
shape("haze.body", body),
|
|
1056
|
+
...["deep", "tint", "glow"].map((k) => shape(`haze.${k}.fill`, `url(#${id}-h${k[0]})`))
|
|
1057
|
+
])
|
|
1058
|
+
];
|
|
1059
|
+
}
|
|
1060
|
+
function peekParts({ who, pose, o, defs, g }) {
|
|
1061
|
+
const sh = FACES[who.face];
|
|
1062
|
+
const { body: fill, deep } = COLORS[who.color];
|
|
1063
|
+
const P = who.persona;
|
|
1064
|
+
const spread = P.spread;
|
|
1065
|
+
const id = o.id;
|
|
1066
|
+
const st = strokeFor2(o.size);
|
|
1067
|
+
const haze = o.variant === "haze";
|
|
1068
|
+
if (haze)
|
|
1069
|
+
defs.push(...hazePaint(who, o));
|
|
1070
|
+
const bodyNode = haze ? n("use", "body", { href: `#${id}-hz` }) : ("d" in sh.body) ? n("path", "body", {
|
|
580
1071
|
d: sh.body.d,
|
|
581
1072
|
fill,
|
|
582
1073
|
stroke: sh.body.stroke ? fill : undefined,
|
|
@@ -664,25 +1155,36 @@ function draw(who, pose, o) {
|
|
|
664
1155
|
const lrx = rx * 1.55;
|
|
665
1156
|
const lry = ry * 1.15;
|
|
666
1157
|
const top = ry + 1.6 - lower * ry * 1.5;
|
|
1158
|
+
const [etx, ety] = bead ? [ex + side * spread + gx * 6, ey + gy * 6] : [ex + side * spread, ey];
|
|
1159
|
+
const lids = [
|
|
1160
|
+
n("rect", `${kk}.lid`, {
|
|
1161
|
+
x: f2(-rx * 2.2),
|
|
1162
|
+
y: f2(-ry * 3),
|
|
1163
|
+
width: f2(rx * 4.4),
|
|
1164
|
+
height: f2(Math.max(0, edge + ry * 3)),
|
|
1165
|
+
transform: `rotate(${f2(a)})`,
|
|
1166
|
+
fill
|
|
1167
|
+
}),
|
|
1168
|
+
n("ellipse", `${kk}.lower`, {
|
|
1169
|
+
cx: 0,
|
|
1170
|
+
cy: f2(top + lry),
|
|
1171
|
+
rx: f2(lrx),
|
|
1172
|
+
ry: f2(lry),
|
|
1173
|
+
fill
|
|
1174
|
+
})
|
|
1175
|
+
];
|
|
1176
|
+
if (haze)
|
|
1177
|
+
defs.push(n("clipPath", `${kk}.lidshape`, { id: `${id}-s${i}` }, lids));
|
|
667
1178
|
const parts = [
|
|
668
1179
|
n("g", `${kk}.inner`, { "clip-path": `url(#${id}-e${i})` }, inner),
|
|
669
|
-
n("g", `${kk}.lids`, { "clip-path": `url(#${id}-l${i})` }, [
|
|
670
|
-
n("
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
}),
|
|
678
|
-
n("ellipse", `${kk}.lower`, {
|
|
679
|
-
cx: 0,
|
|
680
|
-
cy: f2(top + lry),
|
|
681
|
-
rx: f2(lrx),
|
|
682
|
-
ry: f2(lry),
|
|
683
|
-
fill
|
|
684
|
-
})
|
|
685
|
-
])
|
|
1180
|
+
n("g", `${kk}.lids`, { "clip-path": `url(#${id}-l${i})` }, haze ? [
|
|
1181
|
+
n("g", `${kk}.lidpaint`, { "clip-path": `url(#${id}-s${i})` }, [
|
|
1182
|
+
n("use", `${kk}.lidbody`, {
|
|
1183
|
+
href: `#${id}-hz`,
|
|
1184
|
+
transform: `translate(${f2(-etx)} ${f2(-ety)})`
|
|
1185
|
+
})
|
|
1186
|
+
])
|
|
1187
|
+
] : lids)
|
|
686
1188
|
];
|
|
687
1189
|
if (who.eyes === "ring")
|
|
688
1190
|
parts.push(n("g", `${kk}.edges`, {
|
|
@@ -716,9 +1218,7 @@ function draw(who, pose, o) {
|
|
|
716
1218
|
"stroke-width": f2(sw * 0.85),
|
|
717
1219
|
opacity: co > 0.01 ? f2(co) : 0
|
|
718
1220
|
}));
|
|
719
|
-
return n("g", kk, {
|
|
720
|
-
transform: bead ? `translate(${f2(ex + side * spread + gx * 6)} ${f2(ey + gy * 6)})` : `translate(${f2(ex + side * spread)} ${ey})`
|
|
721
|
-
}, parts);
|
|
1221
|
+
return n("g", kk, { transform: `translate(${f2(etx)} ${f2(ety)})` }, parts);
|
|
722
1222
|
});
|
|
723
1223
|
const browY = g("browY") - (eyeS - 1) * sh.ry * 0.9;
|
|
724
1224
|
const btilt = g("browTilt");
|
|
@@ -738,8 +1238,8 @@ function draw(who, pose, o) {
|
|
|
738
1238
|
const half = (t) => wedge ? hl + (hr - hl) * t : bw * k2 / 2;
|
|
739
1239
|
const bx = ex + side * spread;
|
|
740
1240
|
const by = sh.brow.y + browY + (i === 1 ? -basym : basym * 0.3);
|
|
741
|
-
const cos = Math.cos(side * btilt *
|
|
742
|
-
const sin = Math.sin(side * btilt *
|
|
1241
|
+
const cos = Math.cos(side * btilt * RAD2);
|
|
1242
|
+
const sin = Math.sin(side * btilt * RAD2);
|
|
743
1243
|
const inside = (dy) => {
|
|
744
1244
|
for (let t = 0;t <= 1; t += 0.25) {
|
|
745
1245
|
const lx = w2 * (2 * t - 1);
|
|
@@ -755,7 +1255,7 @@ function draw(who, pose, o) {
|
|
|
755
1255
|
let hi = 0;
|
|
756
1256
|
if (!inside(0)) {
|
|
757
1257
|
hi = 40;
|
|
758
|
-
for (let
|
|
1258
|
+
for (let k = 0;k < 12; k++) {
|
|
759
1259
|
const mid2 = (lo + hi) / 2;
|
|
760
1260
|
if (inside(mid2))
|
|
761
1261
|
hi = mid2;
|
|
@@ -858,7 +1358,7 @@ function draw(who, pose, o) {
|
|
|
858
1358
|
fill: deep
|
|
859
1359
|
});
|
|
860
1360
|
} else if (who.trait === "fin") {
|
|
861
|
-
const s = seat(sh.crown, -6, -164 * 22 *
|
|
1361
|
+
const s = seat(sh.crown, -6, -164 * 22 * RAD2);
|
|
862
1362
|
const r = s.a + (hair >= 0 ? hair * 20 : hair * 55) - lag * 0.8;
|
|
863
1363
|
traitT = `translate(${f2(s.x)} ${f2(s.y + lag * 0.25)}) rotate(${f2(r)})`;
|
|
864
1364
|
shape = n("path", "trait.shape", {
|
|
@@ -868,7 +1368,7 @@ function draw(who, pose, o) {
|
|
|
868
1368
|
} else if (who.trait === "ring") {
|
|
869
1369
|
const b = 35 - hair * 14 - (hair < 0 ? hair * -21 : 0) + lag * 0.9;
|
|
870
1370
|
const w2 = ringWalk(sh);
|
|
871
|
-
const s = seat(sh.crown, 8, Math.max(w2.min, w2.rest + 158 * (b - 35) *
|
|
1371
|
+
const s = seat(sh.crown, 8, Math.max(w2.min, w2.rest + 158 * (b - 35) * RAD2));
|
|
872
1372
|
traitT = `translate(${f2(s.x)} ${f2(s.y)}) scale(${f2(1 + Math.max(0, hair) * 0.1)})`;
|
|
873
1373
|
shape = n("circle", "trait.shape", {
|
|
874
1374
|
cx: 0,
|
|
@@ -880,7 +1380,7 @@ function draw(who, pose, o) {
|
|
|
880
1380
|
});
|
|
881
1381
|
} else if (who.trait === "dot") {
|
|
882
1382
|
const s = clamp(-hair, 0, 1.2);
|
|
883
|
-
const th = Math.min(1, s / 0.35) * 61.05 *
|
|
1383
|
+
const th = Math.min(1, s / 0.35) * 61.05 * RAD2;
|
|
884
1384
|
const travel = Math.max(0, s - 0.35) / 0.65 * 92;
|
|
885
1385
|
const p = seat(sh.crown, 12.5, 26.1 * th + travel);
|
|
886
1386
|
let cy = p.y;
|
|
@@ -1051,7 +1551,7 @@ function draw(who, pose, o) {
|
|
|
1051
1551
|
}, parts));
|
|
1052
1552
|
}
|
|
1053
1553
|
if (who.neckwear === "tie") {
|
|
1054
|
-
const skew = Math.abs(Math.sin(g("mk") *
|
|
1554
|
+
const skew = Math.abs(Math.sin(g("mk") * RAD2)) * mw;
|
|
1055
1555
|
const bottom = Math.max(0, mt * kb, mb * kb) + skew + m.sw * st;
|
|
1056
1556
|
wardrobe.push(n("g", "neckwear", {
|
|
1057
1557
|
transform: `translate(${m.x} ${f2(m.y + g("my") + bottom + 8)})`,
|
|
@@ -1065,134 +1565,10 @@ function draw(who, pose, o) {
|
|
|
1065
1565
|
})
|
|
1066
1566
|
]));
|
|
1067
1567
|
}
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
}, [
|
|
1073
|
-
n("g", "plateA", {
|
|
1074
|
-
transform: off(-0.55, 0.45),
|
|
1075
|
-
filter: riso ? `url(#${id}-pA)` : undefined
|
|
1076
|
-
}, [bodyNode, ...cheeks, trait]),
|
|
1077
|
-
n("g", "plateB", {
|
|
1078
|
-
transform: off(0.6, -0.5),
|
|
1079
|
-
filter: riso ? `url(#${id}-pB)` : undefined
|
|
1080
|
-
}, [...eyes, ...brows, mouth, ...wardrobe])
|
|
1081
|
-
]);
|
|
1082
|
-
const scene = [];
|
|
1083
|
-
if (o.frame !== "none") {
|
|
1084
|
-
const bg = o.frame === "ink" ? INK.ink : o.frame === "bone" ? INK.bone : INK.paper;
|
|
1085
|
-
scene.push(n("rect", "bg", { ...VBr, fill: bg }));
|
|
1086
|
-
const th = clamp(g("thing"), 0, 1);
|
|
1087
|
-
const dots = [];
|
|
1088
|
-
for (let j = 0;j < 3; j++)
|
|
1089
|
-
for (let i = 0;i < 3; i++) {
|
|
1090
|
-
const q = j * 3 + i;
|
|
1091
|
-
const kk = smooth(q / 12, q / 12 + 0.35, th);
|
|
1092
|
-
dots.push(n("circle", `lattice.${q}`, {
|
|
1093
|
-
cx: 312 + i * 18,
|
|
1094
|
-
cy: 2 + j * 18,
|
|
1095
|
-
r: f2(1.2 + 2.2 * kk),
|
|
1096
|
-
opacity: f3(kk * 0.55)
|
|
1097
|
-
}));
|
|
1098
|
-
}
|
|
1099
|
-
scene.push(n("g", "lattice", { fill: o.frame === "ink" ? INK.bone : INK.ink }, dots));
|
|
1100
|
-
}
|
|
1101
|
-
scene.push(av);
|
|
1102
|
-
if (grainy)
|
|
1103
|
-
scene.push(n("rect", "grain", {
|
|
1104
|
-
...VBr,
|
|
1105
|
-
filter: `url(#${id}-grain)`,
|
|
1106
|
-
opacity: o.frame === "ink" ? 0.3 : 0.16,
|
|
1107
|
-
"pointer-events": "none"
|
|
1108
|
-
}));
|
|
1109
|
-
const root = n("svg", "root", {
|
|
1110
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
1111
|
-
viewBox: VB.join(" "),
|
|
1112
|
-
width: o.size,
|
|
1113
|
-
height: o.size,
|
|
1114
|
-
role: o.title === false ? undefined : "img",
|
|
1115
|
-
"aria-label": o.title === false ? undefined : o.title,
|
|
1116
|
-
"aria-hidden": o.title === false ? "true" : undefined
|
|
1117
|
-
}, [
|
|
1118
|
-
n("defs", "defs", {}, defs),
|
|
1119
|
-
n("g", "frame", { "clip-path": `url(#${id}-f)` }, scene)
|
|
1120
|
-
]);
|
|
1121
|
-
return o.live ? root : prune(root) ?? root;
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
// src/identity.ts
|
|
1125
|
-
var STYLE = "peek";
|
|
1126
|
-
var tidy = (s) => s.normalize("NFC").trim().replace(/\s+/g, " ").toLowerCase();
|
|
1127
|
-
function fnv1a(str) {
|
|
1128
|
-
let h = 2166136261;
|
|
1129
|
-
for (const b of new TextEncoder().encode(str)) {
|
|
1130
|
-
h ^= b;
|
|
1131
|
-
h = Math.imul(h, 16777619) >>> 0;
|
|
1132
|
-
}
|
|
1133
|
-
return h >>> 0;
|
|
1134
|
-
}
|
|
1135
|
-
function mulberry32(a) {
|
|
1136
|
-
return () => {
|
|
1137
|
-
a = a + 1831565813 | 0;
|
|
1138
|
-
let t = Math.imul(a ^ a >>> 15, 1 | a);
|
|
1139
|
-
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
1140
|
-
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
1141
|
-
};
|
|
1142
|
-
}
|
|
1143
|
-
var LISTS = {
|
|
1144
|
-
face: Object.keys(FACES),
|
|
1145
|
-
color: Object.keys(COLORS),
|
|
1146
|
-
...PARTS,
|
|
1147
|
-
...ACCESSORIES
|
|
1148
|
-
};
|
|
1149
|
-
var AXES = Object.keys(LISTS);
|
|
1150
|
-
function seed(name, axis, version = LATEST) {
|
|
1151
|
-
return fnv1a(`${STYLE}@${version}:${axis}:${tidy(name)}`);
|
|
1152
|
-
}
|
|
1153
|
-
function identify(name, { version = LATEST } = {}) {
|
|
1154
|
-
const lengths = VERSIONS[version];
|
|
1155
|
-
if (!lengths)
|
|
1156
|
-
throw new Error(`peek@${version} does not exist`);
|
|
1157
|
-
const pick = (axis) => LISTS[axis][seed(name, axis, version) % lengths[axis]];
|
|
1158
|
-
const rnd = mulberry32(seed(name, "persona", version));
|
|
1159
|
-
const r = (a, b) => Math.round((a + rnd() * (b - a)) * 100) / 100;
|
|
1160
|
-
const persona = {
|
|
1161
|
-
spread: r(-7, 7),
|
|
1162
|
-
blink: r(0.75, 1.4),
|
|
1163
|
-
add: {
|
|
1164
|
-
browY: r(-4, 4),
|
|
1165
|
-
browTilt: r(-5, 5),
|
|
1166
|
-
browArch: r(-0.2, 0.3),
|
|
1167
|
-
rot: r(-3, 3),
|
|
1168
|
-
hair: r(-0.2, 0.35),
|
|
1169
|
-
gx: r(-0.12, 0.12),
|
|
1170
|
-
my: r(-2, 3)
|
|
1171
|
-
},
|
|
1172
|
-
mul: {
|
|
1173
|
-
eyeS: r(0.92, 1.08),
|
|
1174
|
-
pupil: r(0.9, 1.12),
|
|
1175
|
-
browW: r(0.85, 1.2),
|
|
1176
|
-
mw: r(0.85, 1.18)
|
|
1177
|
-
}
|
|
1178
|
-
};
|
|
1179
|
-
const key = tidy(name);
|
|
1180
|
-
return {
|
|
1181
|
-
key,
|
|
1182
|
-
hash: fnv1a(key),
|
|
1183
|
-
version,
|
|
1184
|
-
face: pick("face"),
|
|
1185
|
-
color: pick("color"),
|
|
1186
|
-
eyes: pick("eyes"),
|
|
1187
|
-
brows: pick("brows"),
|
|
1188
|
-
mouth: pick("mouth"),
|
|
1189
|
-
cheeks: pick("cheeks"),
|
|
1190
|
-
trait: pick("trait"),
|
|
1191
|
-
eyewear: pick("eyewear"),
|
|
1192
|
-
headwear: pick("headwear"),
|
|
1193
|
-
neckwear: pick("neckwear"),
|
|
1194
|
-
persona
|
|
1195
|
-
};
|
|
1568
|
+
return [
|
|
1569
|
+
[bodyNode, ...cheeks, trait],
|
|
1570
|
+
[...eyes, ...brows, mouth, ...wardrobe]
|
|
1571
|
+
];
|
|
1196
1572
|
}
|
|
1197
1573
|
|
|
1198
1574
|
// src/svg.ts
|
|
@@ -1213,12 +1589,14 @@ function settle(name, o, live = false) {
|
|
|
1213
1589
|
neckwear: wear(o, base, "neckwear")
|
|
1214
1590
|
};
|
|
1215
1591
|
const pose = restPose(who, o.expression ?? "normal", o.gaze);
|
|
1592
|
+
const variant = VARIANTS.includes(o.variant) ? o.variant : "peek";
|
|
1216
1593
|
const look = {
|
|
1217
1594
|
size: o.size ?? 64,
|
|
1218
1595
|
frame: o.frame ?? "ink",
|
|
1219
1596
|
square: o.square ?? true,
|
|
1220
1597
|
riso: o.riso ?? false,
|
|
1221
|
-
live
|
|
1598
|
+
live,
|
|
1599
|
+
...variant !== "peek" && { variant }
|
|
1222
1600
|
};
|
|
1223
1601
|
const { eyewear, headwear, neckwear, ...legacy } = who;
|
|
1224
1602
|
const wardrobe = eyewear !== "none" || headwear !== "none" || neckwear !== "none";
|