@weft-js/plugin-surface-keel 0.1.1 → 0.1.4

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.
@@ -0,0 +1,478 @@
1
+
2
+ // packages/core/src/dmath.ts
3
+ var F64 = new Float64Array(1);
4
+ var U32 = new Uint32Array(F64.buffer);
5
+ F64[0] = 1;
6
+ var HI = U32[1] === 1072693248 ? 1 : 0;
7
+ var LO = 1 - HI;
8
+ function dhypot(a, b, c) {
9
+ const x = Math.abs(a);
10
+ const y = Math.abs(b);
11
+ const three = c !== void 0;
12
+ const z = three ? Math.abs(c) : 0;
13
+ if (x === Infinity || y === Infinity || z === Infinity) return Infinity;
14
+ if (x !== x || y !== y || z !== z) return NaN;
15
+ let max = x > y ? x : y;
16
+ if (z > max) max = z;
17
+ if (max === 0) return 0;
18
+ let n = x / max;
19
+ let sum = n * n;
20
+ let comp = 0;
21
+ n = y / max;
22
+ let summand = n * n - comp;
23
+ let pre = sum + summand;
24
+ comp = pre - sum - summand;
25
+ sum = pre;
26
+ if (three) {
27
+ n = z / max;
28
+ summand = n * n - comp;
29
+ pre = sum + summand;
30
+ sum = pre;
31
+ }
32
+ return Math.sqrt(sum) * max;
33
+ }
34
+
35
+ // packages/core/src/frame.ts
36
+ var FRONT = Object.freeze([0, 0, 1]);
37
+ var RIGHT = Object.freeze([1, 0, 0]);
38
+ var UP = Object.freeze([0, 1, 0]);
39
+ function cameraBasis(eye, target) {
40
+ let f = [target[0] - eye[0], target[1] - eye[1], target[2] - eye[2]];
41
+ const fl = dhypot(f[0], f[1], f[2]) || 1;
42
+ f = [f[0] / fl, f[1] / fl, f[2] / fl];
43
+ let r = [f[2], 0, -f[0]];
44
+ const rl = dhypot(r[0], r[1], r[2]) || 1;
45
+ r = rl < 1e-6 ? [1, 0, 0] : [r[0] / rl, r[1] / rl, r[2] / rl];
46
+ const u = [f[1] * r[2] - f[2] * r[1], f[2] * r[0] - f[0] * r[2], f[0] * r[1] - f[1] * r[0]];
47
+ return { forward: f, right: r, up: u };
48
+ }
49
+
50
+ // packages/core/src/math.ts
51
+ var TAU = Math.PI * 2;
52
+ var fract = (v) => v - Math.floor(v);
53
+ var tri = (v) => 1 - Math.abs(2 * fract(v) - 1);
54
+
55
+ // packages/core/src/curl.ts
56
+ var FLOW_PERIOD = 4096;
57
+ var FLOW_SPEED = 0.35;
58
+ var FLOW_R = FLOW_PERIOD * FLOW_SPEED / (2 * Math.PI);
59
+ var W = new Float64Array(8);
60
+ var IO = new Float64Array(7);
61
+ var CURL_GLSL = `
62
+ float curlLat(ivec3 i, uint s) {
63
+ uint h = (uint(i.x) * 0x8da6b343u) ^ (uint(i.y) * 0xd8163841u) ^ (uint(i.z) * 0xcb1ab31fu) ^ s;
64
+ h ^= h >> 16; h *= 0x7feb352du; h ^= h >> 15; h *= 0x846ca68bu; h ^= h >> 16;
65
+ return float(h) / 2147483647.5 - 1.0;
66
+ }
67
+ vec3 curlGrad(vec3 x, uint s) {
68
+ vec3 fi = floor(x); ivec3 i = ivec3(fi); vec3 f = x - fi;
69
+ vec3 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
70
+ vec3 du = 30.0 * f * f * (f * (f - 2.0) + 1.0);
71
+ float a = curlLat(i, s), b = curlLat(i + ivec3(1, 0, 0), s), c = curlLat(i + ivec3(0, 1, 0), s), d = curlLat(i + ivec3(1, 1, 0), s);
72
+ float e = curlLat(i + ivec3(0, 0, 1), s), ff = curlLat(i + ivec3(1, 0, 1), s), g = curlLat(i + ivec3(0, 1, 1), s), h = curlLat(i + ivec3(1, 1, 1), s);
73
+ float k1 = b - a, k2 = c - a, k3 = e - a, k4 = a - b - c + d, k5 = a - c - e + g, k6 = a - b - e + ff, k7 = -a + b + c - d + e - ff - g + h;
74
+ return du * vec3(k1 + k4 * u.y + k6 * u.z + k7 * u.y * u.z, k2 + k5 * u.z + k4 * u.x + k7 * u.z * u.x, k3 + k6 * u.x + k5 * u.y + k7 * u.x * u.y);
75
+ }
76
+ vec3 curlNoiseN(vec3 p, float t, int octaves) {
77
+ float th = t / ${FLOW_PERIOD.toFixed(1)} * 6.283185307;
78
+ p += vec3(${FLOW_R.toFixed(4)} * cos(th), 0.0, ${FLOW_R.toFixed(4)} * sin(th));
79
+ vec3 a = vec3(0.0), b = vec3(0.0);
80
+ float f = 1.0, w = 1.0;
81
+ for (int k = 0; k < 2; k++) {
82
+ if (k >= octaves) break;
83
+ a += curlGrad(p * f, 0x2545f491u + uint(k)) * f * w;
84
+ b += curlGrad(p * f + vec3(31.4, 17.7, 5.9), 0x68e31da4u + uint(k)) * f * w;
85
+ f *= 2.03; w *= 0.5;
86
+ }
87
+ return cross(a, b) * 0.25;
88
+ }
89
+ vec3 curlNoise(vec3 p, float t) { return curlNoiseN(p, t, 2); }`;
90
+
91
+ // packages/core/src/palette.ts
92
+ function ramp(S, hues, extra = {}) {
93
+ return {
94
+ hues,
95
+ chroma: S.between(0.11, 0.19),
96
+ lift: S.between(0.04, 0.09),
97
+ top: S.between(0.94, 0.985),
98
+ gamma: S.between(1, 1.35),
99
+ shift: S.between(6, 20),
100
+ ...extra
101
+ };
102
+ }
103
+ var accentRamp = (S, hue, extra = {}) => ({
104
+ hues: [hue - 6, hue + 6],
105
+ chroma: S.between(0.15, 0.23),
106
+ lift: 0.32,
107
+ top: 0.96,
108
+ gamma: 0.9,
109
+ shift: 4,
110
+ ...extra
111
+ });
112
+ var SCHEMES = [
113
+ ["Monochrome", 3, (S, h) => ({
114
+ key: ramp(S, [h - 4, h + 4]),
115
+ accent: accentRamp(S, h + S.between(-10, 10), { chroma: S.between(0.18, 0.25) })
116
+ })],
117
+ ["Nocturne", 9, (S, h) => ({
118
+ key: ramp(S, [h - 9, h + 9]),
119
+ accent: accentRamp(S, h + 180 + S.between(-35, 35))
120
+ })],
121
+ ["Duotone", 5, (S, h) => {
122
+ const d = S.pick([-1, 1]) * S.between(45, 85);
123
+ return { key: ramp(S, [h - d / 2, h + d / 2]), accent: accentRamp(S, h + 180 + S.between(-25, 25)) };
124
+ }],
125
+ ["Split", 3, (S, h) => ({
126
+ key: ramp(S, [h - 12, h + 12]),
127
+ accent: accentRamp(S, h + S.pick([-1, 1]) * S.between(125, 155))
128
+ })],
129
+ ["Triad", 3, (S, h) => {
130
+ const dir = S.pick([-1, 1]);
131
+ return { key: ramp(S, [h, h + dir * 120]), accent: accentRamp(S, h - dir * 120 + S.between(-15, 15)) };
132
+ }],
133
+ ["Two Worlds", 7.5, (S, h) => {
134
+ const inside = h + 180 + S.between(-40, 40);
135
+ return {
136
+ outside: ramp(S, [h - 10, h + 10], { chroma: S.between(0.12, 0.2), top: 0.9 }),
137
+ key: ramp(S, [inside - 12, inside + 12]),
138
+ accent: accentRamp(S, inside + S.pick([-1, 1]) * S.between(25, 60))
139
+ };
140
+ }],
141
+ ["Neon Noir", 2, (S, h) => ({
142
+ // Near-neutral room, one vivid ink. Contrast by placement, not by volume.
143
+ key: ramp(S, [h - 5, h + 5], { chroma: S.between(0.025, 0.05), shift: 4 }),
144
+ accent: accentRamp(S, h + S.pick([0, 150, 180, 210]), { chroma: 0.3, lift: 0.4 })
145
+ })],
146
+ ["Spectrum", 1.5, (S, h) => {
147
+ const span = S.pick([-1, 1]) * S.between(150, 220);
148
+ return {
149
+ key: ramp(S, [h, h + span / 3, h + span * 2 / 3, h + span], { chroma: S.between(0.13, 0.18) }),
150
+ accent: accentRamp(S, h + span / 2 + 180)
151
+ };
152
+ }],
153
+ ["Prism", 0.6, (S, h) => ({
154
+ key: ramp(S, [h, h + 90, h + 180, h + 270, h + 360], { chroma: S.between(0.13, 0.17), shift: 0 }),
155
+ accent: accentRamp(S, h + 45, { chroma: 0.12 })
156
+ })]
157
+ ];
158
+ var SCHEME_NAMES = SCHEMES.map(([n]) => n);
159
+
160
+ // packages/core/src/look.ts
161
+ var DRAWS = new Float64Array(16);
162
+ var WEIGHTS = [1, 0.8, 0.5];
163
+ var W_NORM = Math.sqrt(WEIGHTS.reduce((a, b) => a + b, 0));
164
+
165
+ // packages/core/src/dither.ts
166
+ var BAYER2 = [0, 2, 3, 1];
167
+ var BAYER4 = [0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5];
168
+ var BAYER8 = (() => {
169
+ const out = new Array(64);
170
+ for (let y = 0; y < 8; y += 1) {
171
+ for (let x = 0; x < 8; x += 1) {
172
+ const q = BAYER4[(y & 3) * 4 + (x & 3)];
173
+ const b = BAYER2[(y >> 2) * 2 + (x >> 2)];
174
+ out[y * 8 + x] = q * 4 + b;
175
+ }
176
+ }
177
+ return out;
178
+ })();
179
+ var b2 = (i) => BAYER2[i];
180
+ var b4 = (i) => BAYER4[i];
181
+ var b8 = (i) => BAYER8[i];
182
+ function equalize(shape, w, h, tie = (x, y) => b8((y & 7) * 8 + (x & 7))) {
183
+ const cells = [];
184
+ for (let y = 0; y < h; y += 1) for (let x = 0; x < w; x += 1) cells.push([Math.round(shape(x, y) * 1e6), tie(x, y), y * w + x]);
185
+ cells.sort((a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2]);
186
+ const table = new Float32Array(w * h);
187
+ cells.forEach(([, , k], rank) => {
188
+ table[k] = (rank + 0.5) / cells.length;
189
+ });
190
+ return (x, y) => table[(y % h + h) % h * w + (x % w + w) % w];
191
+ }
192
+ var ALONG = [0, 4, 2, 6, 1, 5, 3, 7];
193
+ var along = (x) => ALONG[x & 7];
194
+ function dotScreen([ax, ay], [bx, by], w, h) {
195
+ const det = ax * by - ay * bx;
196
+ const coords = (x, y) => [(x * by - y * bx) / det, (y * ax - x * ay) / det];
197
+ const shape = (x, y) => {
198
+ const [u, v] = coords(x, y);
199
+ return -(Math.cos(2 * Math.PI * u) + Math.cos(2 * Math.PI * v));
200
+ };
201
+ const tie = (x, y) => {
202
+ const [u, v] = coords(x, y);
203
+ const [i, j] = [Math.round(u), Math.round(v)];
204
+ const dx = x - (i * ax + j * bx);
205
+ const dy = y - (i * ay + j * by);
206
+ const oct = Math.floor((Math.atan2(dy, dx) + 2 * Math.PI) % (2 * Math.PI) / (Math.PI / 4) + 1e-9) & 7;
207
+ return ALONG[oct];
208
+ };
209
+ return equalize(shape, w, h, tie);
210
+ }
211
+ var BLUE_NOISE = (() => {
212
+ const s = atob("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");
213
+ const t = new Uint8Array(s.length);
214
+ for (let i = 0; i < s.length; i += 1) t[i] = s.charCodeAt(i);
215
+ return t;
216
+ })();
217
+ var SCREENS = {
218
+ bayer2: { name: "Bayer 2", at: (x, y) => (b2((y & 1) * 2 + (x & 1)) + 0.5) / 4 },
219
+ bayer4: { name: "Bayer 4", at: (x, y) => (b4((y & 3) * 4 + (x & 3)) + 0.5) / 16 },
220
+ bayer8: { name: "Bayer 8", at: (x, y) => (b8((y & 7) * 8 + (x & 7)) + 0.5) / 64 },
221
+ chunky: { name: "Chunky", at: (x, y) => (b4((y >> 1 & 3) * 4 + (x >> 1 & 3)) + 0.5) / 16 },
222
+ // (As fine as the old screen: a dot every 18 pixels, now truly at 45°.)
223
+ halftone: { name: "Halftone", at: dotScreen([3, 3], [3, -3], 6, 6) },
224
+ // (The coarse dot keeps its nine flat levels for now: equalized, at 26.6°
225
+ // or square, its dots read as blotches to every critic. The owner's call.)
226
+ coarseDot: { name: "Coarse Dot", at: (x, y) => (Math.cos((x + y) / 6 * Math.PI) * Math.cos((x - y) / 6 * Math.PI) + 1) * 0.5 },
227
+ // (Line screen, crosshatch, checker and weave keep their few flat levels
228
+ // for now: equalized, a night sky's gradient turns to dashes, hatching lies
229
+ // across the objects and a checker becomes a lattice of specks -- and blind
230
+ // critics preferred the flat fills every time. The owner's call.)
231
+ lines: { name: "Line Screen", at: (x, y) => tri(y / 3 + 0.5) * 0.9 + b2((y & 1) * 2 + (x & 1)) / 4 * 0.1 },
232
+ diagonal: { name: "Diagonal", at: equalize((x, y) => [3, 1, 0, 2][x + y & 3], 8, 8, (x, y) => along(x + 4 * (x + y >> 2))) },
233
+ hatch: { name: "Crosshatch", at: (x, y) => (tri((x + y) / 4) + tri((x - y) / 4)) * 0.5 },
234
+ stipple: { name: "Stipple", at: (x, y) => (BLUE_NOISE[(y & 63) * 64 + (x & 63)] + 0.5) / 256 },
235
+ ign: { name: "Interleaved", at: (x, y) => fract(52.9829189 * fract(0.06711056 * x + 583715e-8 * y)) },
236
+ checker: { name: "Checker", at: (x, y) => x + y & 1 ? 0.3 : 0.7 },
237
+ weave: { name: "Weave", at: (x, y) => ((x >> 1) + (y >> 1) & 1 ? tri(x / 4) : tri(y / 4)) * 0.94 + 0.03 }
238
+ };
239
+ var SCREEN_IDS = Object.keys(SCREENS);
240
+ function measureScreen(at, T = 24) {
241
+ const vals = [];
242
+ for (let y = 0; y < T; y += 1) for (let x = 0; x < T; x += 1) vals.push(at(x, y));
243
+ const levels = new Set(vals.map((v) => v.toFixed(4))).size;
244
+ const bins = new Array(16).fill(0);
245
+ for (const v of vals) {
246
+ const b = Math.min(15, Math.floor(v * 16));
247
+ bins[b] = bins[b] + 1;
248
+ }
249
+ const maxBin = Math.max(...bins) / vals.length;
250
+ const mean = vals.reduce((a, v) => a + v, 0) / vals.length;
251
+ const varr = vals.reduce((a, v) => a + (v - mean) ** 2, 0) / vals.length || 1;
252
+ const corr = (dx, dy) => {
253
+ let acc = 0;
254
+ let n = 0;
255
+ for (let y = 0; y < T - 8; y += 1) for (let x = 8; x < T - 8; x += 1) {
256
+ acc += (at(x, y) - mean) * (at(x + dx, y + dy) - mean);
257
+ n += 1;
258
+ }
259
+ return acc / n / varr;
260
+ };
261
+ const shifts = [];
262
+ for (let dy = 0; dy <= 8; dy += 1) for (let dx = -8; dx <= 8; dx += 1) if (dy > 0 || dx > 0) shifts.push({ r: corr(dx, dy), dx, dy });
263
+ const rMax = Math.max(...shifts.map((s) => s.r));
264
+ const best = shifts.filter((s) => s.r > rMax - 0.02).sort((a, b) => Math.hypot(a.dx, a.dy) - Math.hypot(b.dx, b.dy))[0];
265
+ const angle = (Math.atan2(best.dy, best.dx) * 180 / Math.PI + 180) % 180;
266
+ const unit = [[0, 1, 0], [45, 1, 1], [90, 0, 1], [135, -1, 1]].map(([a, dx, dy]) => [a, corr(dx, dy)]);
267
+ const top = Math.max(...unit.map(([, r]) => r));
268
+ return {
269
+ levels,
270
+ maxBin,
271
+ period: +Math.hypot(best.dx, best.dy).toFixed(2),
272
+ angle: +angle.toFixed(1),
273
+ r: +best.r.toFixed(2),
274
+ // (Every direction within a hair of the smoothest counts: a crosshatch runs both diagonals.)
275
+ lines: unit.filter(([, r]) => r > top - 0.02).map(([a]) => a)
276
+ };
277
+ }
278
+ var SCREEN_GEOM = Object.fromEntries(SCREEN_IDS.map((id) => {
279
+ const m = measureScreen(SCREENS[id].at);
280
+ return [id, { period: m.period, angle: m.angle, r: m.r, lines: m.lines }];
281
+ }));
282
+ var SCREEN_KIND = {
283
+ bayer2: ["ordered", 1, "grid"],
284
+ bayer4: ["ordered", 1, "grid"],
285
+ bayer8: ["ordered", 1, "grid"],
286
+ chunky: ["ordered", 3, "grid"],
287
+ halftone: ["dot", 2, "diag"],
288
+ coarseDot: ["dot", 3, "grid"],
289
+ lines: ["line", 2, "h"],
290
+ diagonal: ["line", 2, "d"],
291
+ hatch: ["line", 2, "x"],
292
+ stipple: ["noise", 1, "none"],
293
+ ign: ["noise", 1, "ign"],
294
+ checker: ["pattern", 3, "diag"],
295
+ weave: ["pattern", 3, "grid"]
296
+ };
297
+ var TARGET_BANDS = [["tiny", 48], ["small", 128], ["large", Infinity]];
298
+ var TARGET_SCREENS = {
299
+ ordered: { tiny: "bayer2", small: "bayer4", large: "bayer8" },
300
+ dot: { tiny: "bayer2", small: "halftone", large: "halftone" },
301
+ line: { tiny: "bayer2", small: "lines", large: "hatch" },
302
+ noise: { tiny: "ign", small: "stipple", large: "stipple" },
303
+ pattern: { tiny: "checker", small: "bayer4", large: "weave" }
304
+ };
305
+ var TARGET_STEPS = { tiny: 3, small: 5, large: 7 };
306
+ var SCREEN_MIN_BAND = {
307
+ bayer2: "tiny",
308
+ checker: "tiny",
309
+ ign: "tiny",
310
+ bayer4: "small",
311
+ halftone: "small",
312
+ lines: "small",
313
+ diagonal: "small",
314
+ stipple: "small",
315
+ bayer8: "large",
316
+ chunky: "large",
317
+ coarseDot: "large",
318
+ hatch: "large",
319
+ weave: "large"
320
+ };
321
+ var BAND_ORDER = { tiny: 0, small: 1, large: 2 };
322
+ function bandOf(width, height = width) {
323
+ const side = Math.min(width, height);
324
+ for (const [band, max] of TARGET_BANDS) if (side <= max) return band;
325
+ return "large";
326
+ }
327
+ function screenForTarget(width, height = width, preference = "ordered") {
328
+ const band = bandOf(width, height);
329
+ let family;
330
+ let id;
331
+ if (SCREENS[preference]) {
332
+ const pref = preference;
333
+ family = SCREEN_KIND[pref][0];
334
+ id = BAND_ORDER[SCREEN_MIN_BAND[pref]] <= BAND_ORDER[band] ? pref : TARGET_SCREENS[family][band];
335
+ } else if (TARGET_SCREENS[preference]) {
336
+ family = preference;
337
+ id = TARGET_SCREENS[family][band];
338
+ } else {
339
+ throw new RangeError(`Unknown screen or family: ${preference}`);
340
+ }
341
+ return { id, steps: TARGET_STEPS[band], bias: 0, family, band };
342
+ }
343
+
344
+ // packages/core/src/quantize.ts
345
+ var BAYER4Q = [0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5].map((v) => (v + 0.5) / 16);
346
+
347
+ // packages/core/src/gif.ts
348
+ var DICT = new Int32Array(1 << 17).fill(-1);
349
+ var DICT_TOUCHED = new Int32Array(1 << 12);
350
+
351
+ // packages/core/src/targets.ts
352
+ var preset = (id, label, kind, width, height, paletteLimit, channelBits, screen, hardware = null) => Object.freeze({ id, label, kind, width, height, paletteLimit, channelBits, screen, dither: screen ? 0.9 : 0, hardware });
353
+ var TARGET_PROFILES = Object.freeze({
354
+ native: preset("native", "Original", "native", 128, 128, 65536, 8, 4),
355
+ "8-bit": preset("8-bit", "8-bit style", "style", 256, 240, 16, 8, 2),
356
+ "16-bit": preset("16-bit", "16-bit style", "style", 320, 240, 256, 5, 4),
357
+ "32-bit": preset("32-bit", "32-bit style", "style", 512, 384, 32768, 5, 4),
358
+ "64-bit": preset("64-bit", "64-bit style", "style", 640, 480, 65536, 8, 8),
359
+ "game-boy": preset("game-boy", "Game Boy", "hardware", 160, 144, 4, 8, 2, "game-boy"),
360
+ "game-boy-color": preset("game-boy-color", "Game Boy Color", "hardware", 160, 144, 32, 5, 2, "game-boy-color"),
361
+ chromatic: preset("chromatic", "ModRetro Chromatic", "hardware", 160, 144, 32, 5, 2, "game-boy-color")
362
+ });
363
+ function targetProfile(input) {
364
+ const id = typeof input === "number" ? `${input}-bit` : input;
365
+ if (!Object.hasOwn(TARGET_PROFILES, id)) throw new RangeError(`Unknown target: ${id}`);
366
+ return TARGET_PROFILES[id];
367
+ }
368
+ var byte = (n) => {
369
+ if (!Number.isFinite(n)) throw new RangeError("Colour channels must be finite");
370
+ return Math.max(0, Math.min(255, Math.round(n)));
371
+ };
372
+ var colour = (c) => [byte(c[0] ?? 0), byte(c[1] ?? 0), byte(c[2] ?? 0)];
373
+ var key = (c) => c[0] << 16 | c[1] << 8 | c[2];
374
+ var light = (c) => c[0] * 299 + c[1] * 587 + c[2] * 114;
375
+ function rgb555(c) {
376
+ const [r, g, b] = colour(c).map((v) => Math.round(v * 31 / 255));
377
+ return r | g << 5 | b << 10;
378
+ }
379
+ function fromRgb555(word) {
380
+ return [Math.round((word & 31) * 255 / 31), Math.round((word >> 5 & 31) * 255 / 31), Math.round((word >> 10 & 31) * 255 / 31)];
381
+ }
382
+ function nearestColour(c, palette) {
383
+ if (!palette.length) throw new RangeError("A palette must contain a colour");
384
+ let best = 0, distance = Infinity;
385
+ for (let i = 0; i < palette.length; i += 1) {
386
+ const p = palette[i];
387
+ const d = (c[0] - p[0]) ** 2 * 299 + (c[1] - p[1]) ** 2 * 587 + (c[2] - p[2]) ** 2 * 114;
388
+ if (d < distance) {
389
+ distance = d;
390
+ best = i;
391
+ }
392
+ }
393
+ return best;
394
+ }
395
+ function box(samples) {
396
+ let axis = 0, range = -1;
397
+ for (let a = 0; a < 3; a += 1) {
398
+ let lo = 255, hi = 0;
399
+ for (const s of samples) {
400
+ lo = Math.min(lo, s.c[a]);
401
+ hi = Math.max(hi, s.c[a]);
402
+ }
403
+ if (hi - lo > range) {
404
+ axis = a;
405
+ range = hi - lo;
406
+ }
407
+ }
408
+ return { samples, axis, score: samples.length > 1 ? range * samples.reduce((n, s) => n + s.count, 0) : -1 };
409
+ }
410
+ function reduceColours(input, limit) {
411
+ if (!Number.isSafeInteger(limit) || limit < 1) throw new RangeError("Palette limit must be a positive integer");
412
+ const histogram = /* @__PURE__ */ new Map();
413
+ for (const raw of input) {
414
+ const c = colour(raw), k = key(c), old = histogram.get(k);
415
+ if (old) old.count += 1;
416
+ else histogram.set(k, { c, count: 1 });
417
+ }
418
+ const samples = [...histogram.values()].sort((a, b) => key(a.c) - key(b.c));
419
+ if (samples.length <= limit) return samples.map((s) => [...s.c]);
420
+ const boxes = [box(samples)];
421
+ while (boxes.length < limit) {
422
+ let selected = 0;
423
+ for (let i = 1; i < boxes.length; i += 1) if (boxes[i].score > boxes[selected].score) selected = i;
424
+ const b = boxes[selected];
425
+ if (b.score < 0) break;
426
+ b.samples.sort((a, z) => a.c[b.axis] - z.c[b.axis] || key(a.c) - key(z.c));
427
+ const half = b.samples.reduce((n, s) => n + s.count, 0) / 2;
428
+ let total = 0, split = 0;
429
+ do {
430
+ total += b.samples[split++].count;
431
+ } while (total < half && split < b.samples.length - 1);
432
+ boxes.splice(selected, 1, box(b.samples.slice(0, split)), box(b.samples.slice(split)));
433
+ }
434
+ return boxes.map((b) => {
435
+ const weight = b.samples.reduce((n, s) => n + s.count, 0);
436
+ const mean = [0, 0, 0];
437
+ for (const s of b.samples) for (let a = 0; a < 3; a += 1) mean[a] = mean[a] + s.c[a] * s.count / weight;
438
+ return [...b.samples[nearestColour(mean, b.samples.map((s) => s.c))].c];
439
+ }).sort((a, b) => light(a) - light(b) || key(a) - key(b));
440
+ }
441
+ function adaptTargetPalette(input, target) {
442
+ const profile2 = targetProfile(target);
443
+ const colours = input.map((c) => {
444
+ const rgb = colour(c);
445
+ if (profile2.hardware === "game-boy") {
446
+ const v = Math.round(light(rgb) / 85e3) * 85;
447
+ return [v, v, v];
448
+ }
449
+ return profile2.channelBits === 5 ? fromRgb555(rgb555(rgb)) : rgb;
450
+ });
451
+ const palette = reduceColours(colours, profile2.paletteLimit);
452
+ const cache = new Map(palette.map((c) => [key(c), c]));
453
+ return colours.map((c) => {
454
+ const k = key(c);
455
+ let found = cache.get(k);
456
+ if (!found) {
457
+ found = palette[nearestColour(c, palette)];
458
+ cache.set(k, found);
459
+ }
460
+ return [...found];
461
+ });
462
+ }
463
+
464
+ // packages/render/src/fx.ts
465
+ var FX_ORDER = ["crt", "grade", "fog", "glow", "rim", "flash", "vignette", "scanlines", "dither", "outline", "cycle"];
466
+ var scalePx = (v, side, min = 1, max = 64) => Math.max(min, Math.min(max, Math.round(v * side / 128)));
467
+ var GRADE_PRESETS = { day: { shift: 0 }, dusk: { shift: -0.6 }, night: { shift: -1.4 } };
468
+
469
+ export {
470
+ FX_ORDER,
471
+ GRADE_PRESETS,
472
+ SCREENS,
473
+ adaptTargetPalette,
474
+ cameraBasis,
475
+ scalePx,
476
+ screenForTarget,
477
+ targetProfile,
478
+ };