@doan-labs/peek 1.0.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/LICENSE +21 -0
- package/README.md +64 -1
- package/dist/bloc.d.ts +2 -0
- package/dist/core.d.ts +9 -0
- package/dist/draw.d.ts +24 -2
- package/dist/identity.d.ts +4 -1
- package/dist/identity.js +22 -2
- package/dist/index.d.ts +1 -7
- package/dist/index.js +719 -142
- 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 +737 -166
- package/dist/tables.d.ts +67 -1
- package/dist/vue.d.ts +17 -0
- package/dist/vue.js +2302 -0
- package/package.json +30 -5
- package/src/bloc.ts +326 -0
- package/src/core.ts +39 -0
- package/src/draw.ts +522 -109
- package/src/identity.ts +11 -0
- package/src/index.ts +1 -26
- package/src/peek.svelte +75 -0
- package/src/peek.svelte.d.ts +5 -0
- package/src/peek.tsx +1 -1
- package/src/svelte-peek.ts +18 -0
- package/src/svelte.ts +3 -0
- package/src/svg.ts +30 -2
- package/src/tables.ts +74 -2
- package/src/vue.ts +133 -0
package/dist/index.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"],
|
|
@@ -121,8 +149,25 @@ var PARTS = {
|
|
|
121
149
|
cheeks: ["oval", "dots", "lines"],
|
|
122
150
|
trait: ["square", "fin", "ring", "dot", "peak"]
|
|
123
151
|
};
|
|
152
|
+
var ACCESSORY_INK = { knot: "#E5412D" };
|
|
153
|
+
var ACCESSORIES = {
|
|
154
|
+
eyewear: ["none", "glasses", "sunglasses"],
|
|
155
|
+
headwear: ["none", "bow", "cap", "sprout"],
|
|
156
|
+
neckwear: ["none", "tie"]
|
|
157
|
+
};
|
|
124
158
|
var VERSIONS = {
|
|
125
|
-
1: {
|
|
159
|
+
1: {
|
|
160
|
+
face: 4,
|
|
161
|
+
color: 7,
|
|
162
|
+
eyes: 3,
|
|
163
|
+
brows: 4,
|
|
164
|
+
mouth: 4,
|
|
165
|
+
cheeks: 3,
|
|
166
|
+
trait: 5,
|
|
167
|
+
eyewear: 1,
|
|
168
|
+
headwear: 1,
|
|
169
|
+
neckwear: 1
|
|
170
|
+
}
|
|
126
171
|
};
|
|
127
172
|
var LATEST = 1;
|
|
128
173
|
var GROUPS = {
|
|
@@ -330,6 +375,240 @@ var EXPRESSIONS = {
|
|
|
330
375
|
})
|
|
331
376
|
};
|
|
332
377
|
|
|
378
|
+
// src/bloc.ts
|
|
379
|
+
var RAD = Math.PI / 180;
|
|
380
|
+
var TRI = [
|
|
381
|
+
[170, 12.8],
|
|
382
|
+
[320, 312.8],
|
|
383
|
+
[20, 312.8]
|
|
384
|
+
];
|
|
385
|
+
var BODY = {
|
|
386
|
+
diamond: FACES.diamond,
|
|
387
|
+
semicircle: FACES.semicircle,
|
|
388
|
+
circle: FACES.circle,
|
|
389
|
+
triangle: {
|
|
390
|
+
body: { d: `M${TRI.map((p) => p.join(" ")).join("L")}Z` },
|
|
391
|
+
crown: { poly: TRI, r: 0 }
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
var strokeFor = (px) => px >= 96 ? 1 : px >= 56 ? 1.25 : px >= 40 ? 1.6 : px >= 30 ? 2 : 2.6;
|
|
395
|
+
var boostFor = (px) => px < 30 ? 1.25 : px < 56 ? 1.12 : 1;
|
|
396
|
+
var turn = (x, y, deg) => {
|
|
397
|
+
const c = Math.cos(deg * RAD);
|
|
398
|
+
const s = Math.sin(deg * RAD);
|
|
399
|
+
return [x * c - y * s, x * s + y * c];
|
|
400
|
+
};
|
|
401
|
+
var poly = (pts) => `M${pts.map(([x, y]) => `${f2(x)} ${f2(y)}`).join("L")}Z`;
|
|
402
|
+
function blocParts({ who, pose, o, defs, g }) {
|
|
403
|
+
const sh = FACES[who.face];
|
|
404
|
+
const { body, crown } = BODY[who.face];
|
|
405
|
+
const id = o.id;
|
|
406
|
+
const P = who.persona;
|
|
407
|
+
const spread = P.spread;
|
|
408
|
+
const st = strokeFor(o.size);
|
|
409
|
+
const up = boostFor(o.size);
|
|
410
|
+
const strong = BLOC[who.color];
|
|
411
|
+
const light = o.frame === "bone" || o.frame === "paper" ? COLORS[who.color].deep : COLORS[who.color].body;
|
|
412
|
+
const lineW = sh.brow.w * st;
|
|
413
|
+
defs.push(n("clipPath", "half", { id: `${id}-r` }, [
|
|
414
|
+
n("rect", "half.shape", { x: 170, y: -30, width: 200, height: 400 })
|
|
415
|
+
]));
|
|
416
|
+
const block = (key, fill, clip) => ("d" in body) ? n("path", key, { d: body.d, fill, "clip-path": clip }) : n("circle", key, {
|
|
417
|
+
cx: body.circle[0],
|
|
418
|
+
cy: body.circle[1],
|
|
419
|
+
r: body.circle[2],
|
|
420
|
+
fill,
|
|
421
|
+
"clip-path": clip
|
|
422
|
+
});
|
|
423
|
+
const eyeS = g("eyeS");
|
|
424
|
+
const r0 = Math.sqrt(sh.rx * sh.ry) * up;
|
|
425
|
+
const lid0 = g("lid");
|
|
426
|
+
const lidAsym = g("lidAsym");
|
|
427
|
+
const lower = clamp(g("lower"), 0, 1);
|
|
428
|
+
const pupil = g("pupil");
|
|
429
|
+
const gx = clamp(g("gx"), -1.1, 1.1);
|
|
430
|
+
const gy = clamp(g("gy"), -1.1, 1.1);
|
|
431
|
+
const tilt = g("lidTilt");
|
|
432
|
+
const bead = who.eyes === "bead";
|
|
433
|
+
const eyes = sh.eyes.map(([ex, ey], i) => {
|
|
434
|
+
const side = i === 0 ? -1 : 1;
|
|
435
|
+
const kk = `eye${i}`;
|
|
436
|
+
let lid = clamp(lid0 + (i === 0 ? lidAsym : -lidAsym * 0.3), 0, 1);
|
|
437
|
+
lid = lid + (1 - lid) * pose.blink;
|
|
438
|
+
const cut = clamp((lid - 0.1) / 0.9, 0, 1);
|
|
439
|
+
const r = bead ? r0 * 0.7 * eyeS * (0.4 + 0.6 * pupil) : r0 * eyeS;
|
|
440
|
+
const [tx, ty] = bead ? [ex + side * spread + gx * 6, ey + gy * 6] : [ex + side * spread, ey];
|
|
441
|
+
const a = side * tilt;
|
|
442
|
+
const edge = -r - 1 + cut * (2 * r + 2);
|
|
443
|
+
const floor = r + 1 - lower * r * 2.2;
|
|
444
|
+
const W = r + 2;
|
|
445
|
+
const lidAt = (x) => x * Math.tan(a * RAD) + edge / Math.cos(a * RAD);
|
|
446
|
+
const [t0, t1] = [lidAt(-W), lidAt(W)];
|
|
447
|
+
defs.push(n("clipPath", `${kk}.clip`, { id: `${id}-e${i}` }, [
|
|
448
|
+
n("path", `${kk}.clip.shape`, {
|
|
449
|
+
d: poly([
|
|
450
|
+
[-W, t0],
|
|
451
|
+
[W, t1],
|
|
452
|
+
[W, Math.max(floor, t1)],
|
|
453
|
+
[-W, Math.max(floor, t0)]
|
|
454
|
+
])
|
|
455
|
+
})
|
|
456
|
+
]));
|
|
457
|
+
const pr = sh.pr * pupil * up;
|
|
458
|
+
const px = gx * Math.max(0, r - pr) * 0.85;
|
|
459
|
+
const py = gy * Math.max(0, r - pr) * 0.75;
|
|
460
|
+
const rim = Math.min(r * 0.27 * Math.min(st, 1.6), r * 0.4);
|
|
461
|
+
const inner = bead ? [n("circle", `${kk}.bead`, { cx: 0, cy: 0, r: f2(r), fill: INK.ink })] : [
|
|
462
|
+
n("circle", `${kk}.white`, {
|
|
463
|
+
cx: 0,
|
|
464
|
+
cy: 0,
|
|
465
|
+
r: f2(r),
|
|
466
|
+
fill: INK.paper
|
|
467
|
+
}),
|
|
468
|
+
n("circle", `${kk}.pupil`, {
|
|
469
|
+
cx: f2(px),
|
|
470
|
+
cy: f2(py),
|
|
471
|
+
r: f2(pr),
|
|
472
|
+
fill: INK.ink
|
|
473
|
+
})
|
|
474
|
+
];
|
|
475
|
+
if (who.eyes === "ring")
|
|
476
|
+
inner.push(n("circle", `${kk}.rim`, {
|
|
477
|
+
cx: 0,
|
|
478
|
+
cy: 0,
|
|
479
|
+
r: f2(r - rim / 2),
|
|
480
|
+
fill: "none",
|
|
481
|
+
stroke: INK.ink,
|
|
482
|
+
"stroke-width": f2(rim)
|
|
483
|
+
}));
|
|
484
|
+
const co = smooth(0.86, 1, lid);
|
|
485
|
+
const yy = Math.min(edge, r) - lineW / 2;
|
|
486
|
+
return n("g", kk, { transform: `translate(${f2(tx)} ${f2(ty)})` }, [
|
|
487
|
+
n("g", `${kk}.inner`, { "clip-path": `url(#${id}-e${i})` }, inner),
|
|
488
|
+
n("path", `${kk}.closed`, {
|
|
489
|
+
d: `M${f2(-r * 0.95)} ${f2(yy)}H${f2(r * 0.95)}`,
|
|
490
|
+
transform: `rotate(${f2(a * 0.6)})`,
|
|
491
|
+
stroke: INK.ink,
|
|
492
|
+
"stroke-width": f2(lineW * 1.4),
|
|
493
|
+
opacity: co > 0.01 ? f2(co) : 0
|
|
494
|
+
})
|
|
495
|
+
]);
|
|
496
|
+
});
|
|
497
|
+
const bt = pose.browTilt;
|
|
498
|
+
const basym = g("browAsym");
|
|
499
|
+
const th = lineW * 2 * g("browW");
|
|
500
|
+
const hw = sh.eyes[1][0] - 170 + spread + Math.sqrt(sh.rx * sh.ry) * P.mul.eyeS * up;
|
|
501
|
+
const gap = smooth(0, 8, Math.abs(bt)) * th * 0.9;
|
|
502
|
+
const slant = P.add.browTilt * 0.3 - Math.atan(1.3 * basym / (2 * hw)) / RAD;
|
|
503
|
+
const y0 = sh.brow.y + g("browY") - (eyeS - 1) * sh.ry * 0.9 - 0.35 * basym;
|
|
504
|
+
const halves = (dy, len2 = 1) => [-1, 1].map((s2) => {
|
|
505
|
+
const [ox, oy] = turn(s2 * gap / 2, 0, slant);
|
|
506
|
+
const x0 = 170 + ox;
|
|
507
|
+
const yy = y0 + dy + oy;
|
|
508
|
+
const ang = slant + s2 * bt;
|
|
509
|
+
const [ux, uy] = turn(s2 * (hw * len2 - gap / 2), 0, ang);
|
|
510
|
+
const [nx, ny] = turn(0, th / 2, ang);
|
|
511
|
+
return [
|
|
512
|
+
[x0 - nx, yy - ny],
|
|
513
|
+
[x0 + ux - nx, yy + uy - ny],
|
|
514
|
+
[x0 + ux + nx, yy + uy + ny],
|
|
515
|
+
[x0 + nx, yy + ny]
|
|
516
|
+
];
|
|
517
|
+
});
|
|
518
|
+
const inside = (dy, len2 = 1) => halves(dy, len2).every((q) => q.every(([x, y]) => depth(crown, x, y) + 2 < 0));
|
|
519
|
+
let lo = 0;
|
|
520
|
+
let hi = 0;
|
|
521
|
+
if (!inside(0)) {
|
|
522
|
+
hi = 60;
|
|
523
|
+
for (let k = 0;k < 12; k++) {
|
|
524
|
+
const mid2 = (lo + hi) / 2;
|
|
525
|
+
if (inside(mid2))
|
|
526
|
+
hi = mid2;
|
|
527
|
+
else
|
|
528
|
+
lo = mid2;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
let len = 1;
|
|
532
|
+
if (!inside(hi)) {
|
|
533
|
+
let short = 0;
|
|
534
|
+
for (let k = 0;k < 12; k++) {
|
|
535
|
+
const mid2 = (short + len) / 2;
|
|
536
|
+
if (inside(hi, mid2))
|
|
537
|
+
short = mid2;
|
|
538
|
+
else
|
|
539
|
+
len = mid2;
|
|
540
|
+
}
|
|
541
|
+
len = short;
|
|
542
|
+
}
|
|
543
|
+
const bars = halves(hi, len).map((q, i) => n("path", `bar${i}`, { d: poly(q), fill: INK.ink }));
|
|
544
|
+
const m = sh.mouth;
|
|
545
|
+
const big = (who.face === "diamond" || who.face === "triangle" ? 1.25 : 1) * up;
|
|
546
|
+
const mw = m.w * Math.max(0.05, g("mw")) * big;
|
|
547
|
+
const mt = g("mt") * m.d * big;
|
|
548
|
+
const mb = g("mb") * m.d * big;
|
|
549
|
+
const mid = (mt + mb) / 2;
|
|
550
|
+
const kb = 4 / 3;
|
|
551
|
+
let d;
|
|
552
|
+
if (who.mouth === "poly") {
|
|
553
|
+
const s2 = Math.max(mw * 0.9, (mb - mt) / 2);
|
|
554
|
+
d = poly([
|
|
555
|
+
[-s2, mid],
|
|
556
|
+
[0, mid - s2],
|
|
557
|
+
[s2, mid],
|
|
558
|
+
[0, mid + s2]
|
|
559
|
+
]);
|
|
560
|
+
} else if (who.mouth === "box") {
|
|
561
|
+
const h = Math.max(mb - mt, lineW * 1.6) / 2;
|
|
562
|
+
const w = mw * 0.85;
|
|
563
|
+
d = poly([
|
|
564
|
+
[-w, mid - h],
|
|
565
|
+
[w, mid - h],
|
|
566
|
+
[w, mid + h],
|
|
567
|
+
[-w, mid + h]
|
|
568
|
+
]);
|
|
569
|
+
} else if (who.mouth === "line") {
|
|
570
|
+
const h = lineW * 1.4 / 2;
|
|
571
|
+
const w = mw * 1.5;
|
|
572
|
+
const y = mid * 0.5;
|
|
573
|
+
d = poly([
|
|
574
|
+
[-w, y - h],
|
|
575
|
+
[w, y - h],
|
|
576
|
+
[w, y + h],
|
|
577
|
+
[-w, y + h]
|
|
578
|
+
]);
|
|
579
|
+
} else {
|
|
580
|
+
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`;
|
|
581
|
+
}
|
|
582
|
+
const mouth = n("path", "mouth", {
|
|
583
|
+
d,
|
|
584
|
+
transform: `translate(${f2(m.x + g("mx"))} ${f2(m.y + g("my"))}) rotate(${f2(g("mk"))})`,
|
|
585
|
+
fill: INK.ink
|
|
586
|
+
});
|
|
587
|
+
const hair = g("hair");
|
|
588
|
+
const s = seat(crown, Math.max(0, hair) * 8, 110 + Math.max(0, -hair) * 40);
|
|
589
|
+
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", {
|
|
590
|
+
cx: 0,
|
|
591
|
+
cy: 0,
|
|
592
|
+
r: 17.5,
|
|
593
|
+
fill: "none",
|
|
594
|
+
stroke: strong,
|
|
595
|
+
"stroke-width": 13
|
|
596
|
+
}) : who.trait === "fin" ? n("path", "trait.shape", { d: "M-27 27V-27A54 54 0 0 1 27 27Z" }) : n("rect", "trait.shape", {
|
|
597
|
+
x: -16,
|
|
598
|
+
y: -30,
|
|
599
|
+
width: 32,
|
|
600
|
+
height: 60
|
|
601
|
+
});
|
|
602
|
+
const trait = n("g", "trait", {
|
|
603
|
+
transform: `translate(${f2(s.x)} ${f2(s.y + pose.lag)}) scale(${f2(up)})`,
|
|
604
|
+
fill: strong
|
|
605
|
+
}, [shape]);
|
|
606
|
+
return [
|
|
607
|
+
[block("body", strong), block("body.light", light, `url(#${id}-r)`), trait],
|
|
608
|
+
[...eyes, ...bars, mouth]
|
|
609
|
+
];
|
|
610
|
+
}
|
|
611
|
+
|
|
333
612
|
// src/identity.ts
|
|
334
613
|
var STYLE = "peek";
|
|
335
614
|
var tidy = (s) => s.normalize("NFC").trim().replace(/\s+/g, " ").toLowerCase();
|
|
@@ -352,7 +631,8 @@ function mulberry32(a) {
|
|
|
352
631
|
var LISTS = {
|
|
353
632
|
face: Object.keys(FACES),
|
|
354
633
|
color: Object.keys(COLORS),
|
|
355
|
-
...PARTS
|
|
634
|
+
...PARTS,
|
|
635
|
+
...ACCESSORIES
|
|
356
636
|
};
|
|
357
637
|
var AXES = Object.keys(LISTS);
|
|
358
638
|
function seed(name, axis, version = LATEST) {
|
|
@@ -396,17 +676,12 @@ function identify(name, { version = LATEST } = {}) {
|
|
|
396
676
|
mouth: pick("mouth"),
|
|
397
677
|
cheeks: pick("cheeks"),
|
|
398
678
|
trait: pick("trait"),
|
|
679
|
+
eyewear: pick("eyewear"),
|
|
680
|
+
headwear: pick("headwear"),
|
|
681
|
+
neckwear: pick("neckwear"),
|
|
399
682
|
persona
|
|
400
683
|
};
|
|
401
684
|
}
|
|
402
|
-
// src/peek.tsx
|
|
403
|
-
import {
|
|
404
|
-
createElement,
|
|
405
|
-
useEffect,
|
|
406
|
-
useId,
|
|
407
|
-
useRef,
|
|
408
|
-
useState
|
|
409
|
-
} from "react";
|
|
410
685
|
|
|
411
686
|
// src/draw.ts
|
|
412
687
|
var VB = [-30, -30, 400, 400];
|
|
@@ -419,8 +694,11 @@ var smooth = (a, b, v) => {
|
|
|
419
694
|
};
|
|
420
695
|
var f2 = (v) => String(Math.round(v * 100) / 100);
|
|
421
696
|
var f3 = (v) => String(Math.round(v * 1000) / 1000);
|
|
422
|
-
var
|
|
423
|
-
var
|
|
697
|
+
var strokeFor2 = (px) => px >= 96 ? 1 : px >= 56 ? 1.25 : px >= 40 ? 1.6 : px >= 30 ? 2 : 2.4;
|
|
698
|
+
var RAD2 = Math.PI / 180;
|
|
699
|
+
var STEM = "M0 0Q3 -19 0 -36";
|
|
700
|
+
var LEAF0 = "M0 -33C-24 -30 -38 -52 -25 -53C-12 -54 -2 -43 0 -33Z";
|
|
701
|
+
var LEAF1 = "M0 -33C5 -54 28 -61 27 -48C26 -36 11 -31 0 -33Z";
|
|
424
702
|
function frameOf(face) {
|
|
425
703
|
const baseY = FLOOR + face.sink - face.bottom;
|
|
426
704
|
const eyeY = face.eyes[0][1];
|
|
@@ -449,18 +727,18 @@ function seat(crown, d, u) {
|
|
|
449
727
|
const at = ([x, y], b2) => ({
|
|
450
728
|
x: x + R * Math.sin(b2),
|
|
451
729
|
y: y - R * Math.cos(b2),
|
|
452
|
-
a: b2 /
|
|
730
|
+
a: b2 / RAD2
|
|
453
731
|
});
|
|
454
732
|
const P = crown.poly;
|
|
455
733
|
if (P.length === 1)
|
|
456
734
|
return at(P[0], R ? u / R : 0);
|
|
457
|
-
const
|
|
735
|
+
const n2 = P.length;
|
|
458
736
|
const dir = u >= 0 ? 1 : -1;
|
|
459
737
|
let left = Math.abs(u);
|
|
460
738
|
let i = 0;
|
|
461
739
|
let b = 0;
|
|
462
|
-
for (let step = 0;step <
|
|
463
|
-
const j = (i + dir +
|
|
740
|
+
for (let step = 0;step < n2; step++) {
|
|
741
|
+
const j = (i + dir + n2) % n2;
|
|
464
742
|
const [x0, y0] = P[i];
|
|
465
743
|
const [x1, y1] = P[j];
|
|
466
744
|
const ex = x1 - x0;
|
|
@@ -480,7 +758,7 @@ function seat(crown, d, u) {
|
|
|
480
758
|
return {
|
|
481
759
|
x: x0 + ex / L * left + R * nx,
|
|
482
760
|
y: y0 + ey / L * left + R * ny,
|
|
483
|
-
a: be /
|
|
761
|
+
a: be / RAD2
|
|
484
762
|
};
|
|
485
763
|
left -= L;
|
|
486
764
|
i = j;
|
|
@@ -507,7 +785,7 @@ function depth(crown, x, y) {
|
|
|
507
785
|
}
|
|
508
786
|
return (out <= 0 ? out : near) - crown.r;
|
|
509
787
|
}
|
|
510
|
-
var RING_U = 158 * 35 *
|
|
788
|
+
var RING_U = 158 * 35 * RAD2;
|
|
511
789
|
function ringWalk(sh) {
|
|
512
790
|
const ex = sh.eyes[1][0] + sh.brow.hw + 7;
|
|
513
791
|
const top = sh.brow.y - 30;
|
|
@@ -541,14 +819,11 @@ function prune(node) {
|
|
|
541
819
|
}
|
|
542
820
|
function draw(who, pose, o) {
|
|
543
821
|
const sh = FACES[who.face];
|
|
544
|
-
const { body: fill, deep } = COLORS[who.color];
|
|
545
822
|
const P = who.persona;
|
|
546
823
|
const mul = P.mul;
|
|
547
824
|
const add = P.add;
|
|
548
825
|
const g = (c) => pose[c] * (mul[c] ?? 1) + (add[c] ?? 0);
|
|
549
|
-
const spread = P.spread;
|
|
550
826
|
const id = o.id;
|
|
551
|
-
const st = strokeFor(o.size);
|
|
552
827
|
const { baseY, R } = frameOf(sh);
|
|
553
828
|
const defs = [];
|
|
554
829
|
const VBr = { x: VB[0], y: VB[1], width: VB[2], height: VB[3] };
|
|
@@ -642,7 +917,165 @@ function draw(who, pose, o) {
|
|
|
642
917
|
const B = sh.bottom;
|
|
643
918
|
const ty = baseY - alt * R;
|
|
644
919
|
const sc = (v) => Math.round(v * 1e5) / 1e5;
|
|
645
|
-
const
|
|
920
|
+
const [front, back] = (o.variant === "bloc" ? blocParts : peekParts)({
|
|
921
|
+
who,
|
|
922
|
+
pose,
|
|
923
|
+
o,
|
|
924
|
+
defs,
|
|
925
|
+
g
|
|
926
|
+
});
|
|
927
|
+
const off = (dx, dy) => riso ? `translate(${f2(dx * k * upp)} ${f2(dy * k * upp)})` : undefined;
|
|
928
|
+
const av = n("g", "av", {
|
|
929
|
+
transform: `translate(${f2(x)} ${f2(ty)}) rotate(${f2(rot)} 170 ${B}) translate(170 ${B}) scale(${sc(sx)} ${sc(sy)}) translate(-170 ${-B})`,
|
|
930
|
+
filter: dim > 0.01 ? `url(#${id}-dim)` : undefined
|
|
931
|
+
}, [
|
|
932
|
+
n("g", "plateA", {
|
|
933
|
+
transform: off(-0.55, 0.45),
|
|
934
|
+
filter: riso ? `url(#${id}-pA)` : undefined
|
|
935
|
+
}, front),
|
|
936
|
+
n("g", "plateB", {
|
|
937
|
+
transform: off(0.6, -0.5),
|
|
938
|
+
filter: riso ? `url(#${id}-pB)` : undefined
|
|
939
|
+
}, back)
|
|
940
|
+
]);
|
|
941
|
+
const scene = [];
|
|
942
|
+
if (o.frame !== "none") {
|
|
943
|
+
const bg = o.frame === "ink" ? INK.ink : o.frame === "bone" ? INK.bone : INK.paper;
|
|
944
|
+
scene.push(n("rect", "bg", { ...VBr, fill: bg }));
|
|
945
|
+
const th = clamp(g("thing"), 0, 1);
|
|
946
|
+
const dots = [];
|
|
947
|
+
for (let j = 0;j < 3; j++)
|
|
948
|
+
for (let i = 0;i < 3; i++) {
|
|
949
|
+
const q = j * 3 + i;
|
|
950
|
+
const kk = smooth(q / 12, q / 12 + 0.35, th);
|
|
951
|
+
dots.push(n("circle", `lattice.${q}`, {
|
|
952
|
+
cx: 312 + i * 18,
|
|
953
|
+
cy: 2 + j * 18,
|
|
954
|
+
r: f2(1.2 + 2.2 * kk),
|
|
955
|
+
opacity: f3(kk * 0.55)
|
|
956
|
+
}));
|
|
957
|
+
}
|
|
958
|
+
scene.push(n("g", "lattice", { fill: o.frame === "ink" ? INK.bone : INK.ink }, dots));
|
|
959
|
+
}
|
|
960
|
+
scene.push(av);
|
|
961
|
+
if (grainy)
|
|
962
|
+
scene.push(n("rect", "grain", {
|
|
963
|
+
...VBr,
|
|
964
|
+
filter: `url(#${id}-grain)`,
|
|
965
|
+
opacity: o.frame === "ink" ? 0.3 : 0.16,
|
|
966
|
+
"pointer-events": "none"
|
|
967
|
+
}));
|
|
968
|
+
const root = n("svg", "root", {
|
|
969
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
970
|
+
viewBox: VB.join(" "),
|
|
971
|
+
width: o.size,
|
|
972
|
+
height: o.size,
|
|
973
|
+
role: o.title === false ? undefined : "img",
|
|
974
|
+
"aria-label": o.title === false ? undefined : o.title,
|
|
975
|
+
"aria-hidden": o.title === false ? "true" : undefined
|
|
976
|
+
}, [
|
|
977
|
+
n("defs", "defs", {}, defs),
|
|
978
|
+
n("g", "frame", { "clip-path": `url(#${id}-f)` }, scene)
|
|
979
|
+
]);
|
|
980
|
+
return o.live ? root : prune(root) ?? root;
|
|
981
|
+
}
|
|
982
|
+
var HAZE_LEVEL = {
|
|
983
|
+
medium: {
|
|
984
|
+
deep: [0.502, 0.224],
|
|
985
|
+
glow: [0.749, 0.337],
|
|
986
|
+
tint: [0.4, 0.5],
|
|
987
|
+
tintStops: [
|
|
988
|
+
[0, 0.878],
|
|
989
|
+
[0.15, 0.878],
|
|
990
|
+
[0.618, 0.396]
|
|
991
|
+
]
|
|
992
|
+
},
|
|
993
|
+
light: {
|
|
994
|
+
deep: [0.298, 0.133],
|
|
995
|
+
glow: [0.549, 0.247],
|
|
996
|
+
tint: [0.31, 0.387],
|
|
997
|
+
tintStops: [
|
|
998
|
+
[0, 0.549],
|
|
999
|
+
[0.55, 0.247]
|
|
1000
|
+
]
|
|
1001
|
+
}
|
|
1002
|
+
};
|
|
1003
|
+
function hazePaint(who, o) {
|
|
1004
|
+
const sh = FACES[who.face];
|
|
1005
|
+
const id = o.id;
|
|
1006
|
+
const L = HAZE_LEVEL[o.size < 32 ? "light" : "medium"];
|
|
1007
|
+
const xs = sh.crown.poly.map((p) => p[0]);
|
|
1008
|
+
const ys = sh.crown.poly.map((p) => p[1]);
|
|
1009
|
+
const x0 = Math.min(...xs) - sh.crown.r;
|
|
1010
|
+
const y0 = Math.min(...ys) - sh.crown.r;
|
|
1011
|
+
const W = Math.max(...xs) + sh.crown.r - x0;
|
|
1012
|
+
const H = sh.bottom - y0;
|
|
1013
|
+
const rnd = mulberry32(fnv1a(`haze:${who.key ?? ""}`));
|
|
1014
|
+
const warm = rnd() < 0.5;
|
|
1015
|
+
const left = rnd() < 0.5;
|
|
1016
|
+
const t = rnd();
|
|
1017
|
+
const tintX = 0.04 + 0.174 * t;
|
|
1018
|
+
const tintY = 0.649 + 0.202 * t;
|
|
1019
|
+
const glowX = 0.38 - 0.12 * rnd();
|
|
1020
|
+
const glowY = 0.12 + 0.099 * rnd();
|
|
1021
|
+
const deepX = 0.251 - 0.151 * rnd();
|
|
1022
|
+
const tintA = 90 - 180 * rnd();
|
|
1023
|
+
const at = (inset, onLeft) => x0 + W * (onLeft ? inset : 1 - inset);
|
|
1024
|
+
const i = WHEEL.indexOf(who.color);
|
|
1025
|
+
const near = WHEEL[(i + (warm ? WHEEL.length - 1 : 1)) % WHEEL.length];
|
|
1026
|
+
const { body, deep } = COLORS[who.color];
|
|
1027
|
+
const blob = (k, color, [cx, cy, a, rx, ry], stops) => n("radialGradient", `haze.${k}`, {
|
|
1028
|
+
id: `${id}-h${k[0]}`,
|
|
1029
|
+
gradientUnits: "userSpaceOnUse",
|
|
1030
|
+
cx: 0,
|
|
1031
|
+
cy: 0,
|
|
1032
|
+
r: 1,
|
|
1033
|
+
gradientTransform: `translate(${f2(cx)} ${f2(cy)}) rotate(${f2(a)}) scale(${f2(rx)} ${f2(ry)})`
|
|
1034
|
+
}, [...stops, [1, 0]].map(([at2, op], j) => n("stop", `haze.${k}.${j}`, {
|
|
1035
|
+
offset: at2,
|
|
1036
|
+
"stop-color": color,
|
|
1037
|
+
"stop-opacity": op
|
|
1038
|
+
})));
|
|
1039
|
+
const fade = ([a, b]) => [
|
|
1040
|
+
[0, a],
|
|
1041
|
+
[0.55, b]
|
|
1042
|
+
];
|
|
1043
|
+
const blobs = [
|
|
1044
|
+
blob("deep", deep, [at(deepX, !left), y0 + H * 0.95, 0, W * 0.5, W * 0.35], fade(L.deep)),
|
|
1045
|
+
blob("tint", HAZE[near].tint, [at(tintX, left), y0 + H * tintY, tintA, W * L.tint[0], W * L.tint[1]], L.tintStops),
|
|
1046
|
+
blob("glow", HAZE[who.color].glow, [at(glowX, !left), y0 + H * glowY, 0, W * 0.475, W * 0.356], fade(L.glow))
|
|
1047
|
+
];
|
|
1048
|
+
const shape = (key, paint) => ("d" in sh.body) ? n("path", key, {
|
|
1049
|
+
d: sh.body.d,
|
|
1050
|
+
fill: paint,
|
|
1051
|
+
stroke: sh.body.stroke ? paint : undefined,
|
|
1052
|
+
"stroke-width": sh.body.stroke,
|
|
1053
|
+
"stroke-linejoin": sh.body.stroke ? "round" : undefined
|
|
1054
|
+
}) : n("circle", key, {
|
|
1055
|
+
cx: sh.body.circle[0],
|
|
1056
|
+
cy: sh.body.circle[1],
|
|
1057
|
+
r: sh.body.circle[2],
|
|
1058
|
+
fill: paint
|
|
1059
|
+
});
|
|
1060
|
+
return [
|
|
1061
|
+
...blobs,
|
|
1062
|
+
n("g", "haze", { id: `${id}-hz` }, [
|
|
1063
|
+
shape("haze.body", body),
|
|
1064
|
+
...["deep", "tint", "glow"].map((k) => shape(`haze.${k}.fill`, `url(#${id}-h${k[0]})`))
|
|
1065
|
+
])
|
|
1066
|
+
];
|
|
1067
|
+
}
|
|
1068
|
+
function peekParts({ who, pose, o, defs, g }) {
|
|
1069
|
+
const sh = FACES[who.face];
|
|
1070
|
+
const { body: fill, deep } = COLORS[who.color];
|
|
1071
|
+
const P = who.persona;
|
|
1072
|
+
const spread = P.spread;
|
|
1073
|
+
const id = o.id;
|
|
1074
|
+
const st = strokeFor2(o.size);
|
|
1075
|
+
const haze = o.variant === "haze";
|
|
1076
|
+
if (haze)
|
|
1077
|
+
defs.push(...hazePaint(who, o));
|
|
1078
|
+
const bodyNode = haze ? n("use", "body", { href: `#${id}-hz` }) : ("d" in sh.body) ? n("path", "body", {
|
|
646
1079
|
d: sh.body.d,
|
|
647
1080
|
fill,
|
|
648
1081
|
stroke: sh.body.stroke ? fill : undefined,
|
|
@@ -730,25 +1163,36 @@ function draw(who, pose, o) {
|
|
|
730
1163
|
const lrx = rx * 1.55;
|
|
731
1164
|
const lry = ry * 1.15;
|
|
732
1165
|
const top = ry + 1.6 - lower * ry * 1.5;
|
|
1166
|
+
const [etx, ety] = bead ? [ex + side * spread + gx * 6, ey + gy * 6] : [ex + side * spread, ey];
|
|
1167
|
+
const lids = [
|
|
1168
|
+
n("rect", `${kk}.lid`, {
|
|
1169
|
+
x: f2(-rx * 2.2),
|
|
1170
|
+
y: f2(-ry * 3),
|
|
1171
|
+
width: f2(rx * 4.4),
|
|
1172
|
+
height: f2(Math.max(0, edge + ry * 3)),
|
|
1173
|
+
transform: `rotate(${f2(a)})`,
|
|
1174
|
+
fill
|
|
1175
|
+
}),
|
|
1176
|
+
n("ellipse", `${kk}.lower`, {
|
|
1177
|
+
cx: 0,
|
|
1178
|
+
cy: f2(top + lry),
|
|
1179
|
+
rx: f2(lrx),
|
|
1180
|
+
ry: f2(lry),
|
|
1181
|
+
fill
|
|
1182
|
+
})
|
|
1183
|
+
];
|
|
1184
|
+
if (haze)
|
|
1185
|
+
defs.push(n("clipPath", `${kk}.lidshape`, { id: `${id}-s${i}` }, lids));
|
|
733
1186
|
const parts = [
|
|
734
1187
|
n("g", `${kk}.inner`, { "clip-path": `url(#${id}-e${i})` }, inner),
|
|
735
|
-
n("g", `${kk}.lids`, { "clip-path": `url(#${id}-l${i})` }, [
|
|
736
|
-
n("
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
}),
|
|
744
|
-
n("ellipse", `${kk}.lower`, {
|
|
745
|
-
cx: 0,
|
|
746
|
-
cy: f2(top + lry),
|
|
747
|
-
rx: f2(lrx),
|
|
748
|
-
ry: f2(lry),
|
|
749
|
-
fill
|
|
750
|
-
})
|
|
751
|
-
])
|
|
1188
|
+
n("g", `${kk}.lids`, { "clip-path": `url(#${id}-l${i})` }, haze ? [
|
|
1189
|
+
n("g", `${kk}.lidpaint`, { "clip-path": `url(#${id}-s${i})` }, [
|
|
1190
|
+
n("use", `${kk}.lidbody`, {
|
|
1191
|
+
href: `#${id}-hz`,
|
|
1192
|
+
transform: `translate(${f2(-etx)} ${f2(-ety)})`
|
|
1193
|
+
})
|
|
1194
|
+
])
|
|
1195
|
+
] : lids)
|
|
752
1196
|
];
|
|
753
1197
|
if (who.eyes === "ring")
|
|
754
1198
|
parts.push(n("g", `${kk}.edges`, {
|
|
@@ -782,9 +1226,7 @@ function draw(who, pose, o) {
|
|
|
782
1226
|
"stroke-width": f2(sw * 0.85),
|
|
783
1227
|
opacity: co > 0.01 ? f2(co) : 0
|
|
784
1228
|
}));
|
|
785
|
-
return n("g", kk, {
|
|
786
|
-
transform: bead ? `translate(${f2(ex + side * spread + gx * 6)} ${f2(ey + gy * 6)})` : `translate(${f2(ex + side * spread)} ${ey})`
|
|
787
|
-
}, parts);
|
|
1229
|
+
return n("g", kk, { transform: `translate(${f2(etx)} ${f2(ety)})` }, parts);
|
|
788
1230
|
});
|
|
789
1231
|
const browY = g("browY") - (eyeS - 1) * sh.ry * 0.9;
|
|
790
1232
|
const btilt = g("browTilt");
|
|
@@ -795,7 +1237,7 @@ function draw(who, pose, o) {
|
|
|
795
1237
|
const side = i === 0 ? -1 : 1;
|
|
796
1238
|
const hw = sh.brow.hw;
|
|
797
1239
|
const c = -sh.brow.arch * arch;
|
|
798
|
-
const [
|
|
1240
|
+
const [w2, ca, k2] = who.brows === "bar" ? [hw * 0.9, c * 0.3, 1.75] : who.brows === "dash" ? [hw * 0.42, c * 0.25, 1.9] : [hw, c, 1];
|
|
799
1241
|
const ti = bw * 1.7;
|
|
800
1242
|
const to = bw * 0.45;
|
|
801
1243
|
const [tl, tr] = side < 0 ? [to, ti] : [ti, to];
|
|
@@ -804,11 +1246,11 @@ function draw(who, pose, o) {
|
|
|
804
1246
|
const half = (t) => wedge ? hl + (hr - hl) * t : bw * k2 / 2;
|
|
805
1247
|
const bx = ex + side * spread;
|
|
806
1248
|
const by = sh.brow.y + browY + (i === 1 ? -basym : basym * 0.3);
|
|
807
|
-
const cos = Math.cos(side * btilt *
|
|
808
|
-
const sin = Math.sin(side * btilt *
|
|
1249
|
+
const cos = Math.cos(side * btilt * RAD2);
|
|
1250
|
+
const sin = Math.sin(side * btilt * RAD2);
|
|
809
1251
|
const inside = (dy) => {
|
|
810
1252
|
for (let t = 0;t <= 1; t += 0.25) {
|
|
811
|
-
const lx =
|
|
1253
|
+
const lx = w2 * (2 * t - 1);
|
|
812
1254
|
const ly = 2 * t * (1 - t) * ca;
|
|
813
1255
|
const px = bx + lx * cos - ly * sin;
|
|
814
1256
|
const py = by + dy + lx * sin + ly * cos;
|
|
@@ -821,7 +1263,7 @@ function draw(who, pose, o) {
|
|
|
821
1263
|
let hi = 0;
|
|
822
1264
|
if (!inside(0)) {
|
|
823
1265
|
hi = 40;
|
|
824
|
-
for (let
|
|
1266
|
+
for (let k = 0;k < 12; k++) {
|
|
825
1267
|
const mid2 = (lo + hi) / 2;
|
|
826
1268
|
if (inside(mid2))
|
|
827
1269
|
hi = mid2;
|
|
@@ -843,7 +1285,7 @@ function draw(who, pose, o) {
|
|
|
843
1285
|
});
|
|
844
1286
|
}
|
|
845
1287
|
return n("path", key, {
|
|
846
|
-
d: `M${f2(-
|
|
1288
|
+
d: `M${f2(-w2)} 0Q0 ${f2(ca)} ${f2(w2)} 0`,
|
|
847
1289
|
transform,
|
|
848
1290
|
fill: "none",
|
|
849
1291
|
stroke: INK.ink,
|
|
@@ -924,7 +1366,7 @@ function draw(who, pose, o) {
|
|
|
924
1366
|
fill: deep
|
|
925
1367
|
});
|
|
926
1368
|
} else if (who.trait === "fin") {
|
|
927
|
-
const s = seat(sh.crown, -6, -164 * 22 *
|
|
1369
|
+
const s = seat(sh.crown, -6, -164 * 22 * RAD2);
|
|
928
1370
|
const r = s.a + (hair >= 0 ? hair * 20 : hair * 55) - lag * 0.8;
|
|
929
1371
|
traitT = `translate(${f2(s.x)} ${f2(s.y + lag * 0.25)}) rotate(${f2(r)})`;
|
|
930
1372
|
shape = n("path", "trait.shape", {
|
|
@@ -933,8 +1375,8 @@ function draw(who, pose, o) {
|
|
|
933
1375
|
});
|
|
934
1376
|
} else if (who.trait === "ring") {
|
|
935
1377
|
const b = 35 - hair * 14 - (hair < 0 ? hair * -21 : 0) + lag * 0.9;
|
|
936
|
-
const
|
|
937
|
-
const s = seat(sh.crown, 8, Math.max(
|
|
1378
|
+
const w2 = ringWalk(sh);
|
|
1379
|
+
const s = seat(sh.crown, 8, Math.max(w2.min, w2.rest + 158 * (b - 35) * RAD2));
|
|
938
1380
|
traitT = `translate(${f2(s.x)} ${f2(s.y)}) scale(${f2(1 + Math.max(0, hair) * 0.1)})`;
|
|
939
1381
|
shape = n("circle", "trait.shape", {
|
|
940
1382
|
cx: 0,
|
|
@@ -946,7 +1388,7 @@ function draw(who, pose, o) {
|
|
|
946
1388
|
});
|
|
947
1389
|
} else if (who.trait === "dot") {
|
|
948
1390
|
const s = clamp(-hair, 0, 1.2);
|
|
949
|
-
const th = Math.min(1, s / 0.35) * 61.05 *
|
|
1391
|
+
const th = Math.min(1, s / 0.35) * 61.05 * RAD2;
|
|
950
1392
|
const travel = Math.max(0, s - 0.35) / 0.65 * 92;
|
|
951
1393
|
const p = seat(sh.crown, 12.5, 26.1 * th + travel);
|
|
952
1394
|
let cy = p.y;
|
|
@@ -968,61 +1410,228 @@ function draw(who, pose, o) {
|
|
|
968
1410
|
});
|
|
969
1411
|
}
|
|
970
1412
|
const trait = n("g", "trait", { transform: traitT }, [shape]);
|
|
971
|
-
const
|
|
972
|
-
const
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
const
|
|
988
|
-
|
|
989
|
-
const
|
|
990
|
-
const
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1413
|
+
const wardrobe = [];
|
|
1414
|
+
const w = (px) => f2(px * Math.min(st, 1.6));
|
|
1415
|
+
const line = {
|
|
1416
|
+
fill: "none",
|
|
1417
|
+
stroke: INK.ink,
|
|
1418
|
+
"stroke-width": w(5.25),
|
|
1419
|
+
"stroke-linecap": "round",
|
|
1420
|
+
"stroke-linejoin": "round"
|
|
1421
|
+
};
|
|
1422
|
+
if (who.eyewear !== "none") {
|
|
1423
|
+
const lx = sh.eyes[0][0] - spread;
|
|
1424
|
+
const rr = sh.eyes[1][0] + spread;
|
|
1425
|
+
const ey = sh.eyes[0][1];
|
|
1426
|
+
const gap = rr - lx;
|
|
1427
|
+
const erx = Math.min(rx + 9, (gap - 15) / 2);
|
|
1428
|
+
const ery = ry + 9;
|
|
1429
|
+
const glasses = who.eyewear === "glasses";
|
|
1430
|
+
const lensX = glasses ? erx : Math.min(rx + 11, (gap - 12) / 2);
|
|
1431
|
+
const top = glasses ? ey - 4 : ey - ry - 2;
|
|
1432
|
+
const frames = [
|
|
1433
|
+
n("path", "eyewear.bridge", {
|
|
1434
|
+
...line,
|
|
1435
|
+
d: `M${f2(lx + lensX)} ${f2(top)}Q${f2((lx + rr) / 2)} ${f2(top - (glasses ? 13 : 0))} ${f2(rr - lensX)} ${f2(top)}`
|
|
1436
|
+
})
|
|
1437
|
+
];
|
|
1438
|
+
for (const [i, cx] of [lx, rr].entries()) {
|
|
1439
|
+
const key = `eyewear.lens${i}`;
|
|
1440
|
+
frames.push(n("path", `${key}.arm`, {
|
|
1441
|
+
...line,
|
|
1442
|
+
d: i === 0 ? `M${f2(cx - lensX - 9)} ${f2(top + 2)}L${f2(cx - lensX)} ${f2(top)}` : `M${f2(cx + lensX)} ${f2(top)}L${f2(cx + lensX + 9)} ${f2(top + 2)}`
|
|
1443
|
+
}));
|
|
1444
|
+
if (glasses) {
|
|
1445
|
+
frames.push(n("ellipse", key, {
|
|
1446
|
+
...line,
|
|
1447
|
+
cx: f2(cx),
|
|
1448
|
+
cy: ey,
|
|
1449
|
+
rx: f2(erx),
|
|
1450
|
+
ry: f2(ery)
|
|
1451
|
+
}));
|
|
1452
|
+
frames.push(n("path", `${key}.glint`, {
|
|
1453
|
+
...line,
|
|
1454
|
+
stroke: INK.paper,
|
|
1455
|
+
"stroke-width": w(3),
|
|
1456
|
+
d: `M${f2(cx - erx * 0.65)} ${f2(ey - ery * 0.45)}Q${f2(cx - erx * 0.5)} ${f2(ey - ery * 0.8)} ${f2(cx - erx * 0.2)} ${f2(ey - ery * 0.86)}`
|
|
1457
|
+
}));
|
|
1458
|
+
} else {
|
|
1459
|
+
const h = ry * 2 + 9;
|
|
1460
|
+
frames.push(n("path", key, {
|
|
1461
|
+
d: `M${f2(cx - lensX)} ${f2(top)}H${f2(cx + lensX)}V${f2(top + h * 0.55)}Q${f2(cx + lensX)} ${f2(top + h)} ${f2(cx)} ${f2(top + h)}Q${f2(cx - lensX)} ${f2(top + h)} ${f2(cx - lensX)} ${f2(top + h * 0.55)}Z`,
|
|
1462
|
+
fill: INK.ink,
|
|
1463
|
+
stroke: INK.ink,
|
|
1464
|
+
"stroke-width": w(3),
|
|
1465
|
+
"stroke-linejoin": "round"
|
|
1466
|
+
}));
|
|
1467
|
+
frames.push(n("path", `${key}.glint`, {
|
|
1468
|
+
...line,
|
|
1469
|
+
stroke: INK.paper,
|
|
1470
|
+
"stroke-width": w(4),
|
|
1471
|
+
d: `M${f2(cx - lensX * 0.58)} ${f2(top + 19)}l8 -8m-1 15 6 -6`
|
|
1472
|
+
}), n("circle", `${key}.pin`, {
|
|
1473
|
+
cx: f2(cx + lensX * 0.55),
|
|
1474
|
+
cy: f2(top + h - 9),
|
|
1475
|
+
r: 1.7,
|
|
1476
|
+
fill: INK.paper
|
|
1000
1477
|
}));
|
|
1001
1478
|
}
|
|
1002
|
-
|
|
1479
|
+
}
|
|
1480
|
+
wardrobe.push(n("g", "eyewear", {}, frames));
|
|
1003
1481
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1482
|
+
if (who.headwear !== "none") {
|
|
1483
|
+
const side = who.trait === "ring" || who.trait === "dot" ? -1 : 1;
|
|
1484
|
+
const cap = who.headwear === "cap";
|
|
1485
|
+
const sprout = who.headwear === "sprout";
|
|
1486
|
+
const inset = sprout ? -6 : cap ? -8 : 0;
|
|
1487
|
+
const offset = sprout ? 34 : 40;
|
|
1488
|
+
let lo = 0;
|
|
1489
|
+
let hi = 160;
|
|
1490
|
+
for (let i = 0;i < 16; i++) {
|
|
1491
|
+
const mid2 = (lo + hi) / 2;
|
|
1492
|
+
const p = seat(sh.crown, inset, side * mid2);
|
|
1493
|
+
if (side * (p.x - 170) < offset)
|
|
1494
|
+
lo = mid2;
|
|
1495
|
+
else
|
|
1496
|
+
hi = mid2;
|
|
1497
|
+
}
|
|
1498
|
+
const anchor = seat(sh.crown, inset, side * (lo + hi) / 2);
|
|
1499
|
+
const angle = clamp(anchor.a, -40, 40);
|
|
1500
|
+
const parts = [];
|
|
1501
|
+
if (cap) {
|
|
1502
|
+
parts.push(n("path", "headwear.crown", {
|
|
1503
|
+
...line,
|
|
1504
|
+
fill: INK.paper,
|
|
1505
|
+
d: "M-33 0C-35 -20 -20 -39 0 -39C20 -39 35 -20 33 0Z"
|
|
1506
|
+
}), n("path", "headwear.seam", {
|
|
1507
|
+
...line,
|
|
1508
|
+
"stroke-width": w(3.5),
|
|
1509
|
+
d: "M-4 -38Q9 -24 8 -1"
|
|
1510
|
+
}), n("path", "headwear.brim", {
|
|
1511
|
+
...line,
|
|
1512
|
+
"stroke-width": w(7),
|
|
1513
|
+
d: side > 0 ? "M-35 1Q8 4 52 3" : "M-52 3Q-8 4 35 1"
|
|
1514
|
+
}), n("circle", "headwear.button", {
|
|
1515
|
+
cx: 0,
|
|
1516
|
+
cy: -40,
|
|
1517
|
+
r: 3.5,
|
|
1518
|
+
fill: INK.ink
|
|
1519
|
+
}));
|
|
1520
|
+
} else if (sprout) {
|
|
1521
|
+
parts.push(n("path", "headwear.halo", {
|
|
1522
|
+
...line,
|
|
1523
|
+
stroke: fill,
|
|
1524
|
+
"stroke-width": w(11),
|
|
1525
|
+
d: `${STEM}${LEAF0}${LEAF1}`
|
|
1526
|
+
}), n("path", "headwear.stem", {
|
|
1527
|
+
...line,
|
|
1528
|
+
"stroke-width": w(4.5),
|
|
1529
|
+
d: STEM
|
|
1530
|
+
}), n("path", "headwear.leaf0", {
|
|
1531
|
+
...line,
|
|
1532
|
+
"stroke-width": w(4),
|
|
1533
|
+
fill: COLORS.mint.body,
|
|
1534
|
+
d: LEAF0
|
|
1535
|
+
}), n("path", "headwear.leaf1", {
|
|
1536
|
+
...line,
|
|
1537
|
+
"stroke-width": w(4),
|
|
1538
|
+
fill: COLORS.mint.deep,
|
|
1539
|
+
d: LEAF1
|
|
1540
|
+
}));
|
|
1541
|
+
} else {
|
|
1542
|
+
parts.push(n("path", "headwear.wings", {
|
|
1543
|
+
d: "M-3 0C-13 -16 -28 -23 -29 -10C-31 2 -26 19 -16 13L-3 4L3 4L16 13C26 19 31 2 29 -10C28 -23 13 -16 3 0Z",
|
|
1544
|
+
fill: INK.ink,
|
|
1545
|
+
stroke: fill,
|
|
1546
|
+
"stroke-width": w(6),
|
|
1547
|
+
"stroke-linejoin": "round",
|
|
1548
|
+
"paint-order": "stroke"
|
|
1549
|
+
}), n("ellipse", "headwear.knot", {
|
|
1550
|
+
cx: 0,
|
|
1551
|
+
cy: 1,
|
|
1552
|
+
rx: 6,
|
|
1553
|
+
ry: 8,
|
|
1554
|
+
fill: ACCESSORY_INK.knot
|
|
1555
|
+
}));
|
|
1556
|
+
}
|
|
1557
|
+
wardrobe.push(n("g", "headwear", {
|
|
1558
|
+
transform: `translate(${f2(anchor.x)} ${f2(anchor.y)}) rotate(${f2(sprout ? angle * 0.5 : angle)})`
|
|
1559
|
+
}, parts));
|
|
1560
|
+
}
|
|
1561
|
+
if (who.neckwear === "tie") {
|
|
1562
|
+
const skew = Math.abs(Math.sin(g("mk") * RAD2)) * mw;
|
|
1563
|
+
const bottom = Math.max(0, mt * kb, mb * kb) + skew + m.sw * st;
|
|
1564
|
+
wardrobe.push(n("g", "neckwear", {
|
|
1565
|
+
transform: `translate(${m.x} ${f2(m.y + g("my") + bottom + 8)})`,
|
|
1566
|
+
fill: INK.ink
|
|
1567
|
+
}, [
|
|
1568
|
+
n("path", "neckwear.knot", {
|
|
1569
|
+
d: "M-9 0Q-11 0 -9 4L-5 11Q0 15 5 11L9 4Q11 0 9 0Z"
|
|
1570
|
+
}),
|
|
1571
|
+
n("path", "neckwear.blade", {
|
|
1572
|
+
d: "M-4 15L-13 63Q-14 66 -11 69L0 79L11 69Q14 66 13 63L4 15Z"
|
|
1573
|
+
})
|
|
1574
|
+
]));
|
|
1575
|
+
}
|
|
1576
|
+
return [
|
|
1577
|
+
[bodyNode, ...cheeks, trait],
|
|
1578
|
+
[...eyes, ...brows, mouth, ...wardrobe]
|
|
1579
|
+
];
|
|
1025
1580
|
}
|
|
1581
|
+
// src/svg.ts
|
|
1582
|
+
var wear = (o, base, s) => ACCESSORIES[s].includes(o[s]) ? o[s] : base[s];
|
|
1583
|
+
function settle(name, o, live = false) {
|
|
1584
|
+
const base = identify(name, { version: o.version });
|
|
1585
|
+
const who = {
|
|
1586
|
+
...base,
|
|
1587
|
+
face: o.face ?? base.face,
|
|
1588
|
+
color: o.color ?? base.color,
|
|
1589
|
+
eyes: o.eyes ?? base.eyes,
|
|
1590
|
+
brows: o.brows ?? base.brows,
|
|
1591
|
+
mouth: o.mouth ?? base.mouth,
|
|
1592
|
+
cheeks: o.cheeks ?? base.cheeks,
|
|
1593
|
+
trait: o.trait ?? base.trait,
|
|
1594
|
+
eyewear: wear(o, base, "eyewear"),
|
|
1595
|
+
headwear: wear(o, base, "headwear"),
|
|
1596
|
+
neckwear: wear(o, base, "neckwear")
|
|
1597
|
+
};
|
|
1598
|
+
const pose = restPose(who, o.expression ?? "normal", o.gaze);
|
|
1599
|
+
const variant = VARIANTS.includes(o.variant) ? o.variant : "peek";
|
|
1600
|
+
const look = {
|
|
1601
|
+
size: o.size ?? 64,
|
|
1602
|
+
frame: o.frame ?? "ink",
|
|
1603
|
+
square: o.square ?? true,
|
|
1604
|
+
riso: o.riso ?? false,
|
|
1605
|
+
live,
|
|
1606
|
+
...variant !== "peek" && { variant }
|
|
1607
|
+
};
|
|
1608
|
+
const { eyewear, headwear, neckwear, ...legacy } = who;
|
|
1609
|
+
const wardrobe = eyewear !== "none" || headwear !== "none" || neckwear !== "none";
|
|
1610
|
+
const id = o.id ?? `peek-${fnv1a(JSON.stringify([wardrobe ? who : legacy, pose, look])).toString(36)}`;
|
|
1611
|
+
return { who, pose, opts: { ...look, id, title: o.title ?? name } };
|
|
1612
|
+
}
|
|
1613
|
+
var ILLEGAL = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
|
|
1614
|
+
var esc = (s) => s.replace(ILLEGAL, "�").replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
|
1615
|
+
function serialize(node) {
|
|
1616
|
+
let a = "";
|
|
1617
|
+
for (const k in node.attrs)
|
|
1618
|
+
a += ` ${k}="${esc(node.attrs[k])}"`;
|
|
1619
|
+
if (!node.children.length)
|
|
1620
|
+
return `<${node.tag}${a}/>`;
|
|
1621
|
+
return `<${node.tag}${a}>${node.children.map(serialize).join("")}</${node.tag}>`;
|
|
1622
|
+
}
|
|
1623
|
+
function toSvg(name, props = {}) {
|
|
1624
|
+
const { who, pose, opts } = settle(name, props);
|
|
1625
|
+
return serialize(draw(who, pose, opts));
|
|
1626
|
+
}
|
|
1627
|
+
// src/peek.tsx
|
|
1628
|
+
import {
|
|
1629
|
+
createElement,
|
|
1630
|
+
useEffect,
|
|
1631
|
+
useId,
|
|
1632
|
+
useRef,
|
|
1633
|
+
useState
|
|
1634
|
+
} from "react";
|
|
1026
1635
|
|
|
1027
1636
|
// src/animate.ts
|
|
1028
1637
|
var GROUP_OF = {};
|
|
@@ -1600,45 +2209,6 @@ class Live {
|
|
|
1600
2209
|
}
|
|
1601
2210
|
}
|
|
1602
2211
|
|
|
1603
|
-
// src/svg.ts
|
|
1604
|
-
function settle(name, o, live = false) {
|
|
1605
|
-
const base = identify(name, { version: o.version });
|
|
1606
|
-
const who = {
|
|
1607
|
-
...base,
|
|
1608
|
-
face: o.face ?? base.face,
|
|
1609
|
-
color: o.color ?? base.color,
|
|
1610
|
-
eyes: o.eyes ?? base.eyes,
|
|
1611
|
-
brows: o.brows ?? base.brows,
|
|
1612
|
-
mouth: o.mouth ?? base.mouth,
|
|
1613
|
-
cheeks: o.cheeks ?? base.cheeks,
|
|
1614
|
-
trait: o.trait ?? base.trait
|
|
1615
|
-
};
|
|
1616
|
-
const pose = restPose(who, o.expression ?? "normal", o.gaze);
|
|
1617
|
-
const look = {
|
|
1618
|
-
size: o.size ?? 64,
|
|
1619
|
-
frame: o.frame ?? "ink",
|
|
1620
|
-
square: o.square ?? true,
|
|
1621
|
-
riso: o.riso ?? false,
|
|
1622
|
-
live
|
|
1623
|
-
};
|
|
1624
|
-
const id = o.id ?? `peek-${fnv1a(JSON.stringify([who, pose, look])).toString(36)}`;
|
|
1625
|
-
return { who, pose, opts: { ...look, id, title: o.title ?? name } };
|
|
1626
|
-
}
|
|
1627
|
-
var ILLEGAL = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
|
|
1628
|
-
var esc = (s) => s.replace(ILLEGAL, "�").replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
|
1629
|
-
function serialize(node) {
|
|
1630
|
-
let a = "";
|
|
1631
|
-
for (const k in node.attrs)
|
|
1632
|
-
a += ` ${k}="${esc(node.attrs[k])}"`;
|
|
1633
|
-
if (!node.children.length)
|
|
1634
|
-
return `<${node.tag}${a}/>`;
|
|
1635
|
-
return `<${node.tag}${a}>${node.children.map(serialize).join("")}</${node.tag}>`;
|
|
1636
|
-
}
|
|
1637
|
-
function toSvg(name, props = {}) {
|
|
1638
|
-
const { who, pose, opts } = settle(name, props);
|
|
1639
|
-
return serialize(draw(who, pose, opts));
|
|
1640
|
-
}
|
|
1641
|
-
|
|
1642
2212
|
// src/peek.tsx
|
|
1643
2213
|
var camel = (k) => k.startsWith("aria-") ? k : k.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
1644
2214
|
function toReact(node, root) {
|
|
@@ -1684,17 +2254,24 @@ function Peek({
|
|
|
1684
2254
|
return toReact(tree, { ref: svg, className, style });
|
|
1685
2255
|
}
|
|
1686
2256
|
export {
|
|
2257
|
+
ACCESSORIES,
|
|
1687
2258
|
AXES,
|
|
2259
|
+
BLOC,
|
|
1688
2260
|
COLORS,
|
|
1689
2261
|
EXPRESSIONS,
|
|
1690
2262
|
FACES,
|
|
1691
2263
|
GROUPS,
|
|
2264
|
+
HAZE,
|
|
1692
2265
|
LATEST,
|
|
1693
2266
|
LISTS,
|
|
1694
2267
|
PARTS,
|
|
1695
2268
|
Peek,
|
|
2269
|
+
VARIANTS,
|
|
1696
2270
|
VERSIONS,
|
|
2271
|
+
WHEEL,
|
|
2272
|
+
draw,
|
|
1697
2273
|
identify,
|
|
2274
|
+
settle,
|
|
1698
2275
|
tidy,
|
|
1699
2276
|
toSvg
|
|
1700
2277
|
};
|