@roobscoob/vibes 0.0.0-stage → 0.2.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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +276 -2
  3. package/dist/arclamp.d.ts +34 -0
  4. package/dist/arclamp.d.ts.map +1 -0
  5. package/dist/arclamp.js +5 -0
  6. package/dist/color.d.ts +202 -0
  7. package/dist/color.d.ts.map +1 -0
  8. package/dist/color.js +492 -0
  9. package/dist/field.d.ts +94 -0
  10. package/dist/field.d.ts.map +1 -0
  11. package/dist/field.js +232 -0
  12. package/dist/gamut.d.ts +5 -0
  13. package/dist/gamut.d.ts.map +1 -0
  14. package/dist/gamut.js +29 -0
  15. package/dist/gamut.table.d.ts +5 -0
  16. package/dist/gamut.table.d.ts.map +1 -0
  17. package/dist/gamut.table.js +153 -0
  18. package/dist/here.d.ts +105 -0
  19. package/dist/here.d.ts.map +1 -0
  20. package/dist/here.js +219 -0
  21. package/dist/index.d.ts +8 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +8 -0
  24. package/dist/lemtone.d.ts +37 -0
  25. package/dist/lemtone.d.ts.map +1 -0
  26. package/dist/lemtone.js +158 -0
  27. package/dist/names.d.ts +5 -0
  28. package/dist/names.d.ts.map +1 -0
  29. package/dist/names.js +46 -0
  30. package/dist/parse.d.ts +43 -0
  31. package/dist/parse.d.ts.map +1 -0
  32. package/dist/parse.js +208 -0
  33. package/dist/shape.d.ts +44 -0
  34. package/dist/shape.d.ts.map +1 -0
  35. package/dist/shape.js +89 -0
  36. package/dist/signal.d.ts +26 -0
  37. package/dist/signal.d.ts.map +1 -0
  38. package/dist/signal.js +44 -0
  39. package/dist/sketch.d.ts +29 -0
  40. package/dist/sketch.d.ts.map +1 -0
  41. package/dist/sketch.js +141 -0
  42. package/dist/spaces.d.ts +24 -0
  43. package/dist/spaces.d.ts.map +1 -0
  44. package/dist/spaces.js +91 -0
  45. package/package.json +24 -4
  46. package/src/arclamp.ts +36 -0
  47. package/src/color.ts +590 -0
  48. package/src/field.ts +321 -0
  49. package/src/gamut.table.ts +156 -0
  50. package/src/gamut.ts +32 -0
  51. package/src/here.ts +272 -0
  52. package/src/index.ts +52 -0
  53. package/src/lemtone.ts +180 -0
  54. package/src/names.ts +50 -0
  55. package/src/parse.ts +205 -0
  56. package/src/shape.ts +109 -0
  57. package/src/signal.ts +65 -0
  58. package/src/sketch.ts +172 -0
  59. package/src/spaces.ts +102 -0
