@dotmon/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/chunk-QBHMZDDB.js +358 -0
- package/dist/index.cjs +369 -0
- package/dist/index.d.cts +27 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +1 -0
- package/dist/locales/en.cjs +29 -0
- package/dist/locales/en.d.cts +7 -0
- package/dist/locales/en.d.ts +7 -0
- package/dist/locales/en.js +27 -0
- package/dist/locales/index.cjs +13 -0
- package/dist/locales/index.d.cts +11 -0
- package/dist/locales/index.d.ts +11 -0
- package/dist/locales/index.js +11 -0
- package/dist/locales/ja.cjs +29 -0
- package/dist/locales/ja.d.cts +7 -0
- package/dist/locales/ja.d.ts +7 -0
- package/dist/locales/ja.js +27 -0
- package/dist/render/index.cjs +628 -0
- package/dist/render/index.d.cts +42 -0
- package/dist/render/index.d.ts +42 -0
- package/dist/render/index.js +348 -0
- package/dist/stats-BMnyxbee.d.ts +17 -0
- package/dist/stats-Cs0yRJTk.d.cts +17 -0
- package/dist/types-CsVv0MnX.d.cts +12 -0
- package/dist/types-DYNij_HS.d.ts +12 -0
- package/dist/types-kWKrAd-B.d.cts +27 -0
- package/dist/types-kWKrAd-B.d.ts +27 -0
- package/package.json +67 -0
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/render/raster.ts
|
|
4
|
+
function svgToCanvas(svg, size, background = "transparent") {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const sized = svg.replace("<svg ", `<svg width="${size}" height="${size}" `);
|
|
7
|
+
const img = new Image();
|
|
8
|
+
img.onload = () => {
|
|
9
|
+
const c = document.createElement("canvas");
|
|
10
|
+
c.width = size;
|
|
11
|
+
c.height = size;
|
|
12
|
+
const ctx = c.getContext("2d");
|
|
13
|
+
ctx.imageSmoothingEnabled = false;
|
|
14
|
+
if (background !== "transparent") {
|
|
15
|
+
ctx.fillStyle = background;
|
|
16
|
+
ctx.fillRect(0, 0, size, size);
|
|
17
|
+
}
|
|
18
|
+
ctx.drawImage(img, 0, 0, size, size);
|
|
19
|
+
resolve(c);
|
|
20
|
+
};
|
|
21
|
+
img.onerror = reject;
|
|
22
|
+
img.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(sized);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async function toPng(svg, opts = {}) {
|
|
26
|
+
const { size = 512, background = "transparent" } = opts;
|
|
27
|
+
const c = await svgToCanvas(svg, size, background);
|
|
28
|
+
return new Promise((res) => c.toBlob((b) => res(b), "image/png"));
|
|
29
|
+
}
|
|
30
|
+
async function pngBytes(svg, size, background) {
|
|
31
|
+
const blob = await toPng(svg, { size, background });
|
|
32
|
+
return new Uint8Array(await blob.arrayBuffer());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/render/gif.ts
|
|
36
|
+
function lzwEncode(minCodeSize, indices) {
|
|
37
|
+
const CLEAR = 1 << minCodeSize;
|
|
38
|
+
const EOI = CLEAR + 1;
|
|
39
|
+
let dict;
|
|
40
|
+
let nextCode = 0;
|
|
41
|
+
let codeSize = 0;
|
|
42
|
+
const out = [];
|
|
43
|
+
let bitBuf = 0;
|
|
44
|
+
let bitLen = 0;
|
|
45
|
+
const emit = (code) => {
|
|
46
|
+
bitBuf |= code << bitLen;
|
|
47
|
+
bitLen += codeSize;
|
|
48
|
+
while (bitLen >= 8) {
|
|
49
|
+
out.push(bitBuf & 255);
|
|
50
|
+
bitBuf >>= 8;
|
|
51
|
+
bitLen -= 8;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const init = () => {
|
|
55
|
+
dict = /* @__PURE__ */ new Map();
|
|
56
|
+
for (let i = 0; i < CLEAR; i++) dict.set(String(i), i);
|
|
57
|
+
nextCode = EOI + 1;
|
|
58
|
+
codeSize = minCodeSize + 1;
|
|
59
|
+
};
|
|
60
|
+
init();
|
|
61
|
+
emit(CLEAR);
|
|
62
|
+
let prefix = String(indices[0]);
|
|
63
|
+
for (let i = 1; i < indices.length; i++) {
|
|
64
|
+
const k = indices[i];
|
|
65
|
+
const key = prefix + "," + k;
|
|
66
|
+
if (dict.has(key)) {
|
|
67
|
+
prefix = key;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
emit(dict.get(prefix));
|
|
71
|
+
if (nextCode < 4096) {
|
|
72
|
+
dict.set(key, nextCode);
|
|
73
|
+
if (nextCode === 1 << codeSize && codeSize < 12) codeSize++;
|
|
74
|
+
nextCode++;
|
|
75
|
+
} else {
|
|
76
|
+
emit(CLEAR);
|
|
77
|
+
init();
|
|
78
|
+
}
|
|
79
|
+
prefix = String(k);
|
|
80
|
+
}
|
|
81
|
+
emit(dict.get(prefix));
|
|
82
|
+
emit(EOI);
|
|
83
|
+
if (bitLen > 0) out.push(bitBuf & 255);
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
function encodeGif(w, h, palette, transparentIndex, frames, delayCs) {
|
|
87
|
+
const bytes = [];
|
|
88
|
+
const put = (b) => bytes.push(b & 255);
|
|
89
|
+
const put16 = (v) => {
|
|
90
|
+
put(v);
|
|
91
|
+
put(v >> 8);
|
|
92
|
+
};
|
|
93
|
+
const putStr = (s) => {
|
|
94
|
+
for (let i = 0; i < s.length; i++) put(s.charCodeAt(i));
|
|
95
|
+
};
|
|
96
|
+
let bits = 1;
|
|
97
|
+
while (1 << bits < palette.length) bits++;
|
|
98
|
+
putStr("GIF89a");
|
|
99
|
+
put16(w);
|
|
100
|
+
put16(h);
|
|
101
|
+
put(128 | 112 | bits - 1);
|
|
102
|
+
put(0);
|
|
103
|
+
put(0);
|
|
104
|
+
for (let i = 0; i < 1 << bits; i++) {
|
|
105
|
+
const c = palette[i] || [0, 0, 0];
|
|
106
|
+
put(c[0]);
|
|
107
|
+
put(c[1]);
|
|
108
|
+
put(c[2]);
|
|
109
|
+
}
|
|
110
|
+
put(33);
|
|
111
|
+
put(255);
|
|
112
|
+
put(11);
|
|
113
|
+
putStr("NETSCAPE2.0");
|
|
114
|
+
put(3);
|
|
115
|
+
put(1);
|
|
116
|
+
put16(0);
|
|
117
|
+
put(0);
|
|
118
|
+
const minCode = Math.max(2, bits);
|
|
119
|
+
for (const indices of frames) {
|
|
120
|
+
put(33);
|
|
121
|
+
put(249);
|
|
122
|
+
put(4);
|
|
123
|
+
put(2 << 2 | (transparentIndex >= 0 ? 1 : 0));
|
|
124
|
+
put16(delayCs);
|
|
125
|
+
put(transparentIndex >= 0 ? transparentIndex : 0);
|
|
126
|
+
put(0);
|
|
127
|
+
put(44);
|
|
128
|
+
put16(0);
|
|
129
|
+
put16(0);
|
|
130
|
+
put16(w);
|
|
131
|
+
put16(h);
|
|
132
|
+
put(0);
|
|
133
|
+
put(minCode);
|
|
134
|
+
const data = lzwEncode(minCode, indices);
|
|
135
|
+
for (let i = 0; i < data.length; i += 255) {
|
|
136
|
+
const n = Math.min(255, data.length - i);
|
|
137
|
+
put(n);
|
|
138
|
+
for (let j = 0; j < n; j++) put(data[i + j]);
|
|
139
|
+
}
|
|
140
|
+
put(0);
|
|
141
|
+
}
|
|
142
|
+
put(59);
|
|
143
|
+
return new Uint8Array(bytes);
|
|
144
|
+
}
|
|
145
|
+
async function toGif(frames, opts = {}) {
|
|
146
|
+
const { size = 256, background = "transparent", delayMs = 400 } = opts;
|
|
147
|
+
const bytes = await gifBytes(frames[0], frames[1], size, background, Math.round(delayMs / 10));
|
|
148
|
+
return new Blob([bytes], { type: "image/gif" });
|
|
149
|
+
}
|
|
150
|
+
async function gifBytes(svgA, svgB, px, bg, delayCs = 40) {
|
|
151
|
+
const canvases = [await svgToCanvas(svgA, px, bg), await svgToCanvas(svgB, px, bg)];
|
|
152
|
+
const palette = [];
|
|
153
|
+
const cmap = /* @__PURE__ */ new Map();
|
|
154
|
+
let transparentIndex = -1;
|
|
155
|
+
if (bg === "transparent") {
|
|
156
|
+
transparentIndex = 0;
|
|
157
|
+
palette.push([0, 0, 0]);
|
|
158
|
+
}
|
|
159
|
+
const frames = canvases.map((c) => {
|
|
160
|
+
const d = c.getContext("2d").getImageData(0, 0, px, px).data;
|
|
161
|
+
const out = new Uint8Array(px * px);
|
|
162
|
+
for (let i = 0; i < px * px; i++) {
|
|
163
|
+
const r = d[i * 4], gr = d[i * 4 + 1], bl = d[i * 4 + 2], a = d[i * 4 + 3];
|
|
164
|
+
if (a < 128 && transparentIndex >= 0) {
|
|
165
|
+
out[i] = transparentIndex;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
const key = r << 16 | gr << 8 | bl;
|
|
169
|
+
let idx = cmap.get(key);
|
|
170
|
+
if (idx === void 0) {
|
|
171
|
+
idx = palette.length;
|
|
172
|
+
palette.push([r, gr, bl]);
|
|
173
|
+
cmap.set(key, idx);
|
|
174
|
+
}
|
|
175
|
+
out[i] = idx;
|
|
176
|
+
}
|
|
177
|
+
return out;
|
|
178
|
+
});
|
|
179
|
+
return encodeGif(px, px, palette, transparentIndex, frames, delayCs);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/render/zip.ts
|
|
183
|
+
var CRC_TABLE = (() => {
|
|
184
|
+
const t = new Uint32Array(256);
|
|
185
|
+
for (let n = 0; n < 256; n++) {
|
|
186
|
+
let c = n;
|
|
187
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
188
|
+
t[n] = c;
|
|
189
|
+
}
|
|
190
|
+
return t;
|
|
191
|
+
})();
|
|
192
|
+
function crc32(data) {
|
|
193
|
+
let c = 4294967295;
|
|
194
|
+
for (let i = 0; i < data.length; i++) c = CRC_TABLE[(c ^ data[i]) & 255] ^ c >>> 8;
|
|
195
|
+
return (c ^ 4294967295) >>> 0;
|
|
196
|
+
}
|
|
197
|
+
function buildZip(files) {
|
|
198
|
+
const enc = new TextEncoder();
|
|
199
|
+
const u16 = (v) => [v & 255, v >> 8 & 255];
|
|
200
|
+
const u32 = (v) => [v & 255, v >>> 8 & 255, v >>> 16 & 255, v >>> 24 & 255];
|
|
201
|
+
const now = /* @__PURE__ */ new Date();
|
|
202
|
+
const dosTime = (now.getHours() << 11 | now.getMinutes() << 5 | now.getSeconds() >> 1) & 65535;
|
|
203
|
+
const dosDate = (now.getFullYear() - 1980 << 9 | now.getMonth() + 1 << 5 | now.getDate()) & 65535;
|
|
204
|
+
const chunks = [];
|
|
205
|
+
const central = [];
|
|
206
|
+
let offset = 0;
|
|
207
|
+
for (const f of files) {
|
|
208
|
+
const name = enc.encode(f.name);
|
|
209
|
+
const crc = crc32(f.data);
|
|
210
|
+
const head = new Uint8Array([80, 75, 3, 4, ...u16(20), ...u16(2048), ...u16(0), ...u16(dosTime), ...u16(dosDate), ...u32(crc), ...u32(f.data.length), ...u32(f.data.length), ...u16(name.length), ...u16(0)]);
|
|
211
|
+
chunks.push(head, name, f.data);
|
|
212
|
+
central.push({ name, crc, size: f.data.length, offset });
|
|
213
|
+
offset += head.length + name.length + f.data.length;
|
|
214
|
+
}
|
|
215
|
+
const cd = [];
|
|
216
|
+
let cdSize = 0;
|
|
217
|
+
for (const c of central) {
|
|
218
|
+
const rec = new Uint8Array([80, 75, 1, 2, ...u16(20), ...u16(20), ...u16(2048), ...u16(0), ...u16(dosTime), ...u16(dosDate), ...u32(c.crc), ...u32(c.size), ...u32(c.size), ...u16(c.name.length), ...u16(0), ...u16(0), ...u16(0), ...u16(0), ...u32(0), ...u32(c.offset)]);
|
|
219
|
+
cd.push(rec, c.name);
|
|
220
|
+
cdSize += rec.length + c.name.length;
|
|
221
|
+
}
|
|
222
|
+
const eocd = new Uint8Array([80, 75, 5, 6, ...u16(0), ...u16(0), ...u16(files.length), ...u16(files.length), ...u32(cdSize), ...u32(offset), ...u16(0)]);
|
|
223
|
+
const parts = [...chunks, ...cd, eocd].map((p) => p instanceof Uint8Array ? p : new Uint8Array(p));
|
|
224
|
+
return new Blob(parts, { type: "application/zip" });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// src/hash.ts
|
|
228
|
+
function fnv1a(str) {
|
|
229
|
+
let hash = 2166136261;
|
|
230
|
+
for (let i = 0; i < str.length; i++) {
|
|
231
|
+
hash ^= str.charCodeAt(i);
|
|
232
|
+
hash = Math.imul(hash, 16777619);
|
|
233
|
+
}
|
|
234
|
+
return hash >>> 0;
|
|
235
|
+
}
|
|
236
|
+
function mulberry32(seed) {
|
|
237
|
+
let a = seed;
|
|
238
|
+
return () => {
|
|
239
|
+
a |= 0;
|
|
240
|
+
a = a + 1831565813 | 0;
|
|
241
|
+
let t = Math.imul(a ^ a >>> 15, 1 | a);
|
|
242
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
243
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// src/presets.ts
|
|
248
|
+
var presets = {
|
|
249
|
+
mochi: { connected: true, symmetric: true, outline: true, face: true, legs: "auto" },
|
|
250
|
+
retro: { connected: false, symmetric: true, outline: true, face: false, legs: "none" },
|
|
251
|
+
chaos: { connected: false, symmetric: false, outline: true, face: true, legs: "none" }
|
|
252
|
+
};
|
|
253
|
+
function resolveOptions(options = {}) {
|
|
254
|
+
const base = presets[options.preset ?? "mochi"];
|
|
255
|
+
return {
|
|
256
|
+
connected: options.connected ?? base.connected,
|
|
257
|
+
symmetric: options.symmetric ?? base.symmetric,
|
|
258
|
+
outline: options.outline ?? base.outline,
|
|
259
|
+
face: options.face ?? base.face,
|
|
260
|
+
legs: options.legs ?? base.legs
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/generate.ts
|
|
265
|
+
var SIZE = 16;
|
|
266
|
+
var HALF = 8;
|
|
267
|
+
function generateSvg(seed, options = {}) {
|
|
268
|
+
const o = resolveOptions(options);
|
|
269
|
+
const view = options.view ?? "front";
|
|
270
|
+
const frame = options.frame ?? 0;
|
|
271
|
+
const rand = mulberry32(fnv1a(seed));
|
|
272
|
+
const pick = (arr) => arr[Math.floor(rand() * arr.length)];
|
|
273
|
+
const g = Array.from({ length: SIZE }, () => new Array(SIZE).fill(0));
|
|
274
|
+
const hue = Math.floor(rand() * 12) * 30;
|
|
275
|
+
const sat = 55 + rand() * 25;
|
|
276
|
+
const lig = 55 + rand() * 12;
|
|
277
|
+
const top = 2 + Math.floor(rand() * 2);
|
|
278
|
+
const bottom = 12 + Math.floor(rand() * 2);
|
|
279
|
+
const height = bottom - top + 1;
|
|
280
|
+
if (o.connected) {
|
|
281
|
+
const widths = [];
|
|
282
|
+
let w = 2 + Math.floor(rand() * 2);
|
|
283
|
+
for (let i = 0; i < height; i++) {
|
|
284
|
+
const progress = i / height;
|
|
285
|
+
let step;
|
|
286
|
+
const r = rand();
|
|
287
|
+
if (progress < 0.6) step = r < 0.55 ? 1 : r < 0.85 ? 0 : -1;
|
|
288
|
+
else step = r < 0.35 ? 1 : r < 0.6 ? 0 : -1;
|
|
289
|
+
w = Math.max(1, Math.min(7, w + step));
|
|
290
|
+
widths.push(w);
|
|
291
|
+
}
|
|
292
|
+
for (let i = 1; i < height - 1; i++) {
|
|
293
|
+
if (rand() < 0.18 && widths[i] > 2) widths[i] -= 1;
|
|
294
|
+
}
|
|
295
|
+
for (let i = 0; i < height; i++) {
|
|
296
|
+
for (let x = HALF - widths[i]; x < HALF; x++) g[top + i][x] = 1;
|
|
297
|
+
}
|
|
298
|
+
} else {
|
|
299
|
+
for (let y = top; y <= bottom; y++) {
|
|
300
|
+
for (let x = 1; x < HALF; x++) {
|
|
301
|
+
if (rand() < 0.45) g[y][x] = 1;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
for (let y = 0; y < SIZE; y++) {
|
|
306
|
+
for (let x = 0; x < HALF; x++) {
|
|
307
|
+
if (o.symmetric) {
|
|
308
|
+
g[y][SIZE - 1 - x] = g[y][x];
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (!o.symmetric) {
|
|
313
|
+
for (let y = top; y <= bottom; y++) {
|
|
314
|
+
for (let x = HALF; x < SIZE - 1; x++) {
|
|
315
|
+
g[y][x] = rand() < 0.45 ? 1 : 0;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (o.connected && o.symmetric && rand() < 0.55) {
|
|
320
|
+
const hornStyle = pick(["stub", "long", "ear"]);
|
|
321
|
+
let topRow = top;
|
|
322
|
+
while (topRow <= bottom && !g[topRow].some((v) => v)) topRow++;
|
|
323
|
+
const xs = [];
|
|
324
|
+
for (let x = 0; x < HALF; x++) if (g[topRow][x]) xs.push(x);
|
|
325
|
+
if (xs.length) {
|
|
326
|
+
const hx = xs[0] + (xs.length > 2 ? 1 : 0);
|
|
327
|
+
const len = hornStyle === "long" ? 2 : 1;
|
|
328
|
+
for (let d = 1; d <= len; d++) {
|
|
329
|
+
if (topRow - d >= 0) {
|
|
330
|
+
const col = hornStyle === "ear" ? 1 : 5;
|
|
331
|
+
g[topRow - d][hx] = col;
|
|
332
|
+
g[topRow - d][SIZE - 1 - hx] = col;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (o.face) {
|
|
338
|
+
let topRow = 0;
|
|
339
|
+
while (topRow < SIZE && !g[topRow].some((v) => v === 1)) topRow++;
|
|
340
|
+
let botRow = SIZE - 1;
|
|
341
|
+
while (botRow > 0 && !g[botRow].some((v) => v === 1)) botRow--;
|
|
342
|
+
const eyeRow = Math.min(botRow - 2, topRow + Math.max(1, Math.round((botRow - topRow) * 0.32)));
|
|
343
|
+
const cols = [];
|
|
344
|
+
for (let x = 0; x < SIZE; x++) if (g[eyeRow][x] === 1) cols.push(x);
|
|
345
|
+
if (cols.length >= 1) {
|
|
346
|
+
const side = view === "left" || view === "right";
|
|
347
|
+
const cx = (SIZE - 1) / 2;
|
|
348
|
+
const eyeStyle = pick(["dot", "wide", "sleepy"]);
|
|
349
|
+
const stamp = (x) => {
|
|
350
|
+
if (view === "back") return;
|
|
351
|
+
if (eyeStyle === "dot") {
|
|
352
|
+
g[eyeRow][x] = 4;
|
|
353
|
+
} else if (eyeStyle === "wide") {
|
|
354
|
+
g[eyeRow][x] = 3;
|
|
355
|
+
if (g[eyeRow + 1]) g[eyeRow + 1][x] = 4;
|
|
356
|
+
} else {
|
|
357
|
+
g[eyeRow][x] = 2;
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
const twoEyes = cols.length >= 4;
|
|
361
|
+
if (side) {
|
|
362
|
+
stamp(cols[Math.min(1, cols.length - 1)]);
|
|
363
|
+
} else if (twoEyes) {
|
|
364
|
+
const gap = Math.max(1, Math.min(3, Math.floor(cols.length / 3)));
|
|
365
|
+
stamp(Math.floor(cx - gap));
|
|
366
|
+
stamp(Math.ceil(cx + gap));
|
|
367
|
+
} else {
|
|
368
|
+
stamp(Math.round(cx));
|
|
369
|
+
}
|
|
370
|
+
const mRow = eyeRow + 2 + (eyeStyle === "wide" ? 1 : 0);
|
|
371
|
+
const mW = twoEyes ? pick([1, 1, 3]) : 1;
|
|
372
|
+
if (view !== "back" && mRow <= botRow) {
|
|
373
|
+
if (side) {
|
|
374
|
+
let m0 = 0;
|
|
375
|
+
while (m0 < SIZE && g[mRow][m0] !== 1) m0++;
|
|
376
|
+
for (let x = m0; x < m0 + mW && x < SIZE; x++) {
|
|
377
|
+
if (g[mRow][x] === 1) g[mRow][x] = 4;
|
|
378
|
+
}
|
|
379
|
+
} else {
|
|
380
|
+
for (let x = Math.round(cx - (mW - 1) / 2); x <= Math.round(cx + (mW - 1) / 2); x++) {
|
|
381
|
+
if (g[mRow][x] === 1) g[mRow][x] = 4;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
const legCols = [];
|
|
388
|
+
let drawnLegStyle = null;
|
|
389
|
+
if (o.connected && o.symmetric) {
|
|
390
|
+
const rolled = pick(["none", "two", "two", "two", "many"]);
|
|
391
|
+
const legStyle = o.legs === "auto" ? rolled : o.legs;
|
|
392
|
+
if (legStyle !== "none") {
|
|
393
|
+
let botRow = SIZE - 1;
|
|
394
|
+
while (botRow > 0 && !g[botRow].some((v) => v === 1)) botRow--;
|
|
395
|
+
const maxLen = SIZE - 2 - botRow;
|
|
396
|
+
const xs = [];
|
|
397
|
+
for (let x = 0; x < HALF; x++) if (g[botRow][x] === 1) xs.push(x);
|
|
398
|
+
if (xs.length && maxLen >= 1) {
|
|
399
|
+
const draw = (x, len) => {
|
|
400
|
+
for (let d = 1; d <= len; d++) {
|
|
401
|
+
g[botRow + d][x] = 1;
|
|
402
|
+
g[botRow + d][SIZE - 1 - x] = 1;
|
|
403
|
+
}
|
|
404
|
+
legCols.push({ x, len, botRow });
|
|
405
|
+
};
|
|
406
|
+
if (legStyle === "two") {
|
|
407
|
+
const lx = xs.length > 2 ? xs[1] : xs[0];
|
|
408
|
+
const len = Math.min(maxLen, 1 + Math.floor(rand() * 2));
|
|
409
|
+
if (view === "left" || view === "right") {
|
|
410
|
+
for (let d = 1; d <= len; d++) g[botRow + d][7] = 1;
|
|
411
|
+
legCols.push({ x: 7, len, botRow });
|
|
412
|
+
} else {
|
|
413
|
+
draw(lx, len);
|
|
414
|
+
}
|
|
415
|
+
drawnLegStyle = "two";
|
|
416
|
+
} else {
|
|
417
|
+
for (let i = 0; i < xs.length; i += 2) {
|
|
418
|
+
const len = Math.min(maxLen, rand() < 0.5 ? 2 : 1);
|
|
419
|
+
draw(xs[i], len);
|
|
420
|
+
}
|
|
421
|
+
drawnLegStyle = "many";
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const drawn = drawnLegStyle ?? "none";
|
|
427
|
+
if (frame > 0) {
|
|
428
|
+
if (drawnLegStyle === "two" && legCols.length) {
|
|
429
|
+
const c = legCols[0];
|
|
430
|
+
if (frame === 1) g[c.botRow + c.len][c.x] = 0;
|
|
431
|
+
else g[c.botRow + c.len][SIZE - 1 - c.x] = 0;
|
|
432
|
+
} else if (drawnLegStyle === "many" && legCols.length > 1) {
|
|
433
|
+
for (let i = frame === 1 ? 1 : 0; i < legCols.length; i += 2) {
|
|
434
|
+
const c = legCols[i];
|
|
435
|
+
g[c.botRow + c.len][c.x] = 0;
|
|
436
|
+
g[c.botRow + c.len][SIZE - 1 - c.x] = 0;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (frame === 2) {
|
|
440
|
+
let tR = 0;
|
|
441
|
+
while (tR < SIZE && !g[tR].some((v) => v)) tR++;
|
|
442
|
+
let bR = SIZE - 1;
|
|
443
|
+
while (bR > 0 && !g[bR].some((v) => v)) bR--;
|
|
444
|
+
if (bR - tR > 3) {
|
|
445
|
+
const mid = Math.floor((tR + bR) / 2);
|
|
446
|
+
for (let y = mid; y > tR; y--) g[y] = g[y - 1].slice();
|
|
447
|
+
g[tR] = new Array(SIZE).fill(0);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (o.outline) {
|
|
452
|
+
const isBody = (y, x) => y >= 0 && y < SIZE && x >= 0 && x < SIZE && g[y][x] !== 0 && g[y][x] !== 9;
|
|
453
|
+
const marks = [];
|
|
454
|
+
for (let y = 0; y < SIZE; y++) {
|
|
455
|
+
for (let x = 0; x < SIZE; x++) {
|
|
456
|
+
if (g[y][x] === 0 && (isBody(y - 1, x) || isBody(y + 1, x) || isBody(y, x - 1) || isBody(y, x + 1))) {
|
|
457
|
+
marks.push([y, x]);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
for (const [y, x] of marks) g[y][x] = 9;
|
|
462
|
+
}
|
|
463
|
+
if (view === "right") for (const row of g) row.reverse();
|
|
464
|
+
const colors = {
|
|
465
|
+
1: `hsl(${hue} ${sat}% ${lig}%)`,
|
|
466
|
+
2: `hsl(${hue} ${sat}% ${Math.max(18, lig - 24)}%)`,
|
|
467
|
+
3: "#fdfdf5",
|
|
468
|
+
4: "#1a1a24",
|
|
469
|
+
5: `hsl(${(hue + 165) % 360} ${sat}% ${lig}%)`,
|
|
470
|
+
9: "#1a1a24"
|
|
471
|
+
};
|
|
472
|
+
let rects = "";
|
|
473
|
+
for (let y = 0; y < SIZE; y++) {
|
|
474
|
+
let x = 0;
|
|
475
|
+
while (x < SIZE) {
|
|
476
|
+
const v = g[y][x];
|
|
477
|
+
if (v === 0) {
|
|
478
|
+
x++;
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
let x2 = x;
|
|
482
|
+
while (x2 + 1 < SIZE && g[y][x2 + 1] === v) x2++;
|
|
483
|
+
rects += `<rect x="${x}" y="${y}" width="${x2 - x + 1}" height="1" fill="${colors[v]}"/>`;
|
|
484
|
+
x = x2 + 1;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" shape-rendering="crispEdges">${rects}</svg>`;
|
|
488
|
+
return { svg, drawnLegStyle: drawn, size: SIZE };
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/render/sheet.ts
|
|
492
|
+
var SHEET_POSES = [
|
|
493
|
+
["idle", 0],
|
|
494
|
+
["walk1", 1],
|
|
495
|
+
["walk2", 2]
|
|
496
|
+
];
|
|
497
|
+
var SHEET_ROWS = ["front", "left", "right", "back"];
|
|
498
|
+
async function toSpriteSheet(seed, options = {}) {
|
|
499
|
+
const o = resolveOptions(options);
|
|
500
|
+
const cell = SIZE;
|
|
501
|
+
const sheet = document.createElement("canvas");
|
|
502
|
+
sheet.width = cell * SHEET_POSES.length;
|
|
503
|
+
sheet.height = cell * SHEET_ROWS.length;
|
|
504
|
+
const ctx = sheet.getContext("2d");
|
|
505
|
+
ctx.imageSmoothingEnabled = false;
|
|
506
|
+
const frames = {};
|
|
507
|
+
const animations = {};
|
|
508
|
+
for (let r = 0; r < SHEET_ROWS.length; r++) {
|
|
509
|
+
const view = SHEET_ROWS[r];
|
|
510
|
+
for (let c = 0; c < SHEET_POSES.length; c++) {
|
|
511
|
+
const [pose, frame] = SHEET_POSES[c];
|
|
512
|
+
const cellCanvas = await svgToCanvas(generateSvg(seed, { ...o, view, frame }).svg, cell, "transparent");
|
|
513
|
+
ctx.drawImage(cellCanvas, c * cell, r * cell);
|
|
514
|
+
frames[`${view}_${pose}`] = {
|
|
515
|
+
frame: { x: c * cell, y: r * cell, w: cell, h: cell },
|
|
516
|
+
rotated: false,
|
|
517
|
+
trimmed: false,
|
|
518
|
+
spriteSourceSize: { x: 0, y: 0, w: cell, h: cell },
|
|
519
|
+
sourceSize: { w: cell, h: cell }
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
animations[`idle_${view}`] = [`${view}_idle`];
|
|
523
|
+
animations[`walk_${view}`] = [`${view}_walk1`, `${view}_walk2`];
|
|
524
|
+
}
|
|
525
|
+
const json = {
|
|
526
|
+
frames,
|
|
527
|
+
animations,
|
|
528
|
+
meta: {
|
|
529
|
+
app: "dotmon",
|
|
530
|
+
seed,
|
|
531
|
+
image: "sheet.png",
|
|
532
|
+
format: "RGBA8888",
|
|
533
|
+
size: { w: sheet.width, h: sheet.height },
|
|
534
|
+
scale: "1",
|
|
535
|
+
grid: { cols: SHEET_POSES.length, rows: SHEET_ROWS.length, cellW: cell, cellH: cell },
|
|
536
|
+
rowOrder: SHEET_ROWS,
|
|
537
|
+
colOrder: SHEET_POSES.map((p) => p[0]),
|
|
538
|
+
note: "Pixel art: scale with nearest neighbor. Walk = alternate walk1/walk2 (~400ms). Sheet is always transparent."
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
const png = await new Promise((res) => sheet.toBlob((b) => res(b), "image/png"));
|
|
542
|
+
return { png, json };
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// src/names.ts
|
|
546
|
+
function safeFileName(seed) {
|
|
547
|
+
return "monster-" + seed.replace(/[\\/:*?"<>|\s]/g, "_");
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// src/render/readme.ts
|
|
551
|
+
function assetReadme(seed, locale = "en") {
|
|
552
|
+
if (locale === "ja") {
|
|
553
|
+
return `${seed} \u306E\u30E2\u30F3\u30B9\u30BF\u30FC\u30BB\u30C3\u30C8
|
|
554
|
+
=========================
|
|
555
|
+
\u3053\u306EZIP\u306B\u306F\u3001\u30B2\u30FC\u30E0\u3084\u30A2\u30A4\u30B3\u30F3\u306B\u305D\u306E\u307E\u307E\u4F7F\u3048\u308B\u753B\u50CF\u304C\u5165\u3063\u3066\u3044\u307E\u3059\u3002
|
|
556
|
+
|
|
557
|
+
\u25A0 \u500B\u5225\u753B\u50CF\uFF08512px / \u4FDD\u5B58\u6642\u306E\u80CC\u666F\u8A2D\u5B9A\u3064\u304D\uFF09
|
|
558
|
+
front_idle.png / front_walk1.png / front_walk2.png \u2026 \u307E\u3048\u5411\u304D\u306E \u7ACB\u3061\u30FB\u6B69\u304D1\u30FB\u6B69\u304D2
|
|
559
|
+
\u540C\u3058\u7D44\u307F\u5408\u308F\u305B\u304C back\uFF08\u3046\u3057\u308D\uFF09/ left\uFF08\u3072\u3060\u308A\uFF09/ right\uFF08\u307F\u304E\uFF09\u306B\u3082\u3042\u308A\u307E\u3059\uFF08\u8A0812\u679A\uFF09
|
|
560
|
+
|
|
561
|
+
\u25A0 \u52D5\u304FGIF\uFF08256px\u30FB2\u30B3\u30DE\u30FB\u7121\u9650\u30EB\u30FC\u30D7\uFF09
|
|
562
|
+
front.gif / back.gif / left.gif / right.gif
|
|
563
|
+
|
|
564
|
+
\u25A0 \u30B2\u30FC\u30E0\u7528\u30B9\u30D7\u30E9\u30A4\u30C8\u30B7\u30FC\u30C8
|
|
565
|
+
sheet.png \u2026 16px\u30C9\u30C3\u30C8\u539F\u5BF8\u30FB3\u5217\xD74\u884C\uFF08\u5217: \u7ACB\u3061/\u6B69\u304D1/\u6B69\u304D2\u3001\u884C: \u307E\u3048/\u3072\u3060\u308A/\u307F\u304E/\u3046\u3057\u308D\uFF09
|
|
566
|
+
sheet.json \u2026 \u5404\u30B3\u30DE\u306E\u5EA7\u6A19\u3068\u30A2\u30CB\u30E1\u5B9A\u7FA9\uFF08TexturePacker\u4E92\u63DB\u3002Phaser / PixiJS \u3067\u305D\u306E\u307E\u307E\u8AAD\u3081\u307E\u3059\uFF09
|
|
567
|
+
|
|
568
|
+
\u203B\u30C9\u30C3\u30C8\u7D75\u306A\u306E\u3067\u3001\u62E1\u5927\u3059\u308B\u3068\u304D\u306F\u300C\u307C\u304B\u3057\u306A\u3057\uFF08nearest neighbor\uFF09\u300D\u3092\u9078\u3076\u3068\u304D\u308C\u3044\u306B\u8868\u793A\u3055\u308C\u307E\u3059\u3002
|
|
569
|
+
`;
|
|
570
|
+
}
|
|
571
|
+
return `Monster asset set for "${seed}"
|
|
572
|
+
=========================
|
|
573
|
+
This ZIP contains images ready to use in games or as icons.
|
|
574
|
+
|
|
575
|
+
- Individual PNGs (512px, with your background setting)
|
|
576
|
+
front_idle.png / front_walk1.png / front_walk2.png = front-facing idle / walk 1 / walk 2
|
|
577
|
+
The same set exists for back / left / right (12 images total)
|
|
578
|
+
|
|
579
|
+
- Animated GIFs (256px, 2 frames, infinite loop)
|
|
580
|
+
front.gif / back.gif / left.gif / right.gif
|
|
581
|
+
|
|
582
|
+
- Game sprite sheet
|
|
583
|
+
sheet.png = native 16px pixels, 3 cols x 4 rows (cols: idle/walk1/walk2, rows: front/left/right/back)
|
|
584
|
+
sheet.json = frame coordinates + animation definitions (TexturePacker compatible; loads directly in Phaser / PixiJS)
|
|
585
|
+
|
|
586
|
+
Tip: this is pixel art \u2014 scale it up with "nearest neighbor" (no smoothing) for crisp results.
|
|
587
|
+
`;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// src/render/assetZip.ts
|
|
591
|
+
var VIEWS = ["front", "back", "left", "right"];
|
|
592
|
+
async function toAssetZip(seed, options = {}) {
|
|
593
|
+
const { background = "transparent", locale = "en", ...genOptions } = options;
|
|
594
|
+
const o = resolveOptions(genOptions);
|
|
595
|
+
const dir = safeFileName(seed);
|
|
596
|
+
const files = [];
|
|
597
|
+
files.push({ name: `${dir}/README.txt`, data: new TextEncoder().encode(assetReadme(seed, locale)) });
|
|
598
|
+
for (const view of VIEWS) {
|
|
599
|
+
for (const [pose, frame] of SHEET_POSES) {
|
|
600
|
+
files.push({
|
|
601
|
+
name: `${dir}/${view}_${pose}.png`,
|
|
602
|
+
data: await pngBytes(generateSvg(seed, { ...o, view, frame }).svg, 512, background)
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
const gif = await toGif(
|
|
606
|
+
[generateSvg(seed, { ...o, view, frame: 1 }).svg, generateSvg(seed, { ...o, view, frame: 2 }).svg],
|
|
607
|
+
{ size: 256, background }
|
|
608
|
+
);
|
|
609
|
+
files.push({ name: `${dir}/${view}.gif`, data: new Uint8Array(await gif.arrayBuffer()) });
|
|
610
|
+
}
|
|
611
|
+
const sheet = await toSpriteSheet(seed, o);
|
|
612
|
+
files.push({ name: `${dir}/sheet.png`, data: new Uint8Array(await sheet.png.arrayBuffer()) });
|
|
613
|
+
files.push({ name: `${dir}/sheet.json`, data: new TextEncoder().encode(JSON.stringify(sheet.json, null, 2)) });
|
|
614
|
+
return buildZip(files);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
exports.SHEET_POSES = SHEET_POSES;
|
|
618
|
+
exports.SHEET_ROWS = SHEET_ROWS;
|
|
619
|
+
exports.assetReadme = assetReadme;
|
|
620
|
+
exports.buildZip = buildZip;
|
|
621
|
+
exports.crc32 = crc32;
|
|
622
|
+
exports.encodeGif = encodeGif;
|
|
623
|
+
exports.lzwEncode = lzwEncode;
|
|
624
|
+
exports.svgToCanvas = svgToCanvas;
|
|
625
|
+
exports.toAssetZip = toAssetZip;
|
|
626
|
+
exports.toGif = toGif;
|
|
627
|
+
exports.toPng = toPng;
|
|
628
|
+
exports.toSpriteSheet = toSpriteSheet;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { F as Frame, V as View, G as GenerateOptions, b as Locale } from '../types-kWKrAd-B.cjs';
|
|
2
|
+
|
|
3
|
+
declare function svgToCanvas(svg: string, size: number, background?: string): Promise<HTMLCanvasElement>;
|
|
4
|
+
interface RasterOptions {
|
|
5
|
+
size?: number;
|
|
6
|
+
background?: string;
|
|
7
|
+
}
|
|
8
|
+
declare function toPng(svg: string, opts?: RasterOptions): Promise<Blob>;
|
|
9
|
+
|
|
10
|
+
declare function lzwEncode(minCodeSize: number, indices: Uint8Array): number[];
|
|
11
|
+
declare function encodeGif(w: number, h: number, palette: number[][], transparentIndex: number, frames: Uint8Array[], delayCs: number): Uint8Array;
|
|
12
|
+
interface GifOptions {
|
|
13
|
+
size?: number;
|
|
14
|
+
background?: string;
|
|
15
|
+
delayMs?: number;
|
|
16
|
+
}
|
|
17
|
+
declare function toGif(frames: [string, string], opts?: GifOptions): Promise<Blob>;
|
|
18
|
+
|
|
19
|
+
declare function crc32(data: Uint8Array): number;
|
|
20
|
+
interface ZipEntry {
|
|
21
|
+
name: string;
|
|
22
|
+
data: Uint8Array;
|
|
23
|
+
}
|
|
24
|
+
declare function buildZip(files: ZipEntry[]): Blob;
|
|
25
|
+
|
|
26
|
+
declare const SHEET_POSES: [string, Frame][];
|
|
27
|
+
declare const SHEET_ROWS: View[];
|
|
28
|
+
interface SpriteSheetResult {
|
|
29
|
+
png: Blob;
|
|
30
|
+
json: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
declare function toSpriteSheet(seed: string, options?: GenerateOptions): Promise<SpriteSheetResult>;
|
|
33
|
+
|
|
34
|
+
interface AssetZipOptions extends GenerateOptions {
|
|
35
|
+
background?: string;
|
|
36
|
+
locale?: Locale;
|
|
37
|
+
}
|
|
38
|
+
declare function toAssetZip(seed: string, options?: AssetZipOptions): Promise<Blob>;
|
|
39
|
+
|
|
40
|
+
declare function assetReadme(seed: string, locale?: Locale): string;
|
|
41
|
+
|
|
42
|
+
export { type AssetZipOptions, type GifOptions, type RasterOptions, SHEET_POSES, SHEET_ROWS, type SpriteSheetResult, type ZipEntry, assetReadme, buildZip, crc32, encodeGif, lzwEncode, svgToCanvas, toAssetZip, toGif, toPng, toSpriteSheet };
|