@7ots/cli 0.1.7 → 0.1.8
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 +1 -1
- package/cli/7ots.mjs +13 -1
- package/cli/lib/account.mjs +152 -2
- package/cli/lib/assistant.mjs +106 -41
- package/cli/lib/brain.mjs +13 -2
- package/cli/lib/config.mjs +3 -0
- package/cli/lib/inferences.mjs +30 -6
- package/cli/lib/integrations.mjs +69 -5
- package/cli/lib/orquesta.mjs +44 -1
- package/cli/lib/pet-server.mjs +344 -11
- package/cli/pet/electron/director.cjs +37 -5
- package/cli/pet/electron/main.cjs +92 -10
- package/cli/pet/pet.html +149 -10
- package/cli/pet/settings.html +545 -42
- package/dist/7ots.esm.js +216 -137
- package/dist/7ots.esm.js.map +4 -4
- package/dist/7ots.iife.js +216 -137
- package/dist/7ots.iife.js.map +4 -4
- package/package.json +1 -1
- package/server/channels/agent.mjs +7 -1
- package/server/channels/index.mjs +3 -2
- package/server/platform/connect.mjs +4 -1
- package/server/platform/db.mjs +9 -1
- package/server/platform/device.mjs +101 -4
- package/server/platform/mods.mjs +222 -0
- package/server/platform/orquesta.mjs +78 -14
- package/server/platform/profile.mjs +161 -0
- package/server/platform/routes.mjs +38 -33
- package/server/platform/runtime.mjs +3 -1
- package/server/platform/store.mjs +1 -1
- package/server/platform/vm.mjs +370 -91
- package/server/server.mjs +12 -2
- package/src/avatar/three.js +3 -2
- package/src/character/AvatarEditor.js +95 -7
- package/src/character/Character.js +116 -17
- package/src/character/TradingCard.js +226 -0
- package/src/character/mods/index.js +4 -0
- package/src/character/mods/orc.js +64 -0
- package/src/character/mods.js +251 -0
- package/src/character/more/acc-head.js +213 -0
- package/src/character/more/acc-rest.js +342 -0
- package/src/character/more/brows.js +39 -0
- package/src/character/more/cheeks.js +78 -0
- package/src/character/more/eyes.js +288 -0
- package/src/character/more/mouths.js +259 -0
- package/src/character/more/patterns.js +120 -0
- package/src/character/parts.js +90 -0
- package/src/character/skin.js +166 -1
- package/src/character/styles.js +1285 -9
- package/src/character3d/Character3D.js +148 -35
- package/src/character3d/index.js +7 -1
- package/src/character3d/model.js +367 -0
- package/src/i18n/messages/character.js +34 -22
- package/src/i18n/messages/platform.js +15 -0
- package/src/identity/random.js +17 -3
- package/src/identity/schema.js +10 -0
- package/src/index.js +1 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Catálogo 2 (2026-10): piezas nuevas, añadidas al final de su lista en parts.js.
|
|
3
|
+
* Cada pieza: { id, label (es), en, pt, as3d (id existente más parecido para el visor 3D), … }.
|
|
4
|
+
*
|
|
5
|
+
* Accesorios de ojos, cara, cuello, espalda y extras.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** @param {object} h ayudantes de dibujo de parts.js */
|
|
9
|
+
export default (h) => {
|
|
10
|
+
const { n, shade, S, star, heartPath } = h;
|
|
11
|
+
const both = (f) => [-1, 1].map(f).join('');
|
|
12
|
+
const glint = (x, y, l, w = 2.5) => `<path d="M${n(x)} ${n(y)}l${n(l * 0.6)} ${n(l)}" stroke="#fff" stroke-width="${w}" opacity=".6" stroke-linecap="round"/>`;
|
|
13
|
+
// Correa que rodea la cabeza (se recorta con la silueta: clipExtra).
|
|
14
|
+
const strap = (c, y, w, col) => `<path d="M${n(-c.w * 0.6)} ${n(y)}H${n(c.w * 0.6)}" stroke="${col}" stroke-width="${n(w)}"/>`;
|
|
15
|
+
const ell = (x, y, rx, ry) => `M${n(x - rx)} ${n(y)}a${n(rx)} ${n(ry)} 0 1 0 ${n(rx * 2)} 0a${n(rx)} ${n(ry)} 0 1 0 ${n(-rx * 2)} 0Z`;
|
|
16
|
+
// Collar en arco a la altura del cuello (como el de perlas).
|
|
17
|
+
const arc = (c, k, i, N, dy = 0) => {
|
|
18
|
+
const t = (i / (N - 1)) * Math.PI;
|
|
19
|
+
return [n(-Math.cos(t) * c.w * k), n(Math.sin(t) * 12 - 6 + dy)];
|
|
20
|
+
};
|
|
21
|
+
const flower5 = (x, y, r, col) => `<g transform="translate(${n(x)} ${n(y)})">${[0, 1, 2, 3, 4].map((i) => { const a = (i * Math.PI * 2) / 5 - Math.PI / 2; return `<circle cx="${n(Math.cos(a) * r)}" cy="${n(Math.sin(a) * r)}" r="${n(r * 0.75)}" fill="${col}" stroke="${shade(col, -0.3)}" stroke-width="1"/>`; }).join('')}<circle r="${n(r * 0.5)}" fill="#fde047"/></g>`;
|
|
22
|
+
const note = (x, y, s, col) => `<g transform="translate(${x} ${y}) scale(${s})"><ellipse cx="0" cy="0" rx="6" ry="4.5" transform="rotate(-25)" fill="${col}" stroke="#1f2937" stroke-width="1.5"/><path d="M5 -2V-24Q14 -20 14 -10" stroke="${col}" stroke-width="3" fill="none" stroke-linecap="round"/><path d="M5 -2V-24" stroke="#1f2937" stroke-width="1" opacity=".4"/></g>`;
|
|
23
|
+
const BFLY_UP = 'M0 0C6 -34 54 -50 60 -22C64 -4 36 6 0 2Z';
|
|
24
|
+
const BFLY_LO = 'M0 4C24 6 46 16 38 34C28 46 6 30 0 8Z';
|
|
25
|
+
const FAIRY_UP = 'M0 0C4 -30 30 -64 50 -58C62 -50 40 -14 0 2Z';
|
|
26
|
+
const FAIRY_LO = 'M0 4C20 8 40 22 34 34C26 42 8 26 0 8Z';
|
|
27
|
+
const DRAGON = 'M0 0L16 -40L28 -28L44 -50L50 -32L68 -42Q60 -20 70 -4Q54 -8 50 6Q40 -4 32 12Q22 2 0 10Z';
|
|
28
|
+
const foxD = (c) => `M${n(c.w * 0.26)} -4Q${n(c.w * 0.62)} 4 ${n(c.w * 0.72)} -26Q${n(c.w * 0.8)} -54 ${n(c.w * 0.62)} -70Q${n(c.w * 0.68)} -46 ${n(c.w * 0.52)} -32Q${n(c.w * 0.42)} -20 ${n(c.w * 0.24)} -24Z`;
|
|
29
|
+
|
|
30
|
+
return [
|
|
31
|
+
// ── ojos ──
|
|
32
|
+
{
|
|
33
|
+
id: 'catglasses', label: 'Gafas de gato', en: 'Cat-eye glasses', pt: 'Óculos gatinho', group: 'eyes', anchor: 'eyes', color: '#db2777', color2: '#fde047', as3d: 'glasses',
|
|
34
|
+
draw: (c) => {
|
|
35
|
+
const R = c.r;
|
|
36
|
+
return both((s) => {
|
|
37
|
+
const x = s * c.sp;
|
|
38
|
+
return `<path d="M${n(x - s * R * 1.5)} ${n(-R * 0.9)}Q${n(x)} ${n(-R * 1.5)} ${n(x + s * R * 2.3)} ${n(-R * 1.8)}Q${n(x + s * R * 1.9)} ${n(R * 1.4)} ${n(x)} ${n(R * 1.35)}Q${n(x - s * R * 1.65)} ${n(R * 1.3)} ${n(x - s * R * 1.5)} ${n(-R * 0.9)}Z" fill="#fff" fill-opacity=".14" stroke="${c.color}" stroke-width="3.5" stroke-linejoin="round"/><circle cx="${n(x + s * R * 1.95)}" cy="${n(-R * 1.25)}" r="2.2" fill="${c.color2}" stroke="#1f2937" stroke-width="1"/>`;
|
|
39
|
+
}) + `<path d="M${n(-c.sp + R * 1.5)} ${n(-R * 0.6)}Q0 ${n(-R * 1.2)} ${n(c.sp - R * 1.5)} ${n(-R * 0.6)}" stroke="${c.color}" stroke-width="3" fill="none"/>`;
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'shutters', label: 'Gafas persiana', en: 'Shutter shades', pt: 'Óculos persiana', group: 'eyes', anchor: 'eyes', color: '#facc15', as3d: 'sunglasses',
|
|
44
|
+
draw: (c) => {
|
|
45
|
+
const R = c.r;
|
|
46
|
+
return both((s) => {
|
|
47
|
+
const x = s * c.sp;
|
|
48
|
+
const bars = [-0.55, 0, 0.55].map((k) => `<path d="M${n(x - R * 1.7)} ${n(k * R)}H${n(x + R * 1.7)}" stroke="${c.color}" stroke-width="${n(Math.max(2.5, R * 0.3))}"/>`).join('');
|
|
49
|
+
return `<rect x="${n(x - R * 1.8)}" y="${n(-R * 1.3)}" width="${n(R * 3.6)}" height="${n(R * 2.6)}" rx="${n(R * 0.6)}" fill="none" stroke="#1f2937" stroke-width="6"/>${bars}<rect x="${n(x - R * 1.8)}" y="${n(-R * 1.3)}" width="${n(R * 3.6)}" height="${n(R * 2.6)}" rx="${n(R * 0.6)}" fill="none" stroke="${c.color}" stroke-width="3"/>`;
|
|
50
|
+
}) + `<path d="M${n(-c.sp + R * 1.8)} ${n(-R * 0.6)}H${n(c.sp - R * 1.8)}" stroke="${c.color}" stroke-width="3.5"/>`;
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'skigoggles', label: 'Gafas de esquí', en: 'Ski goggles', pt: 'Óculos de esqui', group: 'eyes', anchor: 'eyes', color: '#f97316', color2: '#1e293b', clipExtra: true, as3d: 'goggles',
|
|
55
|
+
draw: (c) => {
|
|
56
|
+
const R = c.r;
|
|
57
|
+
const x0 = -c.sp - R * 2.2;
|
|
58
|
+
const lens = `M${n(x0)} ${n(-R * 0.2)}Q${n(x0)} ${n(-R * 1.6)} ${n(x0 + R * 1.4)} ${n(-R * 1.6)}H${n(-x0 - R * 1.4)}Q${n(-x0)} ${n(-R * 1.6)} ${n(-x0)} ${n(-R * 0.2)}Q${n(-x0)} ${n(R * 1.5)} ${n(-x0 - R * 1.4)} ${n(R * 1.5)}H${n(R * 0.9)}Q0 ${n(R * 0.6)} ${n(-R * 0.9)} ${n(R * 1.5)}H${n(x0 + R * 1.4)}Q${n(x0)} ${n(R * 1.5)} ${n(x0)} ${n(-R * 0.2)}Z`;
|
|
59
|
+
return `<path d="${lens}" fill="${c.color}" stroke="#1f2937" stroke-width="5" stroke-linejoin="round"/><path d="${lens}" fill="none" stroke="${c.color2}" stroke-width="2.5" stroke-linejoin="round"/><path d="M${n(x0 + R * 0.6)} ${n(-R * 0.5)}Q0 ${n(-R * 1.5)} ${n(-x0 - R * 0.6)} ${n(-R * 0.5)}" stroke="#fde68a" stroke-width="${n(R * 0.5)}" fill="none" opacity=".55" stroke-linecap="round"/><path d="M${n(x0 + R * 0.9)} ${n(R * 0.5)}l${n(R * 0.9)} ${n(-R * 1.3)}" stroke="#fff" stroke-width="3" opacity=".7" stroke-linecap="round"/>`;
|
|
60
|
+
},
|
|
61
|
+
extra: (c) => strap(c, -c.r * 0.2, c.r * 1.3, c.color2),
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'swimgoggles', label: 'Gafas de natación', en: 'Swim goggles', pt: 'Óculos de natação', group: 'eyes', anchor: 'eyes', color: '#06b6d4', color2: '#a5f3fc', clipExtra: true, as3d: 'goggles',
|
|
65
|
+
draw: (c) => {
|
|
66
|
+
const R = c.r;
|
|
67
|
+
return both((s) => `<ellipse cx="${n(s * c.sp)}" rx="${n(R * 1.55)}" ry="${n(R * 1.25)}" fill="${c.color2}" fill-opacity=".55" stroke="${c.color}" stroke-width="4.5"/><ellipse cx="${n(s * c.sp)}" rx="${n(R * 1.55)}" ry="${n(R * 1.25)}" fill="none" stroke="#1f2937" stroke-width="1.2" opacity=".6"/>${glint(s * c.sp - R * 0.8, -R * 0.6, R * 0.7, 2)}`) + `<path d="M${n(-c.sp + R * 1.5)} 0Q0 ${n(-R * 0.5)} ${n(c.sp - R * 1.5)} 0" stroke="${c.color}" stroke-width="3" fill="none"/>`;
|
|
68
|
+
},
|
|
69
|
+
extra: (c) => strap(c, 0, 3, c.color),
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'sleepmask', label: 'Antifaz de dormir', en: 'Sleep mask', pt: 'Máscara de dormir', group: 'eyes', anchor: 'eyes', color: '#a78bfa', color2: '#ffffff', clipExtra: true, as3d: 'sunglasses',
|
|
73
|
+
draw: (c) => {
|
|
74
|
+
const R = c.r;
|
|
75
|
+
const X = c.sp + R * 2.1;
|
|
76
|
+
const d = `M${n(-X)} 0C${n(-X)} ${n(-R * 1.9)} ${n(-c.sp * 0.25)} ${n(-R * 1.9)} 0 ${n(-R * 1.3)}C${n(c.sp * 0.25)} ${n(-R * 1.9)} ${n(X)} ${n(-R * 1.9)} ${n(X)} 0C${n(X)} ${n(R * 1.8)} ${n(c.sp * 0.3)} ${n(R * 1.8)} 0 ${n(R * 1.1)}C${n(-c.sp * 0.3)} ${n(R * 1.8)} ${n(-X)} ${n(R * 1.8)} ${n(-X)} 0Z`;
|
|
77
|
+
const lid = both((s) => {
|
|
78
|
+
const x = s * c.sp;
|
|
79
|
+
return `<path d="M${n(x - R * 0.9)} ${n(R * 0.05)}Q${n(x)} ${n(R * 0.85)} ${n(x + R * 0.9)} ${n(R * 0.05)}" stroke="${c.color2}" stroke-width="${n(Math.max(2, R * 0.22))}" fill="none" stroke-linecap="round"/>${[-0.5, 0, 0.5].map((k) => `<path d="M${n(x + k * R)} ${n(R * 0.5)}l${n(k * R * 0.3)} ${n(R * 0.4)}" stroke="${c.color2}" stroke-width="2" stroke-linecap="round"/>`).join('')}`;
|
|
80
|
+
});
|
|
81
|
+
return `<path d="${d}" fill="${c.color}" ${S(2.5)}/><path d="M${n(-X + R * 0.8)} ${n(-R * 0.9)}Q${n(-c.sp)} ${n(-R * 1.5)} ${n(-c.sp * 0.4)} ${n(-R * 1.25)}" stroke="#fff" stroke-width="2.5" fill="none" opacity=".4" stroke-linecap="round"/>${lid}`;
|
|
82
|
+
},
|
|
83
|
+
extra: (c) => strap(c, -c.r * 0.2, 3.5, shade(c.color, -0.2)),
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: 'vrheadset', label: 'Gafas VR', en: 'VR headset', pt: 'Óculos VR', group: 'eyes', anchor: 'eyes', color: '#e5e7eb', color2: '#111827', clipExtra: true, as3d: 'visor',
|
|
87
|
+
draw: (c) => {
|
|
88
|
+
const R = c.r;
|
|
89
|
+
const X = c.sp + R * 2.3;
|
|
90
|
+
return `<rect x="${n(-X)}" y="${n(-R * 1.8)}" width="${n(X * 2)}" height="${n(R * 3.6)}" rx="${n(R * 1.1)}" fill="${c.color}" ${S(3)}/><rect x="${n(-X + R * 0.6)}" y="${n(-R * 1.2)}" width="${n(X * 2 - R * 1.2)}" height="${n(R * 2.4)}" rx="${n(R * 0.7)}" fill="${c.color2}"/><path d="M${n(-X + R * 1.1)} ${n(-R * 0.6)}H${n(-X + R * 3)}" stroke="#22d3ee" stroke-width="2.5" stroke-linecap="round" opacity=".9"/><circle cx="${n(X - R * 1.3)}" cy="${n(R * 0.5)}" r="${n(Math.max(1.5, R * 0.18))}" fill="#22c55e"/><path d="M${n(-X + R * 0.4)} ${n(-R * 1.55)}H${n(X - R * 0.4)}" stroke="#fff" stroke-width="2" opacity=".6" stroke-linecap="round"/>`;
|
|
91
|
+
},
|
|
92
|
+
extra: (c) => strap(c, 0, c.r * 1.2, '#374151'),
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: 'halfmoon', label: 'Gafas de lectura', en: 'Reading glasses', pt: 'Óculos de leitura', group: 'eyes', anchor: 'eyes', color: '#a16207', as3d: 'glasses',
|
|
96
|
+
draw: (c) => {
|
|
97
|
+
const R = c.r;
|
|
98
|
+
const y = R * 0.45;
|
|
99
|
+
return both((s) => {
|
|
100
|
+
const x = s * c.sp;
|
|
101
|
+
return `<path d="M${n(x - R * 1.6)} ${n(y)}H${n(x + R * 1.6)}Q${n(x + R * 1.6)} ${n(y + R * 1.6)} ${n(x)} ${n(y + R * 1.6)}Q${n(x - R * 1.6)} ${n(y + R * 1.6)} ${n(x - R * 1.6)} ${n(y)}Z" fill="#fff" fill-opacity=".18" stroke="${c.color}" stroke-width="3.2" stroke-linejoin="round"/>`;
|
|
102
|
+
}) + `<path d="M${n(-c.sp + R * 1.6)} ${n(y + 1)}Q0 ${n(y - R * 0.6)} ${n(c.sp - R * 1.6)} ${n(y + 1)}" stroke="${c.color}" stroke-width="2.8" fill="none"/><path d="M${n(-c.sp - R * 1.6)} ${n(y + 1)}Q${n(-c.sp - R * 2.6)} ${n(y + R * 4)} 0 ${n(y + R * 5)}Q${n(c.sp + R * 2.6)} ${n(y + R * 4)} ${n(c.sp + R * 1.6)} ${n(y + 1)}" stroke="${c.color}" stroke-width="1.4" fill="none" stroke-dasharray="2.5 2" opacity=".8"/>`;
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: 'flowerglasses', label: 'Gafas de flor', en: 'Flower glasses', pt: 'Óculos de flor', group: 'eyes', anchor: 'eyes', color: '#f472b6', as3d: 'heartglasses',
|
|
107
|
+
draw: (c) => {
|
|
108
|
+
const R = c.r;
|
|
109
|
+
return both((s) => {
|
|
110
|
+
const x = s * c.sp;
|
|
111
|
+
const petals = Array.from({ length: 7 }, (_, i) => { const a = (i * Math.PI * 2) / 7 - Math.PI / 2; return `<circle cx="${n(x + Math.cos(a) * R * 1.55)}" cy="${n(Math.sin(a) * R * 1.55)}" r="${n(R * 0.72)}"/>`; }).join('');
|
|
112
|
+
return `<g fill="${c.color}" stroke="${shade(c.color, -0.4)}" stroke-width="2">${petals}</g><circle cx="${n(x)}" r="${n(R * 1.3)}" fill="#fff" fill-opacity=".85" stroke="${shade(c.color, -0.4)}" stroke-width="2"/><circle cx="${n(x)}" r="${n(R * 1.3)}" fill="${c.color}" fill-opacity=".15"/>`;
|
|
113
|
+
}) + `<path d="M${n(-c.sp + R * 1.9)} ${n(-R * 0.4)}Q0 ${n(-R * 1.1)} ${n(c.sp - R * 1.9)} ${n(-R * 0.4)}" stroke="${shade(c.color, -0.4)}" stroke-width="3" fill="none"/>`;
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: 'pixelshades', label: 'Gafas píxel', en: 'Pixel shades', pt: 'Óculos pixel', group: 'eyes', anchor: 'eyes', color: '#0f172a', as3d: 'sunglasses',
|
|
118
|
+
draw: (c) => {
|
|
119
|
+
const u = Math.max(2.4, c.r * 0.55);
|
|
120
|
+
const X = c.sp + u * 3.5;
|
|
121
|
+
let d = `<rect x="${n(-X)}" y="${n(-u * 1.6)}" width="${n(X * 2)}" height="${n(u)}" fill="${c.color}"/>`;
|
|
122
|
+
for (const s of [-1, 1]) {
|
|
123
|
+
const x0 = s * c.sp - u * 2.5;
|
|
124
|
+
for (let r = 0; r < 3; r++) d += `<rect x="${n(x0 + (r === 2 ? u : 0))}" y="${n(-u * 0.6 + r * u)}" width="${n(u * (r === 2 ? 3 : 5))}" height="${n(u + 0.3)}" fill="${c.color}"/>`;
|
|
125
|
+
d += `<rect x="${n(x0 + u)}" y="${n(-u * 0.6)}" width="${n(u)}" height="${n(u)}" fill="#fff"/><rect x="${n(x0 + u * 2)}" y="${n(u * 0.4)}" width="${n(u)}" height="${n(u)}" fill="#fff"/>`;
|
|
126
|
+
}
|
|
127
|
+
return d;
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: 'masquerade', label: 'Máscara de baile', en: 'Masquerade mask', pt: 'Máscara de baile', group: 'eyes', anchor: 'eyes', color: '#7c3aed', color2: '#facc15', as3d: 'sunglasses',
|
|
132
|
+
draw: (c) => {
|
|
133
|
+
const R = c.r;
|
|
134
|
+
const sp = c.sp;
|
|
135
|
+
const X = sp + R * 2.6;
|
|
136
|
+
const d = `M${n(-X)} ${n(-R * 1.7)}Q${n(-sp)} ${n(-R * 2.1)} 0 ${n(-R * 1)}Q${n(sp)} ${n(-R * 2.1)} ${n(X)} ${n(-R * 1.7)}Q${n(sp + R * 2.3)} ${n(R * 1.6)} ${n(sp)} ${n(R * 1.5)}Q${n(sp * 0.3)} ${n(R * 1.5)} 0 ${n(R * 0.7)}Q${n(-sp * 0.3)} ${n(R * 1.5)} ${n(-sp)} ${n(R * 1.5)}Q${n(-sp - R * 2.3)} ${n(R * 1.6)} ${n(-X)} ${n(-R * 1.7)}Z${ell(-sp, 0, R * 1.25, R * 1.05)}${ell(sp, 0, R * 1.25, R * 1.05)}`;
|
|
137
|
+
return `<path d="M${n(X - R * 0.4)} ${n(-R * 1.6)}Q${n(X + R * 1.6)} ${n(-R * 4)} ${n(X + R * 0.4)} ${n(-R * 5.4)}Q${n(X + R * 0.2)} ${n(-R * 3.4)} ${n(X - R * 1.2)} ${n(-R * 1.6)}Z" fill="${c.color2}" ${S(2)}/><path d="${d}" fill="${c.color}" fill-rule="evenodd" ${S(2.5)}/>${both((s) => `<circle cx="${n(s * (sp + R * 1.9))}" cy="${n(-R * 0.6)}" r="2" fill="${c.color2}"/><circle cx="${n(s * sp)}" cy="${n(-R * 1.55)}" r="2" fill="${c.color2}"/>`)}`;
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
id: 'heromask', label: 'Antifaz de héroe', en: 'Hero mask', pt: 'Máscara de herói', group: 'eyes', anchor: 'eyes', color: '#dc2626', clipExtra: true, as3d: 'sunglasses',
|
|
142
|
+
draw: (c) => {
|
|
143
|
+
const R = c.r;
|
|
144
|
+
const sp = c.sp;
|
|
145
|
+
const X = sp + R * 2.2;
|
|
146
|
+
const d = `M${n(-X)} ${n(-R * 0.4)}Q${n(-X)} ${n(-R * 1.7)} ${n(-sp)} ${n(-R * 1.7)}Q${n(-sp * 0.3)} ${n(-R * 1.7)} 0 ${n(-R * 1.2)}Q${n(sp * 0.3)} ${n(-R * 1.7)} ${n(sp)} ${n(-R * 1.7)}Q${n(X)} ${n(-R * 1.7)} ${n(X)} ${n(-R * 0.4)}Q${n(X)} ${n(R * 1.6)} ${n(sp)} ${n(R * 1.5)}Q${n(sp * 0.3)} ${n(R * 1.4)} 0 ${n(R * 0.8)}Q${n(-sp * 0.3)} ${n(R * 1.4)} ${n(-sp)} ${n(R * 1.5)}Q${n(-X)} ${n(R * 1.6)} ${n(-X)} ${n(-R * 0.4)}Z${ell(-sp, 0, R * 1.15, R * 0.95)}${ell(sp, 0, R * 1.15, R * 0.95)}`;
|
|
147
|
+
return `<path d="${d}" fill="${c.color}" fill-rule="evenodd" ${S(2.5)}/><path d="M${n(-X + R * 0.6)} ${n(-R * 0.9)}Q${n(-sp - R)} ${n(-R * 1.45)} ${n(-sp)} ${n(-R * 1.45)}" stroke="#fff" stroke-width="2.5" fill="none" opacity=".4" stroke-linecap="round"/>`;
|
|
148
|
+
},
|
|
149
|
+
extra: (c) => strap(c, -c.r * 0.3, 4, shade(c.color, -0.15)),
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: 'loupe', label: 'Lupa de joyero', en: 'Jeweler loupe', pt: 'Lupa de joalheiro', group: 'eyes', anchor: 'eyes', color: '#111827', color2: '#facc15', clipExtra: true, as3d: 'monocle',
|
|
153
|
+
draw: (c) => {
|
|
154
|
+
const R = c.r;
|
|
155
|
+
const x = c.sp;
|
|
156
|
+
return `<circle cx="${n(x)}" r="${n(R * 1.9)}" fill="${c.color}" ${S(2)}/><circle cx="${n(x)}" r="${n(R * 1.35)}" fill="#bae6fd" fill-opacity=".55" stroke="${c.color2}" stroke-width="2.5"/><circle cx="${n(x)}" r="${n(R * 1.35)}" fill="none" stroke="#1f2937" stroke-width="1"/>${glint(x - R * 0.8, -R * 0.7, R * 0.8)}`;
|
|
157
|
+
},
|
|
158
|
+
extra: (c) => `<path d="M${n(-c.w * 0.6)} ${n(-c.r * 3.2)}Q0 ${n(-c.r * 3.6)} ${n(c.sp)} ${n(-c.r * 1.8)}" stroke="#374151" stroke-width="3" fill="none"/>`,
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
// ── cara ──
|
|
162
|
+
{
|
|
163
|
+
id: 'dognose', label: 'Nariz de perrito', en: 'Puppy nose', pt: 'Nariz de cachorro', group: 'face', anchor: 'nose', color: '#1f2937', as3d: 'clownnose',
|
|
164
|
+
draw: (c) => `<g transform="scale(${n(c.m)})"><path d="M-10 -5Q0 -10 10 -5Q11 2 0 7Q-11 2 -10 -5Z" fill="${c.color}" stroke="${shade(c.color, -0.3)}" stroke-width="1.5" stroke-linejoin="round"/><ellipse cx="-3.5" cy="-4.5" rx="3.2" ry="1.6" fill="#fff" opacity=".55"/><path d="M0 7V11" stroke="${c.color}" stroke-width="2" stroke-linecap="round"/></g>`,
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: 'bunnynose', label: 'Nariz de conejito', en: 'Bunny nose', pt: 'Nariz de coelhinho', group: 'face', anchor: 'nose', color: '#f9a8d4', as3d: 'nose',
|
|
168
|
+
draw: (c) => `<g transform="scale(${n(c.m)})"><path d="M-6 -3Q0 -6 6 -3Q4 3 0 4Q-4 3 -6 -3Z" fill="${c.color}" stroke="${shade(c.color, -0.35)}" stroke-width="1.5" stroke-linejoin="round"/><path d="M0 4V8M0 8Q-3 11 -7 10M0 8Q3 11 7 10" stroke="#1f2937" stroke-width="1.6" fill="none" stroke-linecap="round" opacity=".75"/>${both((s) => [0, 1, 2].map((i) => `<circle cx="${s * (14 + i * 4)}" cy="${2 + (i % 2) * 4}" r="1.3" fill="#1f2937" opacity=".55"/>`).join(''))}</g>`,
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: 'nosering', label: 'Aro en la nariz', en: 'Nose ring', pt: 'Argola no nariz', group: 'face', anchor: 'nose', color: '#facc15', as3d: 'nose',
|
|
172
|
+
draw: (c) => `<g transform="scale(${n(c.m)})"><ellipse rx="6" ry="4" fill="${c.body}" style="filter:brightness(.82)"/><ellipse cx="-2" cy="-1.5" rx="2" ry="1" fill="#fff" opacity=".5"/><circle cx="2.5" cy="6" r="4.2" fill="none" stroke="#1f2937" stroke-width="3.6"/><circle cx="2.5" cy="6" r="4.2" fill="none" stroke="${c.color}" stroke-width="2"/></g>`,
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: 'pacifier', label: 'Chupete', en: 'Pacifier', pt: 'Chupeta', group: 'face', anchor: 'mouth', color: '#7dd3fc', color2: '#f9a8d4', as3d: 'lollipop',
|
|
176
|
+
draw: (c) => `<g transform="translate(0 2) scale(${n(c.m)})"><circle cy="9" r="7" fill="none" stroke="#1f2937" stroke-width="5.5"/><circle cy="9" r="7" fill="none" stroke="${c.color2}" stroke-width="3"/><path d="M-15 -2Q-16 -9 -8 -8Q0 -12 8 -8Q16 -9 15 -2Q14 5 0 5Q-14 5 -15 -2Z" fill="${c.color}" ${S(2.5)}/><circle cy="-2" r="4" fill="${shade(c.color, -0.2)}" stroke="#1f2937" stroke-width="2"/><path d="M-10 -5Q-6 -8 -2 -7" stroke="#fff" stroke-width="2" fill="none" opacity=".7" stroke-linecap="round"/></g>`,
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: 'bubblegum', label: 'Chicle', en: 'Bubble gum', pt: 'Chiclete', group: 'face', anchor: 'mouth', color: '#f9a8d4', as3d: 'lollipop',
|
|
180
|
+
draw: (c) => `<g transform="scale(${n(c.m)})"><circle cy="5" r="15" fill="${c.color}" fill-opacity=".92" stroke="${shade(c.color, -0.3)}" stroke-width="2"/><path d="M-9 -2Q-8 -7 -3 -8" stroke="#fff" stroke-width="2.5" fill="none" stroke-linecap="round" opacity=".85"/><circle cx="7" cy="11" r="2" fill="#fff" opacity=".6"/></g>`,
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: 'mouthrose', label: 'Rosa en la boca', en: 'Rose in mouth', pt: 'Rosa na boca', group: 'face', anchor: 'mouth', color: '#e11d48', color2: '#16a34a', as3d: 'pipe',
|
|
184
|
+
draw: (c) => `<g transform="scale(${n(c.m)})"><path d="M-24 2L20 6" stroke="#1f2937" stroke-width="5" stroke-linecap="round"/><path d="M-24 2L20 6" stroke="${c.color2}" stroke-width="3" stroke-linecap="round"/><path d="M4 4q6 8 13 4q-5 -7 -13 -4Z" fill="${c.color2}" stroke="#1f2937" stroke-width="1.5"/><g transform="translate(27 6)"><circle r="8.5" fill="${c.color}" ${S(2)}/><path d="M-4 -1a4 4 0 1 1 6 3M-6 3q4 4 9 1M-1 -5q5 -1 6 4" stroke="${shade(c.color, -0.4)}" stroke-width="1.6" fill="none" stroke-linecap="round"/></g></g>`,
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: 'candycane', label: 'Bastón de caramelo', en: 'Candy cane', pt: 'Bengala doce', group: 'face', anchor: 'mouth', color: '#ef4444', as3d: 'lollipop',
|
|
188
|
+
draw: (c) => {
|
|
189
|
+
const d = 'M0 0L20 4Q30 6 31 -3Q31 -11 24 -10';
|
|
190
|
+
return `<g transform="translate(${n(c.mw * 0.4)} 0) scale(${n(c.m)})"><path d="${d}" stroke="#1f2937" stroke-width="8.5" fill="none" stroke-linecap="round"/><path d="${d}" stroke="#fff" stroke-width="5.5" fill="none" stroke-linecap="round"/><path d="${d}" stroke="${c.color}" stroke-width="5.5" fill="none" stroke-dasharray="4 4"/></g>`;
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: 'wheat', label: 'Espiga', en: 'Straw stalk', pt: 'Espiga', group: 'face', anchor: 'mouth', color: '#eab308', as3d: 'pipe',
|
|
195
|
+
draw: (c) => `<g transform="translate(${n(c.mw * 0.4)} 0) scale(${n(c.m)})"><path d="M0 0L30 -14" stroke="${shade(c.color, -0.25)}" stroke-width="2.5" stroke-linecap="round"/>${[0, 1, 2, 3].map((i) => `<ellipse cx="${26 + i * 4}" cy="${-12 - i * 2}" rx="3.5" ry="1.8" transform="rotate(-50 ${26 + i * 4} ${-12 - i * 2})" fill="${c.color}" stroke="#1f2937" stroke-width="1"/><ellipse cx="${28 + i * 4}" cy="${-9 - i * 2}" rx="3.5" ry="1.8" transform="rotate(10 ${28 + i * 4} ${-9 - i * 2})" fill="${c.color}" stroke="#1f2937" stroke-width="1"/>`).join('')}</g>`,
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: 'tusks', label: 'Colmillos de ogro', en: 'Ogre tusks', pt: 'Presas de ogro', group: 'face', anchor: 'mouth', color: '#fefce8', as3d: 'goatee',
|
|
199
|
+
draw: (c) => both((s) => `<g transform="translate(${n(s * (c.mw * 0.8 + 2 * c.m))} ${n(3 * c.m)}) scale(${n(s * c.m)} ${n(c.m)})"><path d="M-3.5 2Q-4 -6 1.5 -11Q0.5 -4 3.5 2Z" fill="${c.color}" stroke="#1f2937" stroke-width="1.8" stroke-linejoin="round"/></g>`),
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
id: 'pencilstache', label: 'Bigote fino', en: 'Pencil mustache', pt: 'Bigode fino', group: 'face', anchor: 'mouth', color: '#1f2937', as3d: 'mustache',
|
|
203
|
+
draw: (c) => `<path d="M0 -8Q-12 -13 -24 -9Q-26 -8 -24 -7Q-12 -8 0 -5Q12 -8 24 -7Q26 -8 24 -9Q12 -13 0 -8Z" fill="${c.color}" stroke="${c.color}" stroke-width="1.2" stroke-linejoin="round" transform="scale(${n(c.m)})"/>`,
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: 'stubble', label: 'Barba de tres días', en: 'Stubble', pt: 'Barba por fazer', group: 'face', anchor: 'mouth', color: '#44403c', clip: true, as3d: 'beard',
|
|
207
|
+
draw: (c) => {
|
|
208
|
+
let d = '';
|
|
209
|
+
const W = c.w * 0.36;
|
|
210
|
+
for (let i = 0; i < 70; i++) {
|
|
211
|
+
const x = (((i * 37) % 70) / 70 - 0.5) * 2 * W + Math.sin(i * 7.3) * 3;
|
|
212
|
+
const y = -8 + ((i * 53) % 70) / 70 * 34 + Math.cos(i * 3.1) * 2;
|
|
213
|
+
if (Math.abs(x) < c.mw + 3 && y < 7) continue;
|
|
214
|
+
if ((x / W) ** 2 + ((y - 4) / 30) ** 2 > 1) continue;
|
|
215
|
+
d += `<circle cx="${n(x)}" cy="${n(y)}" r="1.2"/>`;
|
|
216
|
+
}
|
|
217
|
+
return `<g fill="${c.color}" opacity=".55">${d}</g>`;
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
id: 'cheeksticker', label: 'Pegatina', en: 'Heart sticker', pt: 'Adesivo', group: 'face', anchor: 'eyes', color: '#f472b6', as3d: 'bandaid',
|
|
222
|
+
draw: (c) => `<g transform="translate(${n(-(c.sp + c.r * 0.5))} ${n(c.r * 2.3)}) rotate(-12)"><path d="${heartPath(7)}" fill="#fff" stroke="#fff" stroke-width="4" stroke-linejoin="round"/><path d="${heartPath(7)}" fill="${c.color}"/><path d="M-4 -3l1.5 -1.5" stroke="#fff" stroke-width="1.6" stroke-linecap="round"/><path d="${star(2.6, 0.35, 4)}" transform="translate(9 -6)" fill="#facc15"/></g>`,
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
id: 'duckbill', label: 'Pico de pato', en: 'Duck bill', pt: 'Bico de pato', group: 'face', anchor: 'mouth', color: '#f59e0b', as3d: 'pignose',
|
|
226
|
+
draw: (c) => `<g transform="scale(${n(c.m)})"><path d="M-20 4Q-14 12 0 11Q14 12 20 4Q10 8 0 7Q-10 8 -20 4Z" fill="${shade(c.color, -0.15)}" ${S(2)}/><path d="M-22 0Q-24 -12 0 -12Q24 -12 22 0Q20 6 0 6Q-20 6 -22 0Z" fill="${c.color}" ${S(2.5)}/><ellipse cx="-4" cy="-7" rx="1.6" ry="1" fill="#1f2937" opacity=".6"/><ellipse cx="4" cy="-7" rx="1.6" ry="1" fill="#1f2937" opacity=".6"/><path d="M-14 -7Q-8 -10 -4 -10" stroke="#fff" stroke-width="2" fill="none" opacity=".5" stroke-linecap="round"/></g>`,
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
id: 'gasmask', label: 'Respirador', en: 'Respirator', pt: 'Respirador', group: 'face', anchor: 'mouth', color: '#64748b', color2: '#334155', clipExtra: true, as3d: 'facemask',
|
|
230
|
+
draw: (c) => `<g transform="scale(${n(c.m)})">${both((s) => `<circle cx="${s * 19}" cy="7" r="8" fill="${c.color2}" ${S(2)}/><circle cx="${s * 19}" cy="7" r="4" fill="none" stroke="${shade(c.color2, 0.3)}" stroke-width="1.5"/>`)}<path d="M-16 -8Q-16 -14 0 -14Q16 -14 16 -8L13 10Q0 18 -13 10Z" fill="${c.color}" ${S(2.5)}/>${[-1, 2, 5, 8].map((y) => `<path d="M-7 ${y}H7" stroke="${c.color2}" stroke-width="1.8" stroke-linecap="round"/>`).join('')}<path d="M-11 -10Q-6 -12 -2 -12" stroke="#fff" stroke-width="2" fill="none" opacity=".45" stroke-linecap="round"/></g>`,
|
|
231
|
+
extra: (c) => strap(c, -4 * c.m, 3, '#374151'),
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
// ── cuello ──
|
|
235
|
+
{
|
|
236
|
+
id: 'heartlocket', label: 'Colgante de corazón', en: 'Heart locket', pt: 'Pingente de coração', group: 'neck', anchor: 'neck', color: '#f43f5e', color2: '#facc15', as3d: 'medal',
|
|
237
|
+
draw: (c) => {
|
|
238
|
+
const N = 13;
|
|
239
|
+
const pts = Array.from({ length: N }, (_, i) => arc(c, 0.3, i, N).join(' ')).join('L');
|
|
240
|
+
return `<path d="M${pts}" stroke="${c.color2}" stroke-width="2" fill="none" stroke-dasharray="2 1.5"/><path d="${heartPath(8)}" transform="translate(0 14)" fill="${c.color}" ${S(2)}/><path d="M-4 10l2 -2" stroke="#fff" stroke-width="1.8" stroke-linecap="round" opacity=".7"/><circle cy="6" r="2" fill="${c.color2}" stroke="#1f2937" stroke-width="1"/>`;
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
id: 'goldchain', label: 'Cadena de oro', en: 'Gold chain', pt: 'Corrente de ouro', group: 'neck', anchor: 'neck', color: '#facc15', as3d: 'pearls',
|
|
245
|
+
draw: (c) => {
|
|
246
|
+
const N = 11;
|
|
247
|
+
let d = '';
|
|
248
|
+
for (let i = 0; i < N; i++) {
|
|
249
|
+
const [x, y] = arc(c, 0.32, i, N, 2);
|
|
250
|
+
const t = (i / (N - 1)) * Math.PI;
|
|
251
|
+
const rot = (Math.atan2(Math.cos(t) * 12, Math.sin(t) * c.w * 0.32) * 180) / Math.PI;
|
|
252
|
+
d += `<ellipse cx="${x}" cy="${y}" rx="${i % 2 ? 4.5 : 5.5}" ry="${i % 2 ? 2.4 : 3.4}" transform="rotate(${n(i % 2 ? rot + 0 : rot)} ${x} ${y})" fill="none" stroke="#1f2937" stroke-width="4"/><ellipse cx="${x}" cy="${y}" rx="${i % 2 ? 4.5 : 5.5}" ry="${i % 2 ? 2.4 : 3.4}" transform="rotate(${n(rot)} ${x} ${y})" fill="none" stroke="${c.color}" stroke-width="2.2"/>`;
|
|
253
|
+
}
|
|
254
|
+
return d;
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
id: 'neckerchief', label: 'Pañuelo al cuello', en: 'Neckerchief', pt: 'Lenço no pescoço', group: 'neck', anchor: 'neck', clip: true, color: '#dc2626', as3d: 'scarf',
|
|
259
|
+
draw: (c) => `<rect x="${n(-c.w)}" y="-7" width="${n(c.w * 2)}" height="11" fill="${c.color}"/><path d="M${n(-c.w)} -7H${n(c.w)}" stroke="#1f2937" stroke-width="1.5" opacity=".4"/>`,
|
|
260
|
+
extra: (c) => `<path d="M-18 -2L18 -2L0 24Z" fill="${c.color}" ${S(2)}/>${[[-6, 3], [6, 3], [0, 12], [-10, -1], [10, -1]].map(([x, y]) => `<circle cx="${x}" cy="${y}" r="1.6" fill="#fff"/>`).join('')}<circle cy="-2" r="4.5" fill="${shade(c.color, -0.2)}" ${S(2)}/>`,
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
id: 'lei', label: 'Collar de flores', en: 'Flower lei', pt: 'Colar de flores', group: 'neck', anchor: 'neck', color: '#f472b6', color2: '#fb923c', as3d: 'pearls',
|
|
264
|
+
draw: (c) => {
|
|
265
|
+
const N = 9;
|
|
266
|
+
return Array.from({ length: N }, (_, i) => { const [x, y] = arc(c, 0.34, i, N); return flower5(x, y, 4.2, i % 2 ? c.color2 : c.color); }).join('');
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: 'dogcollar', label: 'Collar con chapa', en: 'Pet collar', pt: 'Coleira', group: 'neck', anchor: 'neck', color: '#2563eb', color2: '#facc15', clip: true, as3d: 'bell',
|
|
271
|
+
draw: (c) => `<rect x="${n(-c.w)}" y="-6" width="${n(c.w * 2)}" height="10" fill="${c.color}"/><path d="M${n(-c.w)} -6H${n(c.w)}M${n(-c.w)} 4H${n(c.w)}" stroke="#1f2937" stroke-width="1.5" opacity=".45"/>${Array.from({ length: 9 }, (_, i) => `<circle cx="${n((i - 4) * c.w * 0.1)}" cy="-1" r="1.8" fill="#e5e7eb" stroke="#64748b" stroke-width=".8"/>`).join('')}`,
|
|
272
|
+
extra: (c) => `<circle cy="5" r="3" fill="none" stroke="#94a3b8" stroke-width="2"/><circle cy="14" r="7" fill="${c.color2}" ${S(2)}/><path d="M-3 12h6M-2 15h4" stroke="${shade(c.color2, -0.4)}" stroke-width="1.4" stroke-linecap="round"/>`,
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
id: 'ribbonbow', label: 'Lazo de cinta', en: 'Ribbon bow', pt: 'Laço de fita', group: 'neck', anchor: 'neck', color: '#f472b6', as3d: 'bowtie',
|
|
276
|
+
draw: (c) => `<path d="M-3 2L-12 26L-6 22L-2 28ZM3 2L12 26L6 22L2 28Z" fill="${shade(c.color, -0.1)}" ${S(2)}/>${both((s) => `<path d="M0 0Q${s * 10} -18 ${s * 24} -10Q${s * 30} 0 ${s * 24} 8Q${s * 10} 14 0 0Z" fill="${c.color}" ${S(2.5)}/><path d="M${s * 6} -3Q${s * 14} -10 ${s * 20} -6" stroke="${shade(c.color, -0.25)}" stroke-width="1.8" fill="none" stroke-linecap="round"/>`)}<ellipse rx="6" ry="7" fill="${shade(c.color, -0.12)}" ${S(2.5)}/><path d="M-2 -3q2 -1 3 0" stroke="#fff" stroke-width="1.5" fill="none" opacity=".7"/>`,
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
id: 'sailorcollar', label: 'Cuello marinero', en: 'Sailor collar', pt: 'Gola de marinheiro', group: 'neck', anchor: 'neck', color: '#1e3a8a', color2: '#dc2626', clip: true, as3d: 'ruff',
|
|
280
|
+
draw: (c) => `<path d="M${n(-c.w * 0.7)} -8H${n(c.w * 0.7)}V16L0 40L${n(-c.w * 0.7)} 16Z" fill="${c.color}"/><path d="M${n(-c.w * 0.26)} -8H${n(c.w * 0.26)}L0 24Z" fill="#fafafa"/><path d="M${n(-c.w * 0.32)} -8L0 30L${n(c.w * 0.32)} -8" stroke="#fafafa" stroke-width="2.5" fill="none"/><path d="M${n(-c.w * 0.7)} -8H${n(c.w * 0.7)}" stroke="#1f2937" stroke-width="2" opacity=".4"/>`,
|
|
281
|
+
extra: (c) => `<path d="M-3 24L-9 38M3 24L9 38" stroke="${c.color2}" stroke-width="4" stroke-linecap="round"/><path d="M-9 20L9 20L0 28Z" fill="${c.color2}" ${S(2)}/>`,
|
|
282
|
+
},
|
|
283
|
+
|
|
284
|
+
// ── espalda ──
|
|
285
|
+
{
|
|
286
|
+
id: 'butterflywings', label: 'Alas de mariposa', en: 'Butterfly wings', pt: 'Asas de borboleta', group: 'back', anchor: 'center', layer: 'back', color: '#f97316', color2: '#fde047', as3d: 'wings',
|
|
287
|
+
draw: (c) => both((s) => `<g transform="translate(${n(s * c.w * 0.36)} -8) scale(${s * 0.88} 0.88)"><path d="${BFLY_LO}" fill="${c.color2}" ${S(2.5)}/><path d="${BFLY_UP}" fill="${c.color}" ${S(2.5)}/><circle cx="40" cy="-24" r="7" fill="${c.color2}" stroke="#1f2937" stroke-width="2"/><circle cx="40" cy="-24" r="3" fill="#1f2937"/><circle cx="22" cy="-30" r="3" fill="#fff" opacity=".8"/><circle cx="26" cy="22" r="4" fill="#fff" opacity=".7"/></g>`),
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
id: 'fairywings', label: 'Alas de hada', en: 'Fairy wings', pt: 'Asas de fada', group: 'back', anchor: 'center', layer: 'back', color: '#a5f3fc', as3d: 'wings',
|
|
291
|
+
draw: (c) => both((s) => `<g transform="translate(${n(s * c.w * 0.34)} -10) scale(${s} 1)"><path d="${FAIRY_LO}" fill="${c.color}" fill-opacity=".7" stroke="${shade(c.color, -0.45)}" stroke-width="2.2" stroke-linejoin="round"/><path d="${FAIRY_UP}" fill="${c.color}" fill-opacity=".7" stroke="${shade(c.color, -0.45)}" stroke-width="2.2" stroke-linejoin="round"/><path d="M2 0Q22 -24 44 -52M2 6Q18 14 30 30" stroke="${shade(c.color, -0.3)}" stroke-width="1.4" fill="none" opacity=".8"/><path d="${star(3, 0.35, 4)}" transform="translate(30 -40)" fill="#fff"/><path d="${star(2.2, 0.35, 4)}" transform="translate(20 18)" fill="#fff"/></g>`),
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
id: 'dragonwings', label: 'Alas de dragón', en: 'Dragon wings', pt: 'Asas de dragão', group: 'back', anchor: 'center', layer: 'back', color: '#15803d', color2: '#86efac', as3d: 'batwings',
|
|
295
|
+
draw: (c) => both((s) => `<g transform="translate(${n(s * c.w * 0.38)} -10) scale(${s * 0.8} 0.8)"><path d="${DRAGON}" fill="${c.color2}" ${S(2.5)}/><path d="M0 2L16 -40M8 4L44 -50M16 6L68 -42" stroke="${c.color}" stroke-width="3.5" stroke-linecap="round"/><path d="M0 2L16 -40L28 -28L44 -50" stroke="#1f2937" stroke-width="2" fill="none" stroke-linejoin="round"/></g>`),
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
id: 'backpack', label: 'Mochila', en: 'Backpack', pt: 'Mochila', group: 'back', anchor: 'center', layer: 'back', color: '#f97316', color2: '#fde68a', as3d: 'jetpack',
|
|
299
|
+
draw: (c) => {
|
|
300
|
+
const W = c.w * 0.58;
|
|
301
|
+
const H = c.h * 0.32;
|
|
302
|
+
return `<path d="M-14 ${n(-H - 4)}Q0 ${n(-H - 22)} 14 ${n(-H - 4)}" stroke="#1f2937" stroke-width="7" fill="none"/><path d="M-14 ${n(-H - 4)}Q0 ${n(-H - 22)} 14 ${n(-H - 4)}" stroke="${shade(c.color, -0.2)}" stroke-width="4" fill="none"/><rect x="${n(-W)}" y="${n(-H)}" width="${n(W * 2)}" height="${n(H * 2)}" rx="${n(Math.min(W, H) * 0.45)}" fill="${c.color}" ${S(3)}/>${both((s) => `<rect x="${n(s > 0 ? W - 4 : -W - 10)}" y="${n(-H * 0.1)}" width="14" height="${n(H * 0.8)}" rx="5" fill="${c.color2}" ${S(2.5)}/><path d="M${n(s * (W + 3))} ${n(H * 0.05)}v${n(H * 0.25)}" stroke="#1f2937" stroke-width="1.5" opacity=".5"/>`)}`;
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
id: 'turtleshell', label: 'Caparazón', en: 'Turtle shell', pt: 'Casco de tartaruga', group: 'back', anchor: 'center', layer: 'back', color: '#65a30d', color2: '#d9f99d', as3d: 'jetpack',
|
|
307
|
+
draw: (c) => {
|
|
308
|
+
const rx = c.w * 0.6;
|
|
309
|
+
const ry = c.h * 0.54;
|
|
310
|
+
const hex = [[0, -0.55], [-0.62, -0.25], [0.62, -0.25], [-0.62, 0.35], [0.62, 0.35], [0, 0.6], [-0.9, 0.05], [0.9, 0.05]].map(([a, b]) => `<path d="${star(Math.min(rx, ry) * 0.24, 0.87, 3).replace(/./, 'M')}" transform="translate(${n(a * rx * 0.8)} ${n(b * ry * 0.8)})" fill="${shade(c.color, 0.15)}" stroke="${shade(c.color, -0.35)}" stroke-width="2"/>`).join('');
|
|
311
|
+
return `<g transform="translate(0 -6)"><ellipse rx="${n(rx + 6)}" ry="${n(ry + 6)}" fill="${c.color2}" ${S(3)}/><ellipse rx="${n(rx)}" ry="${n(ry)}" fill="${c.color}" stroke="${shade(c.color, -0.35)}" stroke-width="2.5"/>${hex}</g>`;
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
id: 'foxtail', label: 'Cola de zorro', en: 'Fox tail', pt: 'Cauda de raposa', group: 'back', anchor: 'bottom', layer: 'back', color: '#ea580c', color2: '#fafafa', grow: 'part', as3d: 'tail',
|
|
316
|
+
shape: (c) => [{ d: foxD(c) }],
|
|
317
|
+
draw: (c) => `<path d="${foxD(c)}" fill="${c.color}" ${S(2.5)}/><path d="M${n(c.w * 0.62)} -70Q${n(c.w * 0.82)} -56 ${n(c.w * 0.74)} -40Q${n(c.w * 0.64)} -48 ${n(c.w * 0.62)} -70Z" fill="${c.color2}" ${S(2)}/>`,
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
// ── extras ──
|
|
321
|
+
{
|
|
322
|
+
id: 'musicnotes', label: 'Notas musicales', en: 'Music notes', pt: 'Notas musicais', group: 'extra', anchor: 'top', color: '#8b5cf6', as3d: 'sparkles',
|
|
323
|
+
draw: (c) => note(-44, -6, 1, c.color) + note(34, -14, 0.85, shade(c.color, 0.2)) + note(52, -36, 0.65, c.color),
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
id: 'bubbles', label: 'Burbujas', en: 'Bubbles', pt: 'Bolhas', group: 'extra', anchor: 'center', color: '#7dd3fc', as3d: 'sparkles',
|
|
327
|
+
draw: (c) => [[-0.72, -0.5, 7], [-0.85, -0.2, 4], [0.78, -0.4, 9], [0.88, 0.05, 5], [0.7, 0.45, 4]].map(([a, b, r]) => `<g transform="translate(${n(a * Math.min(c.w, 112))} ${n(b * Math.min(c.h, 120))})"><circle r="${r}" fill="${c.color}" fill-opacity=".25" stroke="${shade(c.color, -0.25)}" stroke-width="1.6"/><path d="M${n(-r * 0.55)} ${n(-r * 0.1)}a${n(r * 0.6)} ${n(r * 0.6)} 0 0 1 ${n(r * 0.45)} ${n(-r * 0.45)}" stroke="#fff" stroke-width="1.6" fill="none" stroke-linecap="round"/></g>`).join(''),
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
id: 'butterfly', label: 'Mariposa', en: 'Butterfly', pt: 'Borboleta', group: 'extra', anchor: 'shoulder', color: '#f472b6', color2: '#facc15', as3d: 'parrot',
|
|
331
|
+
draw: (c) => `<g transform="translate(8 -26) rotate(12) scale(.32)">${both((s) => `<g transform="scale(${s} 1)"><path d="${BFLY_LO}" fill="${c.color2}" stroke="#1f2937" stroke-width="5" stroke-linejoin="round"/><path d="${BFLY_UP}" fill="${c.color}" stroke="#1f2937" stroke-width="5" stroke-linejoin="round"/><circle cx="36" cy="-22" r="8" fill="#fff" opacity=".8"/></g>`)}<ellipse rx="5" ry="22" cy="6" fill="#1f2937"/><path d="M-2 -14Q-8 -30 -16 -34M2 -14Q8 -30 16 -34" stroke="#1f2937" stroke-width="4" fill="none" stroke-linecap="round"/></g>`,
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
id: 'raincloud', label: 'Nubecita de lluvia', en: 'Rain cloud', pt: 'Nuvem de chuva', group: 'extra', anchor: 'top', color: '#94a3b8', color2: '#60a5fa', as3d: 'zzz',
|
|
335
|
+
draw: (c) => `<g transform="translate(26 -46)">${[[-6, 12], [0, 18], [6, 12], [12, 18]].map(([x, y]) => `<path d="M${x} ${y}q-3 5 0 7q3 -2 0 -7Z" fill="${c.color2}" stroke="#1e40af" stroke-width="1"/>`).join('')}<path d="M-16 8Q-24 8 -24 0Q-24 -8 -15 -8Q-14 -18 -3 -18Q6 -24 13 -15Q24 -16 24 -4Q30 0 26 6Q24 9 18 8Z" fill="${c.color}" ${S(2.5)}/><path d="M-14 -2Q-12 -9 -5 -10" stroke="#fff" stroke-width="2.5" fill="none" opacity=".6" stroke-linecap="round"/></g>`,
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
id: 'balloon', label: 'Globo', en: 'Balloon', pt: 'Balão', group: 'extra', anchor: 'shoulder', color: '#ef4444', as3d: 'parrot',
|
|
339
|
+
draw: (c) => `<path d="M0 0Q10 -20 2 -36Q-4 -48 10 -58" stroke="#1f2937" stroke-width="1.5" fill="none"/><g transform="translate(14 -78)"><ellipse rx="15" ry="18" fill="${c.color}" ${S(2.5)}/><path d="M-3 18l3 5l3 -5Z" fill="${shade(c.color, -0.2)}" ${S(1.5)}/><path d="M-8 -8Q-6 -14 0 -15" stroke="#fff" stroke-width="3" fill="none" opacity=".6" stroke-linecap="round"/></g>`,
|
|
340
|
+
},
|
|
341
|
+
];
|
|
342
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Catálogo 2 (2026-10): piezas nuevas, añadidas al final de su lista en parts.js.
|
|
3
|
+
* Cada pieza: { id, label (es), en, pt, as3d (id existente más parecido para el visor 3D), … }.
|
|
4
|
+
*
|
|
5
|
+
* Cejas: p = { r, side (-1 izq, 1 der), color, sp }, centradas sobre cada ojo. El lado interior está en x = -side.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** @param {object} h ayudantes de dibujo de parts.js */
|
|
9
|
+
export default ({ n, brow }) => [
|
|
10
|
+
{ id: 'wavy', label: 'Onduladas', en: 'Wavy', pt: 'Onduladas', as3d: 'thin', draw: (p) => brow(p, `M${n(-p.r * 0.9)} ${n(p.r * 0.05)}q${n(p.r * 0.225)} ${n(-p.r * 0.32)} ${n(p.r * 0.45)} 0t${n(p.r * 0.45)} 0t${n(p.r * 0.45)} 0t${n(p.r * 0.45)} 0`, 0.17) },
|
|
11
|
+
{ id: 'worried', label: 'Preocupadas', en: 'Worried', pt: 'Preocupadas', as3d: 'soft', draw: (p) => brow(p, `M${n(p.side * p.r * 0.85)} ${n(p.r * 0.2)}Q${n(p.side * p.r * 0.05)} ${n(-p.r * 0.05)} ${n(-p.side * p.r * 0.75)} ${n(-p.r * 0.45)}`, 0.22) },
|
|
12
|
+
{
|
|
13
|
+
id: 'fluffy', label: 'Esponjosas', en: 'Fluffy', pt: 'Fofinhas', as3d: 'bushy',
|
|
14
|
+
draw: (p) => [[-0.55, 0.05, 0.3], [-0.12, -0.12, 0.36], [0.35, -0.05, 0.33], [0.72, 0.1, 0.24]].map(([x, y, k]) => `<circle cx="${n(x * p.r * -p.side)}" cy="${n(y * p.r)}" r="${n(k * p.r)}" fill="${p.color}"/>`).join(''),
|
|
15
|
+
},
|
|
16
|
+
{ id: 'stub', label: 'Cortitas', en: 'Stubby', pt: 'Curtinhas', as3d: 'thick', draw: (p) => brow(p, `M${n(-p.r * 0.38)} 0H${n(p.r * 0.38)}`, 0.32) },
|
|
17
|
+
{ id: 'bolt', label: 'Rayo', en: 'Lightning', pt: 'Raio', as3d: 'angled', draw: (p) => `<path d="M${n(-p.r * 0.95)} ${n(p.r * 0.18)}L${n(-p.r * 0.35)} ${n(-p.r * 0.35)}L${n(-p.r * 0.05)} ${n(p.r * 0.1)}L${n(p.r * 0.45)} ${n(-p.r * 0.4)}L${n(p.r * 0.95)} ${n(-p.r * 0.02)}" stroke="${p.color}" stroke-width="${n(p.r * 0.21)}" fill="none" stroke-linecap="round" stroke-linejoin="round" transform="scale(${-p.side} 1)"/>` },
|
|
18
|
+
{
|
|
19
|
+
id: 'wedge', label: 'Dramáticas', en: 'Dramatic', pt: 'Dramáticas', as3d: 'angled',
|
|
20
|
+
draw: (p) => `<path d="M${n(p.side * p.r * 0.95)} ${n(-p.r * 0.32)}L${n(-p.side * p.r * 0.7)} ${n(p.r * 0.32)}L${n(-p.side * p.r * 0.8)} ${n(-p.r * 0.12)}Z" fill="${p.color}" stroke="${p.color}" stroke-width="${n(p.r * 0.14)}" stroke-linejoin="round"/>`,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'flame', label: 'Llamas', en: 'Flames', pt: 'Chamas', as3d: 'bushy',
|
|
24
|
+
draw: (p) => {
|
|
25
|
+
const r = p.r;
|
|
26
|
+
return `<path d="M${n(-r * 0.95)} ${n(r * 0.2)}Q${n(-r * 0.75)} ${n(-r * 0.2)} ${n(-r * 0.75)} ${n(-r * 0.5)}Q${n(-r * 0.45)} ${n(-r * 0.25)} ${n(-r * 0.3)} ${n(-r * 0.1)}Q${n(-r * 0.15)} ${n(-r * 0.5)} ${n(r * 0.05)} ${n(-r * 0.75)}Q${n(r * 0.25)} ${n(-r * 0.35)} ${n(r * 0.35)} ${n(-r * 0.12)}Q${n(r * 0.6)} ${n(-r * 0.3)} ${n(r * 0.7)} ${n(-r * 0.52)}Q${n(r * 1.0)} ${n(-r * 0.1)} ${n(r * 0.92)} ${n(r * 0.2)}Q0 ${n(r * 0.02)} ${n(-r * 0.95)} ${n(r * 0.2)}Z" fill="${p.color}" stroke="${p.color}" stroke-width="${n(r * 0.06)}" stroke-linejoin="round" transform="scale(${-p.side} 1)"/>`;
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'raised', label: 'Una levantada', en: 'One raised', pt: 'Uma levantada', as3d: 'arched',
|
|
31
|
+
draw: (p) => (p.side > 0
|
|
32
|
+
? brow(p, `M${n(-p.r * 0.85)} ${n(-p.r * 0.1)}Q${n(-p.r * 0.05)} ${n(-p.r * 1.05)} ${n(p.r * 0.9)} ${n(-p.r * 0.35)}`, 0.2)
|
|
33
|
+
: brow(p, `M${n(-p.r * 0.8)} ${n(p.r * 0.15)}L${n(p.r * 0.8)} ${n(p.r * 0.15)}`, 0.2)),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'feathery', label: 'Plumosas', en: 'Feathery', pt: 'Plumosas', as3d: 'bushy',
|
|
37
|
+
draw: (p) => [[-0.75, 0.15, 0.45], [-0.25, 0.05, 0.55], [0.25, 0.0, 0.55], [0.72, 0.08, 0.42]].map(([x, y, l], i) => `<path d="M${n(x * p.r)} ${n(y * p.r + p.r * 0.2)}q${n(p.r * 0.12)} ${n(-l * p.r)} ${n(p.r * (0.3 + i * 0.05))} ${n(-l * p.r * 0.9)}" stroke="${p.color}" stroke-width="${n(p.r * 0.18)}" fill="none" stroke-linecap="round" transform="scale(${-p.side} 1)"/>`).join(''),
|
|
38
|
+
},
|
|
39
|
+
];
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Catálogo 2 (2026-10): piezas nuevas, añadidas al final de su lista en parts.js.
|
|
3
|
+
* Cada pieza: { id, label (es), en, pt, as3d (id existente más parecido para el visor 3D), … }.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** @param {object} h ayudantes de dibujo de parts.js */
|
|
7
|
+
export default (h) => {
|
|
8
|
+
const { n, shade, star, heartPath } = h;
|
|
9
|
+
// p = { sp (media distancia entre ojos), r (radio ojo), color } — relativo al centro de los ojos
|
|
10
|
+
const both = (fn) => (p) => [-1, 1].map((s) => fn(p, s, s * (p.sp + p.r * 0.4), p.r * 1.8)).join('');
|
|
11
|
+
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
id: 'swirl', label: 'Espirales', en: 'Swirls', pt: 'Espirais', as3d: 'blush',
|
|
15
|
+
draw: both((p, s, x, y) => {
|
|
16
|
+
let d = '';
|
|
17
|
+
for (let t = 0; t <= 11; t += 0.4) {
|
|
18
|
+
const rr = (t / 11) * p.r * 0.62;
|
|
19
|
+
d += `${t ? 'L' : 'M'}${n(x + Math.cos(t * s) * rr * 1.25)} ${n(y + Math.sin(t * s) * rr * 0.8)}`;
|
|
20
|
+
}
|
|
21
|
+
return `<ellipse cx="${n(x)}" cy="${n(y)}" rx="${n(p.r * 0.8)}" ry="${n(p.r * 0.5)}" fill="${p.color}" opacity=".3"/><path d="${d}" stroke="${shade(p.color, -0.15)}" stroke-width="${n(p.r * 0.11)}" fill="none" stroke-linecap="round" opacity=".85"/>`;
|
|
22
|
+
}),
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'sparkle', label: 'Destellos', en: 'Sparkles', pt: 'Brilhos', as3d: 'stars',
|
|
26
|
+
draw: both((p, s, x, y) => `<path d="${star(p.r * 0.5, 0.22, 4)}" transform="translate(${n(x + s * p.r * 0.25)} ${n(y - p.r * 0.1)})" fill="#fff" stroke="#fde68a" stroke-width="${n(p.r * 0.05)}"/><path d="${star(p.r * 0.24, 0.25, 4)}" transform="translate(${n(x - s * p.r * 0.45)} ${n(y + p.r * 0.4)})" fill="#fff"/><circle cx="${n(x + s * p.r * 0.75)}" cy="${n(y + p.r * 0.45)}" r="${n(p.r * 0.08)}" fill="#fff"/>`),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'flowers', label: 'Florecitas', en: 'Flowers', pt: 'Florzinhas', as3d: 'hearts',
|
|
30
|
+
draw: both((p, s, x, y) => {
|
|
31
|
+
const pr = p.r * 0.2;
|
|
32
|
+
const cx = x + s * p.r * 0.15;
|
|
33
|
+
return `<g transform="translate(${n(cx)} ${n(y)})">${[0, 1, 2, 3, 4].map((i) => {
|
|
34
|
+
const a = (i * Math.PI * 2) / 5 - Math.PI / 2;
|
|
35
|
+
return `<circle cx="${n(Math.cos(a) * pr * 1.3)}" cy="${n(Math.sin(a) * pr * 1.3)}" r="${n(pr)}" fill="${p.color}"/>`;
|
|
36
|
+
}).join('')}<circle r="${n(pr * 0.85)}" fill="#facc15"/></g>`;
|
|
37
|
+
}),
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'glow', label: 'Rubor suave', en: 'Soft glow', pt: 'Rubor suave', as3d: 'blush',
|
|
41
|
+
draw: (p) => {
|
|
42
|
+
const id = `ck${Math.round(Math.random() * 1e9)}`;
|
|
43
|
+
return `<radialGradient id="${id}"><stop offset="0" stop-color="${p.color}" stop-opacity=".75"/><stop offset=".55" stop-color="${p.color}" stop-opacity=".35"/><stop offset="1" stop-color="${p.color}" stop-opacity="0"/></radialGradient>${[-1, 1].map((s) => `<ellipse cx="${n(s * (p.sp + p.r * 0.45))}" cy="${n(p.r * 1.8)}" rx="${n(p.r * 1.25)}" ry="${n(p.r * 0.85)}" fill="url(#${id})"/>`).join('')}`;
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'tribal', label: 'Triángulos', en: 'Tribal marks', pt: 'Triângulos', as3d: 'paint',
|
|
48
|
+
draw: both((p, s, x, y) => [0, 1, 2].map((i) => {
|
|
49
|
+
const cx = x - s * p.r * 0.25 + s * i * p.r * 0.48;
|
|
50
|
+
return `<path d="M${n(cx - p.r * 0.2)} ${n(y - p.r * 0.3)}L${n(cx + p.r * 0.2)} ${n(y - p.r * 0.3)}L${n(cx)} ${n(y + p.r * 0.3)}Z" fill="${shade(p.color, -0.15)}" stroke="${shade(p.color, -0.15)}" stroke-width="${n(p.r * 0.08)}" stroke-linejoin="round"/>`;
|
|
51
|
+
}).join('')),
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'pixel', label: 'Rubor píxel', en: 'Pixel blush', pt: 'Rubor pixel', as3d: 'blush',
|
|
55
|
+
draw: both((p, s, x, y) => {
|
|
56
|
+
const q = p.r * 0.32;
|
|
57
|
+
const cells = [[-1.5, -0.5], [-0.5, -0.5], [0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [1.5, 0.5], [-1.5, 0.5], [1.5, -0.5]];
|
|
58
|
+
return cells.map(([a, b], i) => `<rect x="${n(x + a * q - q / 2)}" y="${n(y + b * q - q / 2)}" width="${n(q)}" height="${n(q)}" fill="${p.color}" opacity="${i < 6 ? 0.6 : 0.35}"/>`).join('');
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'mole', label: 'Lunar', en: 'Beauty mark', pt: 'Pinta', as3d: 'freckles',
|
|
63
|
+
draw: (p) => `<circle cx="${n(p.sp + p.r * 0.9)}" cy="${n(p.r * 2.4)}" r="${n(p.r * 0.15)}" fill="${shade(p.color, -0.7)}"/>`,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: 'shy', label: 'Vergüenza', en: 'Shy', pt: 'Vergonha', as3d: 'lines',
|
|
67
|
+
draw: both((p, s, x, y) => `<ellipse cx="${n(x)}" cy="${n(y)}" rx="${n(p.r * 0.85)}" ry="${n(p.r * 0.48)}" fill="${p.color}" opacity=".45"/>${[0, 1, 2, 3].map((i) => `<path d="M${n(x + (i - 1.5) * p.r * 0.36 - p.r * 0.1)} ${n(y + p.r * 0.2)}l${n(p.r * 0.2)} ${n(-p.r * 0.4)}" stroke="${shade(p.color, -0.3)}" stroke-width="${n(p.r * 0.09)}" stroke-linecap="round" opacity=".8"/>`).join('')}`),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: 'doll', label: 'Muñeca', en: 'Doll cheeks', pt: 'Boneca', as3d: 'blush',
|
|
71
|
+
draw: both((p, s, x, y) => `<circle cx="${n(x)}" cy="${n(y)}" r="${n(p.r * 0.55)}" fill="${p.color}" opacity=".85"/><circle cx="${n(x - p.r * 0.18)}" cy="${n(y - p.r * 0.18)}" r="${n(p.r * 0.13)}" fill="#fff" opacity=".7"/>`),
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'heartblush', label: 'Rubor de amor', en: 'Love blush', pt: 'Rubor apaixonado', as3d: 'hearts',
|
|
75
|
+
draw: both((p, s, x, y) => `<ellipse cx="${n(x)}" cy="${n(y)}" rx="${n(p.r * 0.85)}" ry="${n(p.r * 0.48)}" fill="${p.color}" opacity=".5"/><path d="${heartPath(p.r * 0.22)}" transform="translate(${n(x + s * p.r * 0.75)} ${n(y - p.r * 0.55)}) rotate(${s * 15})" fill="#f43f5e"/>`),
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
};
|