package/src/field.ts ADDED
@@ -0,0 +1,321 @@
1
+ // Fields: patterns over the room and time, made by chaining.
2
+ //
3
+ // A field is a function of `here`, so it's what a sketch's own function is
4
+ // too: field(fn) gives a function the chain methods, and any field can be
5
+ // called with a `here` to read it. Sources make numbers from 0 to 1; a
6
+ // palette (.color) makes them colors; sketch() turns a color field into
7
+ // update().
8
+ //
9
+ // Sources count across the room's width: osc(3) is three bands wall to wall,
10
+ // and the room's center is the middle of what its lights fill. Any number a
11
+ // method or source takes can instead be a function of here (a field too):
12
+ // osc(3, ({ u }) => 0.05 + 0.2 * u) goes round faster toward the right.
13
+ //
14
+ // Every transform is also a plain function on here (rotate(here, 30), ...),
15
+ // built on here.at(), so nothing needs a chain.
16
+
17
+ import { color, isPalette, toPalette, type Bulb, type Color, type ColorInput, type ColorLike, type Palette, type Stop } from "./color.ts";
18
+ import type { Here, Light } from "./here.ts";
19
+ import { clamp, hash, lerp, noiseAt, wave } from "./shape.ts";
20
+
21
+ /** A number, or a number that depends on here (a field, or any function of here). */
22
+ export type Param = number | ((here: Here) => number);
23
+
24
+ /** A color (anything that says one), or one that depends on here. */
25
+ export type ColorParam = ColorInput | ((here: Here) => ColorInput);
26
+
27
+ export interface Field<T> {
28
+ (here: Here): T;
29
+
30
+ /** Turned by `degrees` around the room's center (clockwise on the plan). */
31
+ rotate(degrees: Param): Field<T>;
32
+ /** k times as big, from the room's center. */
33
+ scale(k: Param): Field<T>;
34
+ /** Moved dx meters right and dy down. */
35
+ move(dx: Param, dy?: Param): Field<T>;
36
+ /** Folded into n mirrored slices around the room's center, like a kaleidoscope. */
37
+ kaleid(n: Param): Field<T>;
38
+ /** Pushed around by another field: up to `amount` meters (default 1) where it's 0 or 1, not at all where it's 0.5. */
39
+ warp(by: (here: Here) => number, amount?: Param): Field<T>;
40
+ /** `seconds` behind: .delay(({ u }) => 2 * u) runs two seconds later at the room's right than its left. */
41
+ delay(seconds: Param): Field<T>;
42
+ /** k times as fast. */
43
+ speed(k: Param): Field<T>;
44
+
45
+ /** Its value, changed by your own function. */
46
+ map<U>(fn: (value: T, here: Here) => U): Field<U>;
47
+ /** Only on the lights that pass `test`; the rest are left as they are below. */
48
+ only(test: (light: Light) => unknown): Field<T | null>;
49
+
50
+ /** Raised to the power k: above 1 darkens the middle, below 1 lifts it. */
51
+ pow(this: Field<number>, k: Param): Field<number>;
52
+ /** Cut into n flat steps from 0 to 1. */
53
+ steps(this: Field<number>, n: Param): Field<number>;
54
+ /** 1 at or above `level` (default 0.5), 0 below: hard edges. */
55
+ thresh(this: Field<number>, level?: Param): Field<number>;
56
+ /** 1 minus it. */
57
+ invert(this: Field<number>): Field<number>;
58
+ /** Times k. */
59
+ mul(this: Field<number>, k: Param): Field<number>;
60
+ /** Plus another number, or (for colors) another light, shining together. */
61
+ add(this: Field<number>, other: Param): Field<number>;
62
+ add(this: Field<ColorLike>, other: ColorParam): Field<Color>;
63
+ /** Toward `other` by t (0.5 if not given), from 0 (all this) to 1 (all other). */
64
+ mix(this: Field<number>, other: Param, t?: Param): Field<number>;
65
+ mix(this: Field<ColorLike>, other: ColorParam, t?: Param): Field<Color>;
66
+ /** Made a color: by a palette (0 to 1 along it), or your own function. */
67
+ color(this: Field<number>, by: (value: number, here: Here) => ColorInput): Field<Color>;
68
+ /** Made a color by a palette that changes: a field of palettes, like clock(96).every(16).seq([...]). */
69
+ color(this: Field<number>, by: Field<Palette>): Field<Color>;
70
+ /** Made a color by a palette's stops ([dark, "#f80", "white"]), or by one color, from dark at 0 to it at 1. */
71
+ color(this: Field<number>, by: readonly Stop[] | ColorLike): Field<Color>;
72
+
73
+ /** At `lm` lumens. */
74
+ at(this: Field<ColorLike>, lm: Param): Field<Color>;
75
+ /** k times the light. */
76
+ dim(this: Field<ColorLike>, k: Param): Field<Color>;
77
+ /** Looks k times as bright. */
78
+ fade(this: Field<ColorLike>, k: Param): Field<Color>;
79
+ /** Brighter by `amount` (default 1, twice the light) times `by`: .boost(beat, 0.8) lifts it 80% on each beat. */
80
+ boost(this: Field<ColorLike>, by: Param, amount?: Param): Field<Color>;
81
+ /** The hue turned by `degrees`. */
82
+ shiftHue(this: Field<ColorLike>, degrees: Param): Field<Color>;
83
+ /** k times the chroma. */
84
+ saturate(this: Field<ColorLike>, k: Param): Field<Color>;
85
+ /** Dimmed where need be to what a bulb makes (a color bulb, br30, unless told), so its colors aren't paled. */
86
+ vivid(this: Field<ColorLike>, bulb?: Bulb): Field<Color>;
87
+ }
88
+
89
+ // ---- making fields ------------------------------------------------------------
90
+
91
+ /** Reads a number or color that may depend on here. */
92
+ const read = <T>(p: T | ((here: Here) => T), here: Here): T =>
93
+ typeof p === "function" ? (p as (here: Here) => T)(here) : p;
94
+
95
+ // The chain methods, shared by every field (a field is a function whose prototype is this).
96
+ const methods = {
97
+ rotate(this: Field<unknown>, degrees: Param) {
98
+ return make((here) => this(rotate(here, read(degrees, here))));
99
+ },
100
+ scale(this: Field<unknown>, k: Param) {
101
+ return make((here) => this(scale(here, read(k, here))));
102
+ },
103
+ move(this: Field<unknown>, dx: Param, dy: Param = 0) {
104
+ return make((here) => this(move(here, read(dx, here), read(dy, here))));
105
+ },
106
+ kaleid(this: Field<unknown>, n: Param) {
107
+ return make((here) => this(kaleid(here, read(n, here))));
108
+ },
109
+ warp(this: Field<unknown>, by: (here: Here) => number, amount: Param = 1) {
110
+ return make((here) => this(warp(here, by, read(amount, here))));
111
+ },
112
+ delay(this: Field<unknown>, seconds: Param) {
113
+ return make((here) => this(delay(here, read(seconds, here))));
114
+ },
115
+ speed(this: Field<unknown>, k: Param) {
116
+ return make((here) => this(speed(here, read(k, here))));
117
+ },
118
+ map(this: Field<unknown>, fn: (value: unknown, here: Here) => unknown) {
119
+ return make((here) => fn(this(here), here));
120
+ },
121
+ only(this: Field<unknown>, test: (light: Light) => unknown) {
122
+ return make((here) => (test(here.light) ? this(here) : null));
123
+ },
124
+ pow(this: Field<number>, k: Param) {
125
+ return make((here) => Math.max(0, this(here)) ** read(k, here));
126
+ },
127
+ steps(this: Field<number>, n: Param) {
128
+ return make((here) => {
129
+ const count = Math.max(2, Math.round(read(n, here)));
130
+ return Math.min(count - 1, Math.floor(clamp(this(here)) * count)) / (count - 1);
131
+ });
132
+ },
133
+ thresh(this: Field<number>, level: Param = 0.5) {
134
+ return make((here) => (this(here) >= read(level, here) ? 1 : 0));
135
+ },
136
+ invert(this: Field<number>) {
137
+ return make((here) => 1 - this(here));
138
+ },
139
+ mul(this: Field<number>, k: Param) {
140
+ return make((here) => this(here) * read(k, here));
141
+ },
142
+ add(this: Field<number | ColorLike>, other: Param | ColorParam) {
143
+ return make((here) => {
144
+ const v = this(here);
145
+ return typeof v === "number" ? v + read(other as Param, here) : color(v).add(read(other as ColorParam, here));
146
+ });
147
+ },
148
+ mix(this: Field<number | ColorLike>, other: Param | ColorParam, t: Param = 0.5) {
149
+ return make((here) => {
150
+ const v = this(here);
151
+ const k = read(t, here);
152
+ return typeof v === "number" ? lerp(v, read(other as Param, here), k) : color(v).mix(read(other as ColorParam, here), k);
153
+ });
154
+ },
155
+ color(this: Field<number>, by: unknown) {
156
+ if (isField(by)) return make((here) => toPalette(by(here))(this(here)));
157
+ if (isPalette(by)) return make((here) => by(this(here)));
158
+ if (typeof by === "function") return make((here) => color((by as (value: number, here: Here) => ColorInput)(this(here), here)));
159
+ const p = toPalette(by);
160
+ return make((here) => p(this(here)));
161
+ },
162
+ at(this: Field<ColorLike>, lm: Param) {
163
+ return make((here) => color(this(here)).at(read(lm, here)));
164
+ },
165
+ dim(this: Field<ColorLike>, k: Param) {
166
+ return make((here) => color(this(here)).dim(read(k, here)));
167
+ },
168
+ fade(this: Field<ColorLike>, k: Param) {
169
+ return make((here) => color(this(here)).fade(read(k, here)));
170
+ },
171
+ boost(this: Field<ColorLike>, by: Param, amount: Param = 1) {
172
+ return make((here) => color(this(here)).dim(1 + read(amount, here) * read(by, here)));
173
+ },
174
+ shiftHue(this: Field<ColorLike>, degrees: Param) {
175
+ return make((here) => color(this(here)).shiftHue(read(degrees, here)));
176
+ },
177
+ saturate(this: Field<ColorLike>, k: Param) {
178
+ return make((here) => color(this(here)).saturate(read(k, here)));
179
+ },
180
+ vivid(this: Field<ColorLike>, bulb?: Bulb) {
181
+ return make((here) => color(this(here)).vivid(bulb));
182
+ },
183
+ };
184
+
185
+ const proto: object = Object.setPrototypeOf(methods, Function.prototype);
186
+
187
+ function make<T>(fn: (here: Here) => T): Field<T> {
188
+ return Object.setPrototypeOf(fn, proto) as Field<T>;
189
+ }
190
+
191
+ /** Your own function of here as a field, to chain onto. */
192
+ export function field<T>(fn: (here: Here) => T): Field<T> {
193
+ return isField(fn) ? (fn as Field<T>) : make((here) => fn(here));
194
+ }
195
+
196
+ /** Whether v is a field (made by a source, field(), or a chain). */
197
+ export const isField = (v: unknown): v is Field<unknown> => typeof v === "function" && Object.getPrototypeOf(v) === proto;
198
+
199
+ // ---- sources --------------------------------------------------------------------
200
+
201
+ /** Here as room units: centered on the room, 1 = its width, the same scale both ways. */
202
+ function unit(here: Here): [number, number] {
203
+ const { center, width } = here.room;
204
+ return [(here.x - center.x) / width, (here.y - center.y) / width];
205
+ }
206
+
207
+ /** Smooth bands, `n` across the room (left to right), each light going round `speed` times a second. */
208
+ export function osc(n: Param = 3, speed: Param = 0.1, phase: Param = 0): Field<number> {
209
+ return make((here) => wave(unit(here)[0] * read(n, here) - here.t * read(speed, here) + read(phase, here)));
210
+ }
211
+
212
+ /** Smooth noise, blobs about `n` across the room, changing about `speed` times a second. */
213
+ export function noise(n: Param = 3, speed: Param = 0.1): Field<number> {
214
+ return make((here) => {
215
+ const [s, w] = unit(here);
216
+ const k = read(n, here);
217
+ return noiseAt(s * k + 7.31, w * k + 3.17, here.t * read(speed, here));
218
+ });
219
+ }
220
+
221
+ /** Rings out from the room's center, `n` of them out to its left and right ends, moving out `speed` times a second. */
222
+ export function rings(n: Param = 3, speed: Param = 0.1): Field<number> {
223
+ return make((here) => {
224
+ const [s, w] = unit(here);
225
+ return wave(2 * Math.hypot(s, w) * read(n, here) - here.t * read(speed, here));
226
+ });
227
+ }
228
+
229
+ /** 0 to 1 across the room: left to right at 0°, top to bottom at 90°. */
230
+ export function gradient(degrees: Param = 0): Field<number> {
231
+ return make((here) => {
232
+ const r = (read(degrees, here) * Math.PI) / 180;
233
+ const dx = Math.cos(r);
234
+ const dy = Math.sin(r);
235
+ const { center, width, depth } = here.room;
236
+ const reach = (width * Math.abs(dx) + depth * Math.abs(dy)) / 2;
237
+ return clamp(0.5 + ((here.x - center.x) * dx + (here.y - center.y) * dy) / (2 * reach));
238
+ });
239
+ }
240
+
241
+ /** Cells, about `n` across the room, their centers drifting `speed` times a second: 0 at a cell's center, rising toward its edges. */
242
+ export function voronoi(n: Param = 4, speed: Param = 0.1): Field<number> {
243
+ return make((here) => {
244
+ const [s, w] = unit(here);
245
+ const k = read(n, here);
246
+ const x = s * k;
247
+ const y = w * k;
248
+ const phase = here.t * read(speed, here) * 2 * Math.PI;
249
+ const ix = Math.floor(x);
250
+ const iy = Math.floor(y);
251
+ let best = Infinity;
252
+ for (let dx = -1; dx <= 1; dx++) {
253
+ for (let dy = -1; dy <= 1; dy++) {
254
+ const cx = ix + dx;
255
+ const cy = iy + dy;
256
+ const px = cx + 0.5 + 0.35 * Math.sin(phase + 2 * Math.PI * hash(cx, cy, 1));
257
+ const py = cy + 0.5 + 0.35 * Math.cos(phase + 2 * Math.PI * hash(cx, cy, 2));
258
+ best = Math.min(best, Math.hypot(x - px, y - py));
259
+ }
260
+ }
261
+ return clamp(best);
262
+ });
263
+ }
264
+
265
+ /** The same value everywhere: solid(0.5), or a color, solid(white(2700, 300)). */
266
+ export function solid<T>(value: T): Field<T> {
267
+ return make(() => value);
268
+ }
269
+
270
+ // ---- transforms, as plain functions -----------------------------------------------
271
+
272
+ /** Here, looking as if the pattern were turned by `degrees` around the room's center. */
273
+ export function rotate(here: Here, degrees: number): Here {
274
+ const { center } = here.room;
275
+ const r = (-degrees * Math.PI) / 180;
276
+ const c = Math.cos(r);
277
+ const s = Math.sin(r);
278
+ const dx = here.x - center.x;
279
+ const dy = here.y - center.y;
280
+ return here.at({ x: center.x + dx * c - dy * s, y: center.y + dx * s + dy * c });
281
+ }
282
+
283
+ /** Here, looking as if the pattern were k times as big, from the room's center. */
284
+ export function scale(here: Here, k: number): Here {
285
+ const { center } = here.room;
286
+ if (k === 0) return here.at({ x: center.x, y: center.y });
287
+ return here.at({ x: center.x + (here.x - center.x) / k, y: center.y + (here.y - center.y) / k });
288
+ }
289
+
290
+ /** Here, looking as if the pattern were moved dx meters right and dy down. */
291
+ export function move(here: Here, dx: number, dy = 0): Here {
292
+ return here.at({ x: here.x - dx, y: here.y - dy });
293
+ }
294
+
295
+ /** Here, folded into n mirrored slices around the room's center. */
296
+ export function kaleid(here: Here, n: number): Here {
297
+ if (!(n >= 1)) return here;
298
+ const { center } = here.room;
299
+ const dx = here.x - center.x;
300
+ const dy = here.y - center.y;
301
+ const slice = (2 * Math.PI) / n;
302
+ let a = Math.atan2(dy, dx);
303
+ a = ((a % slice) + slice) % slice;
304
+ a = Math.abs(a - slice / 2);
305
+ const r = Math.hypot(dx, dy);
306
+ return here.at({ x: center.x + r * Math.cos(a), y: center.y + r * Math.sin(a) });
307
+ }
308
+
309
+ /** Here, pushed up to `amount` meters each way by `by` (none where it's 0.5). */
310
+ export function warp(here: Here, by: (here: Here) => number, amount = 1): Here {
311
+ const ox = (by(here) - 0.5) * 2 * amount;
312
+ // The push down comes from somewhere else in `by`, so it isn't the same as the push right.
313
+ const oy = (by(here.at({ x: here.x + 37.7, y: here.y - 19.1 })) - 0.5) * 2 * amount;
314
+ return here.at({ x: here.x + ox, y: here.y + oy });
315
+ }
316
+
317
+ /** Here, `seconds` earlier. */
318
+ export const delay = (here: Here, seconds: number): Here => here.at({ t: here.t - seconds });
319
+
320
+ /** Here, with time going k times as fast. */
321
+ export const speed = (here: Here, k: number): Here => here.at({ t: here.t * k });
@@ -0,0 +1,156 @@
1
+ // Made by scripts/gamut.ts from vibes' bulb model (Walter's lemtone solve): don't edit.
2
+ // What a color bulb makes without paling a color: for every 2.5° of hue from 0,
3
+ // the most chroma it makes (EDGES), and the most lumens, in tenths, at 20
4
+ // even steps from white out to that (MOST_TENTHS).
5
+
6
+ export const HUE_STEP = 2.5;
7
+ export const STEPS = 20;
8
+
9
+ export const EDGES: readonly number[] = [0.53825, 0.53219, 0.52685, 0.52225, 0.51837, 0.51521, 0.51279, 0.51109, 0.51012, 0.50989, 0.51042, 0.47176, 0.43321, 0.40131, 0.37452, 0.35177, 0.33225, 0.31538, 0.30071, 0.28788, 0.27662, 0.2667, 0.25796, 0.25023, 0.24342, 0.2374, 0.23212, 0.22749, 0.22346, 0.21999, 0.21704, 0.21457, 0.21255, 0.21098, 0.20982, 0.20908, 0.20874, 0.2088, 0.20926, 0.21013, 0.21141, 0.21312, 0.21526, 0.21788, 0.22099, 0.22462, 0.22882, 0.23364, 0.23913, 0.24537, 0.25245, 0.26047, 0.26954, 0.27983, 0.29152, 0.30485, 0.32011, 0.33766, 0.358, 0.38174, 0.40973, 0.44311, 0.4835, 0.53321, 0.54008, 0.51525, 0.49354, 0.47449, 0.45773, 0.44295, 0.42991, 0.41841, 0.40828, 0.39939, 0.39163, 0.38489, 0.3791, 0.3742, 0.37014, 0.36686, 0.36434, 0.36255, 0.36147, 0.3611, 0.36142, 0.36245, 0.36419, 0.36667, 0.36989, 0.37391, 0.37876, 0.3845, 0.39119, 0.3989, 0.40775, 0.41783, 0.4293, 0.44231, 0.45708, 0.47385, 0.49294, 0.51473, 0.53973, 0.56855, 0.60203, 0.64128, 0.6878, 0.74375, 0.81173, 0.89359, 0.93361, 0.91853, 0.90439, 0.89106, 0.87843, 0.86636, 0.85475, 0.84348, 0.83243, 0.82151, 0.8106, 0.79961, 0.78845, 0.77706, 0.76537, 0.75336, 0.74102, 0.72836, 0.71542, 0.70226, 0.68897, 0.67565, 0.66239, 0.6493, 0.63649, 0.62405, 0.61206, 0.60059, 0.5897, 0.57944, 0.56984, 0.56086, 0.5526, 0.54506];
10
+
11
+ export const MOST_TENTHS: readonly (readonly number[])[] = [
12
+ [8081, 7829, 7661, 5423, 3996, 3135, 2553, 2132, 1814, 1567, 1369, 1207, 1073, 959, 863, 779, 707, 644, 588, 538, 494],
13
+ [8081, 7876, 7722, 5562, 4095, 3210, 2612, 2178, 1851, 1597, 1393, 1226, 1087, 971, 871, 785, 711, 646, 588, 537, 492],
14
+ [8081, 7922, 7784, 5715, 4205, 3294, 2676, 2229, 1892, 1629, 1419, 1247, 1104, 983, 880, 792, 715, 646, 586, 535, 491],
15
+ [8081, 7957, 7847, 5883, 4325, 3386, 2748, 2286, 1937, 1665, 1448, 1270, 1122, 997, 891, 797, 713, 644, 584, 533, 489],
16
+ [8081, 7981, 7911, 6066, 4457, 3485, 2825, 2349, 1988, 1706, 1480, 1296, 1142, 1013, 896, 795, 712, 642, 583, 532, 488],
17
+ [8081, 8004, 7975, 6267, 4601, 3596, 2912, 2418, 2043, 1751, 1516, 1324, 1165, 1020, 894, 793, 710, 641, 581, 531, 487],
18
+ [8081, 8028, 8041, 6490, 4761, 3718, 3008, 2495, 2105, 1801, 1556, 1356, 1178, 1018, 893, 792, 709, 639, 580, 529, 485],
19
+ [8081, 8052, 8082, 6739, 4939, 3859, 3115, 2581, 2174, 1856, 1601, 1389, 1177, 1017, 892, 791, 708, 638, 579, 528, 484],
20
+ [8081, 8076, 8141, 7020, 5139, 4014, 3240, 2677, 2252, 1918, 1651, 1388, 1177, 1017, 891, 790, 707, 637, 578, 527, 483],
21
+ [8081, 8101, 8201, 7341, 5366, 4188, 3377, 2786, 2339, 1989, 1684, 1389, 1177, 1017, 891, 790, 706, 637, 577, 527, 483],
22
+ [8081, 8126, 8264, 7713, 5625, 4388, 3533, 2910, 2438, 2068, 1686, 1390, 1177, 1017, 891, 789, 706, 636, 577, 526, 482],
23
+ [8081, 8149, 8281, 8673, 6483, 5066, 4102, 3389, 2849, 2425, 2014, 1640, 1376, 1181, 1031, 911, 813, 731, 662, 581, 10],
24
+ [8081, 8169, 8298, 8633, 7601, 5928, 4824, 4004, 3378, 2886, 2484, 1992, 1651, 1404, 1217, 1070, 952, 843, 721, 631, 10],
25
+ [8081, 8187, 8317, 8611, 8965, 6941, 5654, 4718, 3989, 3418, 2957, 2414, 1976, 1662, 1429, 1249, 1107, 914, 777, 678, 10],
26
+ [8081, 8203, 8350, 8600, 9076, 8168, 6616, 5551, 4711, 4046, 3508, 2941, 2362, 1967, 1674, 1453, 1206, 982, 831, 722, 10],
27
+ [8081, 8218, 8359, 8596, 9034, 9655, 7833, 6537, 5585, 4801, 4172, 3624, 2837, 2325, 1963, 1689, 1298, 1048, 882, 764, 10],
28
+ [8081, 8232, 8369, 8606, 8997, 9540, 9502, 7833, 6662, 5751, 5001, 4383, 3441, 2763, 2300, 1887, 1390, 1113, 932, 804, 10],
29
+ [8081, 8245, 8379, 8606, 8969, 9460, 9995, 9258, 8165, 7015, 6092, 5339, 4261, 3312, 2710, 2040, 1482, 1177, 981, 843, 10],
30
+ [8081, 8257, 8388, 8609, 8947, 9393, 10036, 9374, 8672, 7993, 7386, 6696, 5447, 4042, 3221, 2191, 1574, 1239, 1028, 881, 10],
31
+ [8081, 8268, 8398, 8615, 8930, 9345, 9903, 9463, 8819, 8163, 7568, 7038, 6571, 5070, 3729, 2346, 1668, 1301, 1073, 917, 10],
32
+ [8081, 8278, 8408, 8623, 8919, 9301, 9777, 9538, 8949, 8327, 7743, 7223, 6746, 6524, 4091, 2506, 1762, 1362, 1118, 952, 10],
33
+ [8081, 8288, 8418, 8634, 8911, 9267, 9679, 9614, 9058, 8467, 7899, 7389, 6924, 6645, 4482, 2672, 1857, 1423, 1162, 987, 10],
34
+ [8081, 8298, 8428, 8645, 8906, 9242, 9602, 9684, 9150, 8595, 8052, 7553, 7097, 6765, 4908, 2842, 1955, 1483, 1206, 1020, 10],
35
+ [8081, 8306, 8437, 8656, 8904, 9225, 9542, 9732, 9227, 8718, 8195, 7710, 7265, 6872, 5373, 3018, 2054, 1544, 1249, 1054, 10],
36
+ [8081, 8315, 8447, 8667, 8904, 9207, 9492, 9761, 9284, 8821, 8321, 7849, 7414, 7009, 5879, 3202, 2156, 1605, 1292, 1087, 10],
37
+ [8081, 8323, 8456, 8679, 8906, 9193, 9452, 9775, 9338, 8913, 8446, 7988, 7566, 7171, 6409, 3394, 2262, 1667, 1335, 1119, 10],
38
+ [8081, 8331, 8466, 8691, 8909, 9182, 9420, 9754, 9389, 8993, 8553, 8120, 7713, 7319, 7011, 3596, 2372, 1729, 1379, 1152, 10],
39
+ [8081, 8339, 8475, 8700, 8914, 9175, 9396, 9677, 9430, 9063, 8659, 8243, 7850, 7472, 7174, 3811, 2487, 1794, 1422, 1185, 10],
40
+ [8081, 8347, 8484, 8702, 8919, 9169, 9376, 9608, 9456, 9120, 8756, 8359, 7980, 7619, 7277, 4039, 2600, 1860, 1467, 1217, 10],
41
+ [8081, 8354, 8494, 8714, 8926, 9165, 9360, 9552, 9469, 9166, 8834, 8471, 8116, 7762, 7424, 4292, 2718, 1928, 1512, 1251, 10],
42
+ [8081, 8361, 8503, 8726, 8933, 9161, 9332, 9504, 9236, 8734, 8349, 7910, 7556, 7333, 7094, 4575, 2843, 1999, 1558, 1285, 10],
43
+ [8081, 8369, 8512, 8738, 8942, 9136, 9302, 9309, 8663, 8116, 7679, 7269, 6876, 6584, 6347, 4885, 2974, 2073, 1606, 1319, 10],
44
+ [8081, 8376, 8522, 8751, 8923, 9100, 9271, 8852, 8189, 7625, 7159, 6746, 6334, 6006, 5705, 5229, 3114, 2150, 1654, 1354, 10],
45
+ [8081, 8383, 8531, 8739, 8903, 9065, 9243, 8477, 7793, 7219, 6732, 6305, 5891, 5539, 5216, 4926, 3263, 2231, 1705, 1390, 10],
46
+ [8081, 8390, 8541, 8717, 8873, 9030, 8980, 8154, 7455, 6871, 6376, 5934, 5521, 5167, 4862, 4552, 3424, 2317, 1758, 1428, 10],
47
+ [8081, 8381, 8551, 8696, 8844, 8996, 8706, 7861, 7149, 6561, 6058, 5611, 5205, 4850, 4535, 4225, 3599, 2408, 1813, 1466, 10],
48
+ [8081, 8371, 8554, 8674, 8815, 8961, 8439, 7580, 6864, 6277, 5775, 5340, 4931, 4580, 4265, 3959, 3616, 2506, 1871, 1507, 10],
49
+ [8081, 8361, 8539, 8653, 8786, 8937, 8189, 7324, 6612, 6024, 5524, 5092, 4691, 4345, 4036, 3738, 3449, 2611, 1932, 1549, 10],
50
+ [8081, 8351, 8525, 8631, 8757, 8902, 7960, 7087, 6379, 5795, 5329, 4870, 4478, 4139, 3828, 3540, 3263, 2726, 1998, 1593, 10],
51
+ [8081, 8341, 8510, 8610, 8728, 8820, 7739, 6871, 6164, 5605, 5120, 4669, 4287, 3955, 3659, 3379, 3115, 2737, 2068, 1640, 10],
52
+ [8081, 8331, 8496, 8599, 8699, 8615, 7529, 6663, 5964, 5419, 4925, 4485, 4113, 3790, 3502, 3237, 2983, 2690, 2143, 1691, 10],
53
+ [8081, 8321, 8481, 8579, 8669, 8424, 7329, 6467, 5777, 5235, 4744, 4316, 3955, 3641, 3361, 3103, 2858, 2615, 2226, 1744, 10],
54
+ [8081, 8311, 8466, 8556, 8640, 8229, 7141, 6280, 5627, 5062, 4575, 4160, 3809, 3504, 3232, 2983, 2747, 2516, 2204, 1802, 10],
55
+ [8081, 8301, 8458, 8533, 8609, 8039, 6954, 6103, 5453, 4900, 4417, 4014, 3673, 3377, 3114, 2873, 2647, 2426, 2171, 1865, 10],
56
+ [8081, 8290, 8443, 8509, 8578, 7852, 6772, 5962, 5288, 4743, 4269, 3878, 3547, 3260, 3005, 2773, 2556, 2346, 2135, 1870, 10],
57
+ [8081, 8279, 8427, 8485, 8547, 7669, 6596, 5793, 5133, 4588, 4128, 3749, 3428, 3150, 2904, 2680, 2472, 2272, 2073, 1844, 10],
58
+ [8081, 8267, 8410, 8459, 8515, 7494, 6426, 5631, 4984, 4440, 3994, 3626, 3315, 3046, 2809, 2593, 2394, 2204, 2017, 1818, 10],
59
+ [8081, 8256, 8392, 8434, 8482, 7317, 6260, 5474, 4838, 4298, 3865, 3509, 3208, 2948, 2719, 2512, 2321, 2140, 1964, 1788, 10],
60
+ [8081, 8243, 8374, 8407, 8448, 7140, 6123, 5319, 4684, 4161, 3742, 3397, 3106, 2855, 2634, 2435, 2252, 2081, 1915, 1751, 10],
61
+ [8081, 8231, 8355, 8378, 8363, 6962, 5954, 5167, 4534, 4028, 3622, 3289, 3008, 2766, 2553, 2362, 2187, 2024, 1868, 1715, 10],
62
+ [8081, 8217, 8335, 8349, 8174, 6784, 5786, 5017, 4387, 3898, 3506, 3184, 2913, 2679, 2475, 2292, 2125, 1970, 1824, 1681, 10],
63
+ [8081, 8203, 8313, 8318, 7988, 6605, 5620, 4855, 4244, 3772, 3393, 3082, 2821, 2596, 2400, 2224, 2065, 1919, 1780, 1648, 10],
64
+ [8081, 8188, 8291, 8285, 7789, 6449, 5453, 4692, 4103, 3647, 3283, 2983, 2731, 2515, 2327, 2159, 2008, 1869, 1739, 1615, 10],
65
+ [8081, 8171, 8267, 8250, 7585, 6258, 5286, 4531, 3964, 3525, 3174, 2885, 2643, 2436, 2256, 2096, 1952, 1820, 1698, 1583, 10],
66
+ [8081, 8154, 8241, 8212, 7375, 6065, 5106, 4372, 3826, 3404, 3066, 2789, 2557, 2358, 2186, 2033, 1897, 1773, 1658, 1550, 10],
67
+ [8081, 8135, 8213, 8172, 7158, 5869, 4917, 4212, 3688, 3283, 2959, 2694, 2472, 2282, 2117, 1972, 1843, 1726, 1618, 1518, 10],
68
+ [8081, 8114, 8182, 8128, 6961, 5670, 4728, 4053, 3551, 3163, 2853, 2599, 2387, 2206, 2050, 1912, 1790, 1679, 1579, 1486, 10],
69
+ [8081, 8091, 8141, 8080, 6719, 5453, 4537, 3892, 3413, 3042, 2747, 2505, 2303, 2131, 1982, 1852, 1737, 1633, 1539, 1453, 10],
70
+ [8081, 8066, 8090, 8027, 6469, 5216, 4344, 3730, 3274, 2921, 2640, 2410, 2218, 2055, 1915, 1792, 1684, 1587, 1499, 1419, 10],
71
+ [8081, 8038, 8032, 7968, 6208, 4974, 4148, 3566, 3133, 2799, 2532, 2315, 2133, 1980, 1847, 1732, 1630, 1540, 1459, 1385, 10],
72
+ [8081, 8005, 7968, 7717, 5923, 4728, 3948, 3398, 2990, 2675, 2423, 2218, 2047, 1903, 1779, 1671, 1577, 1493, 1417, 1350, 10],
73
+ [8081, 7968, 7894, 7362, 5596, 4475, 3743, 3227, 2844, 2548, 2312, 2120, 1960, 1825, 1710, 1610, 1522, 1444, 1375, 1313, 10],
74
+ [8081, 7925, 7808, 6982, 5259, 4215, 3532, 3051, 2694, 2418, 2199, 2020, 1871, 1746, 1639, 1547, 1466, 1394, 1331, 1274, 10],
75
+ [8081, 7873, 7708, 6490, 4872, 3927, 3308, 2870, 2539, 2285, 2082, 1917, 1780, 1665, 1567, 1482, 1408, 1343, 1285, 1234, 10],
76
+ [8081, 7854, 7646, 6369, 4787, 3859, 3248, 2817, 2495, 2247, 2049, 1888, 1755, 1642, 1546, 1463, 1391, 1327, 1271, 1221, 10],
77
+ [8081, 7858, 7654, 6425, 4827, 3889, 3272, 2836, 2512, 2261, 2061, 1899, 1764, 1650, 1553, 1469, 1396, 1332, 1275, 1225, 10],
78
+ [8081, 7862, 7660, 6478, 4864, 3917, 3295, 2855, 2527, 2274, 2073, 1909, 1773, 1658, 1560, 1476, 1402, 1337, 1280, 1229, 10],
79
+ [8081, 7865, 7667, 6527, 4899, 3944, 3316, 2872, 2542, 2287, 2084, 1918, 1781, 1666, 1567, 1482, 1407, 1342, 1284, 1233, 10],
80
+ [8081, 7869, 7673, 6574, 4932, 3969, 3336, 2889, 2556, 2299, 2094, 1928, 1789, 1673, 1574, 1488, 1413, 1347, 1289, 1237, 10],
81
+ [8081, 7872, 7678, 6618, 4963, 3993, 3356, 2905, 2570, 2311, 2105, 1937, 1798, 1680, 1580, 1494, 1418, 1352, 1293, 1241, 10],
82
+ [8081, 7874, 7683, 6660, 4993, 4016, 3374, 2921, 2583, 2322, 2115, 1946, 1805, 1687, 1586, 1499, 1424, 1357, 1298, 1245, 10],
83
+ [8081, 7877, 7688, 6701, 5022, 4039, 3393, 2936, 2596, 2333, 2124, 1954, 1813, 1694, 1593, 1505, 1429, 1362, 1302, 1249, 10],
84
+ [8081, 7880, 7693, 6739, 5050, 4060, 3410, 2950, 2609, 2344, 2134, 1963, 1821, 1701, 1599, 1511, 1434, 1367, 1307, 1253, 10],
85
+ [8081, 7882, 7697, 6777, 5077, 4081, 3427, 2965, 2621, 2355, 2144, 1971, 1829, 1708, 1605, 1517, 1439, 1371, 1311, 1258, 10],
86
+ [8081, 7884, 7702, 6814, 5104, 4102, 3444, 2979, 2633, 2366, 2153, 1980, 1836, 1715, 1612, 1522, 1445, 1376, 1316, 1262, 10],
87
+ [8081, 7886, 7706, 6849, 5130, 4122, 3460, 2993, 2645, 2376, 2162, 1988, 1844, 1722, 1618, 1528, 1450, 1381, 1320, 1266, 10],
88
+ [8081, 7889, 7710, 6884, 5155, 4142, 3477, 3006, 2657, 2386, 2171, 1996, 1851, 1729, 1624, 1534, 1455, 1386, 1325, 1271, 10],
89
+ [8081, 7891, 7714, 6918, 5180, 4161, 3493, 3020, 2668, 2397, 2181, 2005, 1859, 1736, 1631, 1540, 1461, 1391, 1330, 1275, 10],
90
+ [8081, 7893, 7718, 6952, 5204, 4181, 3509, 3034, 2680, 2407, 2190, 2013, 1866, 1743, 1637, 1546, 1466, 1397, 1335, 1280, 10],
91
+ [8081, 7895, 7721, 6986, 5229, 4200, 3524, 3047, 2692, 2418, 2199, 2022, 1874, 1750, 1644, 1552, 1472, 1402, 1340, 1284, 10],
92
+ [8081, 7897, 7725, 7019, 5253, 4219, 3540, 3061, 2704, 2428, 2209, 2030, 1882, 1757, 1650, 1558, 1478, 1407, 1345, 1289, 10],
93
+ [8081, 7899, 7729, 7052, 5278, 4239, 3556, 3074, 2716, 2439, 2218, 2039, 1890, 1764, 1657, 1565, 1484, 1413, 1350, 1294, 10],
94
+ [8081, 7901, 7732, 7082, 5302, 4258, 3573, 3088, 2728, 2450, 2228, 2048, 1898, 1772, 1664, 1571, 1490, 1419, 1356, 1299, 10],
95
+ [8081, 7902, 7736, 7109, 5327, 4278, 3589, 3102, 2740, 2461, 2238, 2057, 1906, 1780, 1671, 1578, 1496, 1425, 1361, 1305, 10],
96
+ [8081, 7904, 7740, 7136, 5352, 4298, 3606, 3117, 2753, 2472, 2248, 2066, 1915, 1788, 1679, 1585, 1503, 1431, 1367, 1310, 10],
97
+ [8081, 7906, 7744, 7163, 5378, 4318, 3623, 3131, 2766, 2483, 2259, 2076, 1924, 1796, 1687, 1592, 1510, 1437, 1373, 1316, 10],
98
+ [8081, 7908, 7747, 7191, 5403, 4339, 3640, 3146, 2779, 2495, 2270, 2086, 1933, 1805, 1695, 1600, 1517, 1444, 1380, 1322, 10],
99
+ [8081, 7910, 7751, 7219, 5430, 4360, 3658, 3162, 2793, 2508, 2281, 2096, 1943, 1814, 1703, 1608, 1525, 1452, 1387, 1329, 10],
100
+ [8081, 7912, 7755, 7248, 5458, 4382, 3677, 3178, 2807, 2521, 2293, 2107, 1953, 1823, 1712, 1616, 1533, 1459, 1394, 1336, 10],
101
+ [8081, 7914, 7759, 7278, 5486, 4405, 3696, 3195, 2822, 2534, 2305, 2119, 1964, 1833, 1722, 1625, 1541, 1467, 1402, 1343, 10],
102
+ [8081, 7916, 7763, 7308, 5515, 4429, 3716, 3213, 2838, 2549, 2318, 2131, 1975, 1844, 1732, 1635, 1550, 1476, 1410, 1351, 10],
103
+ [8081, 7918, 7767, 7340, 5542, 4454, 3737, 3231, 2855, 2564, 2332, 2144, 1987, 1855, 1743, 1645, 1560, 1486, 1419, 1360, 10],
104
+ [8081, 7921, 7772, 7374, 5570, 4480, 3760, 3251, 2872, 2580, 2347, 2158, 2000, 1868, 1754, 1656, 1571, 1496, 1429, 1370, 10],
105
+ [8081, 7923, 7776, 7409, 5598, 4508, 3783, 3272, 2891, 2597, 2363, 2172, 2014, 1881, 1767, 1669, 1583, 1507, 1440, 1380, 10],
106
+ [8081, 7925, 7781, 7445, 5629, 4537, 3808, 3294, 2911, 2615, 2380, 2189, 2030, 1895, 1781, 1682, 1596, 1520, 1452, 1392, 10],
107
+ [8081, 7928, 7786, 7483, 5662, 4568, 3835, 3318, 2933, 2635, 2399, 2206, 2046, 1911, 1796, 1697, 1610, 1533, 1466, 1405, 10],
108
+ [8081, 7930, 7791, 7523, 5697, 4602, 3865, 3344, 2957, 2657, 2419, 2225, 2065, 1929, 1813, 1713, 1626, 1549, 1481, 1420, 10],
109
+ [8081, 7933, 7797, 7566, 5735, 4639, 3897, 3372, 2983, 2681, 2442, 2247, 2085, 1949, 1832, 1731, 1644, 1566, 1498, 1437, 10],
110
+ [8081, 7936, 7803, 7611, 5774, 4677, 3931, 3404, 3011, 2708, 2467, 2271, 2108, 1971, 1854, 1752, 1664, 1586, 1518, 1456, 10],
111
+ [8081, 7939, 7809, 7658, 5817, 4715, 3970, 3438, 3043, 2738, 2496, 2298, 2134, 1996, 1878, 1777, 1688, 1610, 1541, 1479, 10],
112
+ [8081, 7943, 7816, 7673, 5864, 4757, 4013, 3478, 3080, 2772, 2528, 2329, 2165, 2026, 1907, 1805, 1716, 1638, 1568, 1459, 10],
113
+ [8081, 7946, 7824, 7668, 5916, 4805, 4062, 3522, 3121, 2812, 2566, 2366, 2200, 2060, 1941, 1839, 1733, 1585, 1454, 1337, 10],
114
+ [8081, 7950, 7832, 7666, 5974, 4858, 4117, 3573, 3169, 2857, 2610, 2409, 2242, 2102, 1934, 1752, 1592, 1452, 1328, 1216, 10],
115
+ [8081, 7955, 7830, 7662, 6039, 4920, 4175, 3633, 3226, 2912, 2663, 2460, 2209, 1976, 1776, 1604, 1452, 1319, 1201, 1096, 10],
116
+ [8081, 7960, 7827, 7652, 6112, 4991, 4243, 3704, 3294, 2978, 2601, 2289, 2028, 1808, 1618, 1454, 1311, 1186, 1075, 976, 10],
117
+ [8081, 7966, 7822, 7642, 6196, 5075, 4326, 3762, 3194, 2747, 2386, 2091, 1845, 1636, 1458, 1304, 1170, 1052, 948, 855, 10],
118
+ [8081, 7972, 7816, 7623, 6290, 5138, 4176, 3469, 2930, 2506, 2167, 1889, 1657, 1462, 1295, 1152, 1027, 917, 821, 735, 10],
119
+ [8081, 7979, 7803, 7597, 6054, 4750, 3834, 3164, 2656, 2260, 1941, 1682, 1466, 1285, 1130, 997, 882, 781, 692, 613, 10],
120
+ [8081, 7988, 7789, 7482, 5586, 4340, 3475, 2847, 2374, 2004, 1709, 1469, 1271, 1104, 963, 841, 736, 644, 563, 491, 310],
121
+ [8081, 7999, 7766, 6937, 5099, 3918, 3109, 2527, 2088, 1748, 1477, 1258, 1076, 925, 796, 686, 591, 509, 436, 372, 234],
122
+ [8081, 8017, 7772, 6761, 4935, 3771, 2978, 2408, 1980, 1648, 1385, 1172, 997, 851, 728, 623, 532, 453, 383, 323, 10],
123
+ [8081, 8038, 7812, 7030, 5133, 3923, 3100, 2504, 2057, 1713, 1440, 1220, 1039, 888, 761, 651, 558, 476, 404, 340, 10],
124
+ [8081, 8059, 7854, 7230, 5353, 4092, 3231, 2606, 2142, 1784, 1502, 1273, 1086, 929, 797, 684, 586, 500, 424, 358, 10],
125
+ [8081, 8079, 7898, 7276, 5600, 4285, 3370, 2719, 2235, 1863, 1570, 1332, 1137, 974, 836, 716, 613, 524, 446, 378, 10],
126
+ [8081, 8099, 7944, 7331, 5878, 4482, 3523, 2844, 2340, 1952, 1645, 1398, 1190, 1016, 871, 747, 641, 550, 470, 401, 10],
127
+ [8081, 8119, 7993, 7395, 6195, 4697, 3694, 2983, 2450, 2037, 1711, 1450, 1236, 1058, 909, 782, 673, 579, 497, 426, 10],
128
+ [8081, 8138, 8012, 7468, 6438, 4874, 3824, 3081, 2530, 2107, 1774, 1506, 1287, 1104, 951, 820, 708, 612, 527, 453, 10],
129
+ [8081, 8125, 7956, 7551, 6582, 5015, 3942, 3182, 2618, 2185, 1844, 1568, 1343, 1155, 997, 863, 747, 648, 560, 484, 10],
130
+ [8081, 8073, 7900, 7640, 6740, 5171, 4072, 3294, 2716, 2271, 1920, 1637, 1405, 1212, 1049, 910, 791, 688, 597, 518, 10],
131
+ [8081, 8022, 7843, 7588, 6914, 5343, 4217, 3418, 2824, 2367, 2006, 1714, 1475, 1275, 1107, 963, 840, 733, 639, 557, 10],
132
+ [8081, 7974, 7783, 7531, 7055, 5523, 4378, 3556, 2944, 2473, 2100, 1799, 1552, 1345, 1171, 1022, 894, 783, 686, 600, 10],
133
+ [8081, 7927, 7723, 7473, 7179, 5715, 4556, 3709, 3078, 2591, 2206, 1895, 1638, 1424, 1243, 1088, 955, 839, 738, 649, 10],
134
+ [8081, 7883, 7666, 7403, 7104, 5588, 4537, 3761, 3183, 2723, 2324, 2001, 1735, 1512, 1324, 1163, 1024, 903, 797, 704, 10],
135
+ [8081, 7840, 7600, 7332, 6418, 5039, 4096, 3416, 2900, 2494, 2167, 1898, 1673, 1482, 1318, 1176, 1051, 941, 843, 756, 10],
136
+ [8081, 7800, 7525, 7256, 5854, 4604, 3754, 3137, 2670, 2303, 2007, 1764, 1560, 1387, 1238, 1109, 996, 896, 806, 727, 10],
137
+ [8081, 7763, 7454, 7157, 5402, 4254, 3475, 2910, 2482, 2146, 1875, 1652, 1465, 1307, 1171, 1052, 948, 856, 774, 701, 10],
138
+ [8081, 7727, 7388, 6777, 5035, 3968, 3246, 2723, 2326, 2015, 1764, 1558, 1386, 1239, 1113, 1004, 907, 822, 746, 678, 10],
139
+ [8081, 7694, 7327, 6371, 4735, 3734, 3057, 2568, 2197, 1906, 1672, 1479, 1318, 1182, 1064, 962, 872, 793, 722, 658, 10],
140
+ [8081, 7664, 7271, 6036, 4488, 3542, 2902, 2440, 2090, 1815, 1595, 1413, 1262, 1133, 1022, 926, 841, 767, 700, 641, 10],
141
+ [8081, 7635, 7220, 5757, 4281, 3380, 2772, 2333, 2000, 1739, 1530, 1358, 1214, 1092, 987, 896, 816, 745, 682, 626, 10],
142
+ [8081, 7610, 7173, 5524, 4109, 3245, 2662, 2242, 1924, 1675, 1475, 1310, 1173, 1056, 956, 869, 793, 726, 666, 612, 10],
143
+ [8081, 7586, 7131, 5328, 3963, 3132, 2570, 2165, 1859, 1620, 1428, 1270, 1139, 1026, 930, 847, 774, 709, 652, 601, 10],
144
+ [8081, 7565, 7092, 5164, 3841, 3036, 2492, 2101, 1805, 1574, 1388, 1236, 1109, 1001, 908, 828, 757, 695, 640, 590, 10],
145
+ [8081, 7546, 7058, 5025, 3738, 2955, 2426, 2046, 1758, 1534, 1354, 1206, 1083, 979, 889, 811, 743, 683, 629, 581, 10],
146
+ [8081, 7528, 7028, 4906, 3650, 2885, 2370, 1999, 1719, 1500, 1325, 1181, 1061, 959, 872, 796, 730, 672, 620, 574, 10],
147
+ [8081, 7513, 7000, 4805, 3575, 2826, 2322, 1959, 1685, 1471, 1299, 1159, 1042, 942, 857, 783, 719, 662, 611, 566, 10],
148
+ [8081, 7499, 6976, 4721, 3512, 2777, 2281, 1925, 1656, 1446, 1278, 1140, 1025, 928, 845, 772, 709, 653, 604, 560, 10],
149
+ [8081, 7493, 7007, 4739, 3513, 2774, 2278, 1921, 1652, 1442, 1273, 1135, 1020, 923, 839, 767, 704, 648, 599, 555, 10],
150
+ [8081, 7542, 7101, 4808, 3560, 2808, 2303, 1939, 1665, 1451, 1279, 1139, 1022, 923, 839, 765, 701, 645, 595, 551, 10],
151
+ [8081, 7591, 7195, 4887, 3615, 2848, 2333, 1961, 1681, 1463, 1288, 1145, 1026, 925, 839, 765, 700, 643, 592, 548, 10],
152
+ [8081, 7639, 7291, 4974, 3676, 2894, 2367, 1987, 1701, 1478, 1300, 1153, 1032, 929, 841, 766, 700, 642, 590, 545, 10],
153
+ [8081, 7687, 7387, 5072, 3745, 2945, 2406, 2017, 1724, 1496, 1313, 1164, 1039, 935, 845, 768, 700, 641, 589, 543, 10],
154
+ [8081, 7735, 7484, 5179, 3822, 3003, 2450, 2051, 1751, 1517, 1330, 1176, 1049, 941, 850, 771, 702, 641, 588, 541, 10],
155
+ [8081, 7782, 7582, 5296, 3905, 3066, 2499, 2089, 1781, 1541, 1348, 1191, 1060, 950, 856, 774, 704, 642, 588, 539, 496],
156
+ ];
package/src/gamut.ts ADDED
@@ -0,0 +1,32 @@
1
+ // What a color bulb (br30) can make, from a table of vibes' model of it
2
+ // (gamut.table.ts), read between its points: within a few percent of the
3
+ // model, less sure right at the edge of what it makes, but never a color it
4
+ // can't make at all. color.ts gives this to sketches as `br30`.
5
+
6
+ import { hueChroma } from "./lemtone.ts";
7
+ import { EDGES, HUE_STEP, MOST_TENTHS, STEPS } from "./gamut.table.ts";
8
+
9
+ /** Where hue `hue` falls between the table's rows: the two rows, and how far from the first to the second. */
10
+ function rowsAt(hue: number): [number, number, number] {
11
+ const x = (((hue % 360) + 360) % 360) / HUE_STEP;
12
+ const i = Math.floor(x) % EDGES.length;
13
+ return [i, (i + 1) % EDGES.length, x - Math.floor(x)];
14
+ }
15
+
16
+ /** The most chroma the bulb makes of this hue (degrees). */
17
+ export function edgeOf(hue: number): number {
18
+ const [i, j, w] = rowsAt(hue);
19
+ return EDGES[i]! + (EDGES[j]! - EDGES[i]!) * w;
20
+ }
21
+
22
+ /** The most lumens the bulb makes of the color a, b before it starts to pale it; 0 past its most saturated. */
23
+ export function maxLmOf(a: number, b: number): number {
24
+ const { hue, chroma } = hueChroma(a, b);
25
+ const [i, j, w] = rowsAt(hue);
26
+ const f = (chroma / edgeOf(hue)) * STEPS;
27
+ if (!(f <= STEPS)) return 0;
28
+ const k = Math.min(Math.floor(f), STEPS - 1);
29
+ const along = (row: readonly number[]) => row[k]! + (row[k + 1]! - row[k]!) * (f - k);
30
+ const lo = along(MOST_TENTHS[i]!);
31
+ return (lo + (along(MOST_TENTHS[j]!) - lo) * w) / 10;
32
+ }