@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.
- package/LICENSE +21 -0
- package/README.md +276 -2
- package/dist/arclamp.d.ts +34 -0
- package/dist/arclamp.d.ts.map +1 -0
- package/dist/arclamp.js +5 -0
- package/dist/color.d.ts +202 -0
- package/dist/color.d.ts.map +1 -0
- package/dist/color.js +492 -0
- package/dist/field.d.ts +94 -0
- package/dist/field.d.ts.map +1 -0
- package/dist/field.js +232 -0
- package/dist/gamut.d.ts +5 -0
- package/dist/gamut.d.ts.map +1 -0
- package/dist/gamut.js +29 -0
- package/dist/gamut.table.d.ts +5 -0
- package/dist/gamut.table.d.ts.map +1 -0
- package/dist/gamut.table.js +153 -0
- package/dist/here.d.ts +105 -0
- package/dist/here.d.ts.map +1 -0
- package/dist/here.js +219 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/lemtone.d.ts +37 -0
- package/dist/lemtone.d.ts.map +1 -0
- package/dist/lemtone.js +158 -0
- package/dist/names.d.ts +5 -0
- package/dist/names.d.ts.map +1 -0
- package/dist/names.js +46 -0
- package/dist/parse.d.ts +43 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +208 -0
- package/dist/shape.d.ts +44 -0
- package/dist/shape.d.ts.map +1 -0
- package/dist/shape.js +89 -0
- package/dist/signal.d.ts +26 -0
- package/dist/signal.d.ts.map +1 -0
- package/dist/signal.js +44 -0
- package/dist/sketch.d.ts +29 -0
- package/dist/sketch.d.ts.map +1 -0
- package/dist/sketch.js +141 -0
- package/dist/spaces.d.ts +24 -0
- package/dist/spaces.d.ts.map +1 -0
- package/dist/spaces.js +91 -0
- package/package.json +24 -4
- package/src/arclamp.ts +36 -0
- package/src/color.ts +590 -0
- package/src/field.ts +321 -0
- package/src/gamut.table.ts +156 -0
- package/src/gamut.ts +32 -0
- package/src/here.ts +272 -0
- package/src/index.ts +52 -0
- package/src/lemtone.ts +180 -0
- package/src/names.ts +50 -0
- package/src/parse.ts +205 -0
- package/src/shape.ts +109 -0
- package/src/signal.ts +65 -0
- package/src/sketch.ts +172 -0
- package/src/spaces.ts +102 -0
package/dist/color.js
ADDED
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
// Colors: lemtones, the light itself, with brightness in lumens.
|
|
2
|
+
//
|
|
3
|
+
// A Color is a hue and chroma (OKLab's a and b at full lightness: 0, 0 is
|
|
4
|
+
// white) and an amount of light in lumens. Lumens are absolute, the same on
|
|
5
|
+
// every light: 800 lm is a bright bulb, 20 lm a glow, whichever light it's
|
|
6
|
+
// on. Colors never change: read them with properties (c.lm, c.hue, c.hex),
|
|
7
|
+
// and change them with methods, each giving a new color (c.at(400),
|
|
8
|
+
// c.mix(other, 0.5)).
|
|
9
|
+
//
|
|
10
|
+
// Anywhere a color goes, anything that says one will do (a ColorInput): a
|
|
11
|
+
// Color; text (any CSS color, "2700K", "265° C0.30 · 40 lm"); a number, as a
|
|
12
|
+
// color temperature in kelvin; [r, g, b]; or an object ({ a, b, lm },
|
|
13
|
+
// { r, g, b }, { h, s, l }, { l, c, h }, { hue, chroma }, { kelvin },
|
|
14
|
+
// { x, y }, or arclamp's own channels). Screens' spaces count their white as
|
|
15
|
+
// 1000 lm (spaces.ts), so #0000ff is about 72 lm and rgb(1, 1, 1) 1000.
|
|
16
|
+
import { edgeOf, maxLmOf } from "./gamut.js";
|
|
17
|
+
import { abOf, abOfXy, abToXyz, hueChroma, kelvinOf as kelvinOfAb, LUMENS_PER_BRIGHTNESS, oklabToXyz, whiteAB, xyzToAb, xyzToOklab, } from "./lemtone.js";
|
|
18
|
+
import { parseColor } from "./parse.js";
|
|
19
|
+
import { clamp, lerp } from "./shape.js";
|
|
20
|
+
import { decode, encode, hslToSrgb, hwbToSrgb, labToXyz, linearP3ToXyz, linearSrgbToXyz, SCREEN_WHITE_LM, srgbToHsl, xyzD50ToXyz, xyzToLinearSrgb, } from "./spaces.js";
|
|
21
|
+
export class Color {
|
|
22
|
+
/** OKLab's a at full lightness: green (−) to red (+). */
|
|
23
|
+
a;
|
|
24
|
+
/** OKLab's b at full lightness: blue (−) to yellow (+). */
|
|
25
|
+
b;
|
|
26
|
+
/** How much light, in lumens. */
|
|
27
|
+
lm;
|
|
28
|
+
/** 0 builds pale colors and whites from the RGB LEDs, 1 from the white ones. */
|
|
29
|
+
fidelity;
|
|
30
|
+
constructor(a, b, lm, fidelity = 1) {
|
|
31
|
+
this.a = a;
|
|
32
|
+
this.b = b;
|
|
33
|
+
this.lm = lm;
|
|
34
|
+
this.fidelity = fidelity;
|
|
35
|
+
}
|
|
36
|
+
/** Reads anything that says a color, as color() does. */
|
|
37
|
+
static from(input, lm) {
|
|
38
|
+
return color(input, lm);
|
|
39
|
+
}
|
|
40
|
+
// ---- reading ----
|
|
41
|
+
/** Its hue in degrees, 0–360 (OKLab's: about 30 red, 110 yellow, 145 green, 265 blue, 330 magenta). */
|
|
42
|
+
get hue() {
|
|
43
|
+
return hueChroma(this.a, this.b).hue;
|
|
44
|
+
}
|
|
45
|
+
/** How saturated: 0 is white, 0.1 pale, 0.3 strong. */
|
|
46
|
+
get chroma() {
|
|
47
|
+
return Math.hypot(this.a, this.b);
|
|
48
|
+
}
|
|
49
|
+
/** Its color temperature, if it's a white; null if it isn't. */
|
|
50
|
+
get kelvin() {
|
|
51
|
+
return kelvinOfAb(this.a, this.b);
|
|
52
|
+
}
|
|
53
|
+
/** The color temperature of the white nearest it, white or not. */
|
|
54
|
+
get nearestKelvin() {
|
|
55
|
+
return kelvinOfAb(this.a, this.b, Infinity);
|
|
56
|
+
}
|
|
57
|
+
/** How bright it looks, 0–1 (the cube root of its light; 1000 lm and up is 1). */
|
|
58
|
+
get looks() {
|
|
59
|
+
return Math.min(1, Math.cbrt(Math.max(0, this.lm) / SCREEN_WHITE_LM));
|
|
60
|
+
}
|
|
61
|
+
/** Its brightness in arclamp's units: 1.0 is 1000 lm. */
|
|
62
|
+
get brightness() {
|
|
63
|
+
return this.lm / LUMENS_PER_BRIGHTNESS;
|
|
64
|
+
}
|
|
65
|
+
/** As arclamp's channels for a color light. */
|
|
66
|
+
get channels() {
|
|
67
|
+
return { a: this.a, b: this.b, brightness: this.brightness, fidelity: this.fidelity };
|
|
68
|
+
}
|
|
69
|
+
/** As CIE XYZ (D65), with Y = 1 for 1000 lm. */
|
|
70
|
+
get xyz() {
|
|
71
|
+
const unit = abToXyz(this.a, this.b);
|
|
72
|
+
const k = Math.max(0, this.lm) / SCREEN_WHITE_LM;
|
|
73
|
+
return unit ? [unit[0] * k, unit[1] * k, unit[2] * k] : [0, 0, 0];
|
|
74
|
+
}
|
|
75
|
+
/** Its CIE 1931 chromaticity. */
|
|
76
|
+
get xy() {
|
|
77
|
+
const [X, Y, Z] = abToXyz(this.a, this.b) ?? [0.9505, 1, 1.089];
|
|
78
|
+
return { x: X / (X + Y + Z), y: Y / (X + Y + Z) };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* As sRGB, 0–1 per channel at 1000 lm of white (past 1 when brighter). What
|
|
82
|
+
* a screen can't show of it (the bulbs' deepest blues, say) is left out.
|
|
83
|
+
*/
|
|
84
|
+
get rgb() {
|
|
85
|
+
const [r, g, b] = xyzToLinearSrgb(this.xyz).map((v) => encode(Math.max(0, v)));
|
|
86
|
+
return { r: r, g: g, b: b };
|
|
87
|
+
}
|
|
88
|
+
/** As a CSS hex code, "#rrggbb", clipped to what a screen shows. */
|
|
89
|
+
get hex() {
|
|
90
|
+
const { r, g, b } = this.rgb;
|
|
91
|
+
return `#${[r, g, b].map((v) => Math.round(clamp(v) * 255).toString(16).padStart(2, "0")).join("")}`;
|
|
92
|
+
}
|
|
93
|
+
/** As HSL: hue in degrees, saturation and lightness 0–1 (clipped to what a screen shows). */
|
|
94
|
+
get hsl() {
|
|
95
|
+
const { r, g, b } = this.rgb;
|
|
96
|
+
return srgbToHsl(clamp(r), clamp(g), clamp(b));
|
|
97
|
+
}
|
|
98
|
+
/** As OKLab (CSS's: L 0–1, at 1000 lm of white). */
|
|
99
|
+
get oklab() {
|
|
100
|
+
const [l, a, b] = xyzToOklab(this.xyz);
|
|
101
|
+
return { l, a, b };
|
|
102
|
+
}
|
|
103
|
+
/** As OKLCh (CSS's: L 0–1 at 1000 lm of white, chroma, hue in degrees). */
|
|
104
|
+
get oklch() {
|
|
105
|
+
const { l, a, b } = this.oklab;
|
|
106
|
+
return { l, c: Math.hypot(a, b), h: hueChroma(a, b).hue };
|
|
107
|
+
}
|
|
108
|
+
// ---- changing (each gives a new color) ----
|
|
109
|
+
/** The same color at `lm` lumens. */
|
|
110
|
+
at(lm) {
|
|
111
|
+
return new Color(this.a, this.b, lm, this.fidelity);
|
|
112
|
+
}
|
|
113
|
+
/** k times the light: dim(0.5) is half the lumens, dim(2) twice. */
|
|
114
|
+
dim(k) {
|
|
115
|
+
return this.at(this.lm * k);
|
|
116
|
+
}
|
|
117
|
+
/** Looks k times as bright: fade(0.5) looks half as bright (an eighth of the lumens). */
|
|
118
|
+
fade(k) {
|
|
119
|
+
return this.at(this.lm * Math.max(0, k) ** 3);
|
|
120
|
+
}
|
|
121
|
+
/** With some of it changed: with({ fidelity: 0 }), with({ hue: 200 }), with({ kelvin: 3000 }). */
|
|
122
|
+
with(changes) {
|
|
123
|
+
let { a, b } = this;
|
|
124
|
+
if (changes.kelvin !== undefined)
|
|
125
|
+
[a, b] = whiteAB(kelvinIn(changes.kelvin));
|
|
126
|
+
if (changes.hue !== undefined || changes.chroma !== undefined) {
|
|
127
|
+
const now = hueChroma(a, b);
|
|
128
|
+
[a, b] = abOf(changes.hue ?? now.hue, changes.chroma ?? now.chroma);
|
|
129
|
+
}
|
|
130
|
+
a = changes.a ?? a;
|
|
131
|
+
b = changes.b ?? b;
|
|
132
|
+
const lm = changes.lm ?? changes.lumens ?? (changes.brightness !== undefined ? changes.brightness * LUMENS_PER_BRIGHTNESS : this.lm);
|
|
133
|
+
return new Color(a, b, lm, changes.fidelity ?? this.fidelity);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Toward `other` by t (0 is this, 1 is other; 0.5 if not given), evenly as
|
|
137
|
+
* it looks: the color in a straight line through OKLab, brightness by its
|
|
138
|
+
* cube root. To or from dark, only the brightness changes.
|
|
139
|
+
*/
|
|
140
|
+
mix(other, t = 0.5) {
|
|
141
|
+
const y = color(other);
|
|
142
|
+
const k = clamp(t);
|
|
143
|
+
if (k === 0)
|
|
144
|
+
return this;
|
|
145
|
+
if (k === 1)
|
|
146
|
+
return y;
|
|
147
|
+
const lm = lerp(Math.cbrt(this.lm), Math.cbrt(y.lm), k) ** 3;
|
|
148
|
+
if (!(this.lm > 0))
|
|
149
|
+
return y.at(lm);
|
|
150
|
+
if (!(y.lm > 0))
|
|
151
|
+
return this.at(lm);
|
|
152
|
+
return new Color(lerp(this.a, y.a, k), lerp(this.b, y.b, k), lm, lerp(this.fidelity, y.fidelity, k));
|
|
153
|
+
}
|
|
154
|
+
/** This and others shining together: their light added up, as it would be on a wall. */
|
|
155
|
+
add(...others) {
|
|
156
|
+
const sum = [0, 0, 0];
|
|
157
|
+
let fidelity = 0;
|
|
158
|
+
for (const c of [this, ...others.map((o) => color(o))]) {
|
|
159
|
+
if (!(c.lm > 0))
|
|
160
|
+
continue;
|
|
161
|
+
const xyz = abToXyz(c.a, c.b);
|
|
162
|
+
if (!xyz)
|
|
163
|
+
continue;
|
|
164
|
+
for (let i = 0; i < 3; i++)
|
|
165
|
+
sum[i] += xyz[i] * c.lm;
|
|
166
|
+
fidelity += c.fidelity * c.lm;
|
|
167
|
+
}
|
|
168
|
+
if (!(sum[1] > 0))
|
|
169
|
+
return off;
|
|
170
|
+
const [a, b] = xyzToAb(sum);
|
|
171
|
+
return new Color(a, b, sum[1], fidelity / sum[1]);
|
|
172
|
+
}
|
|
173
|
+
/** The hue turned by `degrees`. */
|
|
174
|
+
shiftHue(degrees) {
|
|
175
|
+
return this.with({ hue: this.hue + degrees });
|
|
176
|
+
}
|
|
177
|
+
/** k times the chroma: 0 is white, 1 unchanged, 2 twice as saturated. */
|
|
178
|
+
saturate(k) {
|
|
179
|
+
return new Color(this.a * k, this.b * k, this.lm, this.fidelity);
|
|
180
|
+
}
|
|
181
|
+
/** Dimmed, if need be, to what a bulb makes of it (a color bulb, br30, unless told), so it isn't paled. */
|
|
182
|
+
vivid(bulb = br30) {
|
|
183
|
+
return bulb.vivid(this);
|
|
184
|
+
}
|
|
185
|
+
/** Like "2700 K · 400 lm" or "265° C0.300 · 40 lm"; color() reads it back. */
|
|
186
|
+
toString() {
|
|
187
|
+
if (!(this.lm > 0))
|
|
188
|
+
return "off";
|
|
189
|
+
const k = this.kelvin;
|
|
190
|
+
const name = k !== null ? `${Math.round(k)} K` : `${+this.hue.toFixed(1)}° C${this.chroma.toFixed(3)}`;
|
|
191
|
+
const f = this.fidelity !== 1 ? ` · F${this.fidelity.toFixed(2)}` : "";
|
|
192
|
+
return `${name} · ${+this.lm.toFixed(2)} lm${f}`;
|
|
193
|
+
}
|
|
194
|
+
toJSON() {
|
|
195
|
+
return { a: this.a, b: this.b, lm: this.lm, fidelity: this.fidelity };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/** No light. */
|
|
199
|
+
export const off = Object.freeze(new Color(0, 0, 0));
|
|
200
|
+
// An old bulb that's on, as a color: a 2700 K incandescent of about 800 lm.
|
|
201
|
+
const OLD_BULB = (() => {
|
|
202
|
+
const [a, b] = whiteAB(2700);
|
|
203
|
+
return Object.freeze(new Color(a, b, 800));
|
|
204
|
+
})();
|
|
205
|
+
// ---- making colors ----------------------------------------------------------------
|
|
206
|
+
/**
|
|
207
|
+
* Anything that says a color, as a Color (see the top of color.ts), at `lm`
|
|
208
|
+
* lumens if given. Throws, saying why, if it can't tell what color it is.
|
|
209
|
+
*/
|
|
210
|
+
export function color(input, lm) {
|
|
211
|
+
const c = read(input);
|
|
212
|
+
return lm === undefined ? c : c.at(lm);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* A white of this color temperature (2700 K is a warm bulb, 4000 K neutral,
|
|
216
|
+
* 6500 K daylight; the bulbs go as warm as about 1800 K, dimly), at `lm`
|
|
217
|
+
* lumens (1000 if not given). With no temperature, OKLab's own white.
|
|
218
|
+
*/
|
|
219
|
+
export function white(kelvin, lm = SCREEN_WHITE_LM) {
|
|
220
|
+
if (kelvin === undefined)
|
|
221
|
+
return new Color(0, 0, lm);
|
|
222
|
+
const [a, b] = whiteAB(kelvinIn(typeof kelvin === "string" ? parseFloat(kelvin) : kelvin));
|
|
223
|
+
return new Color(a, b, lm);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* A color in the lights' own terms: hue in degrees (OKLab's: about 30 red,
|
|
227
|
+
* 110 yellow, 145 green, 265 blue, 330 magenta), chroma at full lightness (0
|
|
228
|
+
* white, 0.1 pale, 0.3 strong, up to about 0.8 for the bulbs' violet), and
|
|
229
|
+
* lumens (1000 if not given).
|
|
230
|
+
*/
|
|
231
|
+
export function lemtone(hue, chroma, lm = SCREEN_WHITE_LM, fidelity = 1) {
|
|
232
|
+
const [a, b] = abOf(hue, chroma);
|
|
233
|
+
return new Color(a, b, lm, fidelity);
|
|
234
|
+
}
|
|
235
|
+
/** CSS's oklch(): lightness 0–1 (past 1, a percentage), chroma, hue in degrees; L = 1 is 1000 lm of white. */
|
|
236
|
+
export function oklch(l, c, h, alpha = 1) {
|
|
237
|
+
const r = (h * Math.PI) / 180;
|
|
238
|
+
return fromXyz(oklabToXyz([unitOrPercent(l), c * Math.cos(r), c * Math.sin(r)])).dim(alpha);
|
|
239
|
+
}
|
|
240
|
+
/** CSS's oklab(): lightness 0–1 (past 1, a percentage), a, b; L = 1 is 1000 lm of white. */
|
|
241
|
+
export function oklab(l, a, b, alpha = 1) {
|
|
242
|
+
return fromXyz(oklabToXyz([unitOrPercent(l), a, b])).dim(alpha);
|
|
243
|
+
}
|
|
244
|
+
/** sRGB, 0–1 per channel (0–255 if any is past 1), white at 1000 lm. */
|
|
245
|
+
export function rgb(r, g, b, alpha = 1) {
|
|
246
|
+
const k = r > 1 || g > 1 || b > 1 ? 255 : 1;
|
|
247
|
+
return fromSrgb([r / k, g / k, b / k]).dim(alpha);
|
|
248
|
+
}
|
|
249
|
+
/** HSL: hue in degrees, saturation and lightness 0–1 (past 1, percentages), white at 1000 lm. */
|
|
250
|
+
export function hsl(h, s, l, alpha = 1) {
|
|
251
|
+
return fromSrgb(hslToSrgb(h, unitOrPercent(s), unitOrPercent(l))).dim(alpha);
|
|
252
|
+
}
|
|
253
|
+
/** A CSS hex code ("#f80", "#ff8800", "ff8800cc"), or anything else color() reads. */
|
|
254
|
+
export const hex = (code) => color(code);
|
|
255
|
+
/** A CIE 1931 chromaticity, at `lm` lumens (1000 if not given). */
|
|
256
|
+
export function xy(x, y, lm = SCREEN_WHITE_LM) {
|
|
257
|
+
const [a, b] = abOfXy([x, y]);
|
|
258
|
+
return new Color(a, b, lm);
|
|
259
|
+
}
|
|
260
|
+
// The Planckian spline's range (Kim et al.): warmer or cooler is held at its ends.
|
|
261
|
+
const kelvinIn = (k) => Math.min(25000, Math.max(1667, k));
|
|
262
|
+
const unitOrPercent = (v) => (Math.abs(v) > 1 ? v / 100 : v);
|
|
263
|
+
/** Light as XYZ (Y = 1 for 1000 lm), as a Color. */
|
|
264
|
+
function fromXyz(X) {
|
|
265
|
+
if (!(X[1] > 0))
|
|
266
|
+
return off;
|
|
267
|
+
const [a, b] = xyzToAb(X);
|
|
268
|
+
return new Color(a, b, X[1] * SCREEN_WHITE_LM);
|
|
269
|
+
}
|
|
270
|
+
const fromSrgb = (rgb) => fromXyz(linearSrgbToXyz(rgb.map(decode)));
|
|
271
|
+
function read(input) {
|
|
272
|
+
if (input instanceof Color)
|
|
273
|
+
return input;
|
|
274
|
+
if (input === null || input === undefined || input === false)
|
|
275
|
+
return off;
|
|
276
|
+
if (typeof input === "string") {
|
|
277
|
+
const spec = parseColor(input);
|
|
278
|
+
if (!spec) {
|
|
279
|
+
throw new Error(`"${input}" isn't a color I can read: try a CSS color ("#ff8800", "orange", "oklch(0.7 0.2 265)"), a color temperature ("2700K"), or "265° C0.3 · 40 lm"`);
|
|
280
|
+
}
|
|
281
|
+
return fromSpec(spec);
|
|
282
|
+
}
|
|
283
|
+
if (typeof input === "number")
|
|
284
|
+
return fromKelvin(input);
|
|
285
|
+
if (Array.isArray(input))
|
|
286
|
+
return fromArray(input);
|
|
287
|
+
if (typeof input === "object")
|
|
288
|
+
return fromObject(input);
|
|
289
|
+
throw new Error(`${String(input)} isn't a color`);
|
|
290
|
+
}
|
|
291
|
+
function fromKelvin(k) {
|
|
292
|
+
if (!(k >= 500 && k <= 100000)) {
|
|
293
|
+
throw new Error(`${k} isn't a color: a number is read as a color temperature in kelvin, like 2700 (a warm bulb) or 6500 (daylight)`);
|
|
294
|
+
}
|
|
295
|
+
return white(k);
|
|
296
|
+
}
|
|
297
|
+
function fromArray(list) {
|
|
298
|
+
if (list.length >= 3 && list.length <= 4 && list.every((v) => typeof v === "number")) {
|
|
299
|
+
const [r, g, b, alpha = 1] = list;
|
|
300
|
+
return rgb(r, g, b, alpha > 1 ? alpha / 255 : alpha);
|
|
301
|
+
}
|
|
302
|
+
throw new Error(`[${list.join(", ")}] isn't a color: as a list, give [r, g, b] (0–1, or 0–255)`);
|
|
303
|
+
}
|
|
304
|
+
function fromSpec(spec) {
|
|
305
|
+
let c;
|
|
306
|
+
switch (spec.space) {
|
|
307
|
+
case "off":
|
|
308
|
+
c = off;
|
|
309
|
+
break;
|
|
310
|
+
case "srgb":
|
|
311
|
+
c = fromSrgb(spec.rgb);
|
|
312
|
+
break;
|
|
313
|
+
case "srgb-linear":
|
|
314
|
+
c = fromXyz(linearSrgbToXyz(spec.rgb));
|
|
315
|
+
break;
|
|
316
|
+
case "display-p3":
|
|
317
|
+
c = fromXyz(linearP3ToXyz(spec.rgb.map(decode)));
|
|
318
|
+
break;
|
|
319
|
+
case "xyz":
|
|
320
|
+
c = fromXyz(spec.xyz);
|
|
321
|
+
break;
|
|
322
|
+
case "xyz-d50":
|
|
323
|
+
c = fromXyz(xyzD50ToXyz(spec.xyz));
|
|
324
|
+
break;
|
|
325
|
+
case "hsl":
|
|
326
|
+
c = fromSrgb(hslToSrgb(spec.h, spec.s, spec.l));
|
|
327
|
+
break;
|
|
328
|
+
case "hwb":
|
|
329
|
+
c = fromSrgb(hwbToSrgb(spec.h, spec.w, spec.b));
|
|
330
|
+
break;
|
|
331
|
+
case "oklab":
|
|
332
|
+
c = fromXyz(oklabToXyz(spec.lab));
|
|
333
|
+
break;
|
|
334
|
+
case "oklch": {
|
|
335
|
+
const r = (spec.h * Math.PI) / 180;
|
|
336
|
+
c = fromXyz(oklabToXyz([spec.l, spec.c * Math.cos(r), spec.c * Math.sin(r)]));
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
case "lab":
|
|
340
|
+
c = fromXyz(labToXyz(...spec.lab));
|
|
341
|
+
break;
|
|
342
|
+
case "lch": {
|
|
343
|
+
const r = (spec.h * Math.PI) / 180;
|
|
344
|
+
c = fromXyz(labToXyz(spec.l, spec.c * Math.cos(r), spec.c * Math.sin(r)));
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
case "kelvin":
|
|
348
|
+
c = white(spec.kelvin);
|
|
349
|
+
break;
|
|
350
|
+
case "lemtone":
|
|
351
|
+
c = lemtone(spec.hue, spec.chroma);
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
if (spec.alpha !== undefined)
|
|
355
|
+
c = c.dim(clamp(spec.alpha));
|
|
356
|
+
if (spec.lm !== undefined)
|
|
357
|
+
c = c.at(spec.lm);
|
|
358
|
+
if (spec.fidelity !== undefined)
|
|
359
|
+
c = c.with({ fidelity: spec.fidelity });
|
|
360
|
+
return c;
|
|
361
|
+
}
|
|
362
|
+
/** The first of these an object has as a number (numbers given as text too). */
|
|
363
|
+
function numberIn(o, ...keys) {
|
|
364
|
+
for (const key of keys) {
|
|
365
|
+
const v = o[key];
|
|
366
|
+
const n = typeof v === "number" ? v : typeof v === "string" && v.trim() !== "" ? Number(v) : typeof v === "boolean" ? Number(v) : NaN;
|
|
367
|
+
if (Number.isFinite(n))
|
|
368
|
+
return n;
|
|
369
|
+
}
|
|
370
|
+
return undefined;
|
|
371
|
+
}
|
|
372
|
+
function fromObject(o) {
|
|
373
|
+
const n = (...keys) => numberIn(o, ...keys);
|
|
374
|
+
const has = (...keys) => keys.every((k) => n(k) !== undefined);
|
|
375
|
+
const alpha = (...keys) => {
|
|
376
|
+
const v = n(...keys);
|
|
377
|
+
return v === undefined ? 1 : v > 1 ? v / 255 : v;
|
|
378
|
+
};
|
|
379
|
+
let c;
|
|
380
|
+
if (has("on") && !has("a") && !has("r"))
|
|
381
|
+
c = n("on") >= 0.5 ? OLD_BULB : off;
|
|
382
|
+
else if (has("r", "g", "b"))
|
|
383
|
+
c = rgb(n("r"), n("g"), n("b"), alpha("alpha", "a", "opacity"));
|
|
384
|
+
else if (has("h", "s", "l"))
|
|
385
|
+
c = hsl(n("h"), n("s"), n("l"), alpha("alpha", "a", "opacity"));
|
|
386
|
+
else if (has("h", "w", "b") && !has("a"))
|
|
387
|
+
c = fromSrgb(hwbToSrgb(n("h"), unitOrPercent(n("w")), unitOrPercent(n("b"))));
|
|
388
|
+
else if (has("l", "c", "h"))
|
|
389
|
+
c = oklch(n("l"), n("c"), n("h"));
|
|
390
|
+
else if (has("L", "C", "H"))
|
|
391
|
+
c = oklch(n("L"), n("C"), n("H"));
|
|
392
|
+
else if (has("l", "a", "b"))
|
|
393
|
+
c = oklab(n("l"), n("a"), n("b"));
|
|
394
|
+
else if (has("L", "a", "b"))
|
|
395
|
+
c = oklab(n("L"), n("a"), n("b"));
|
|
396
|
+
else if (n("hue", "chroma") !== undefined)
|
|
397
|
+
c = lemtone(n("hue") ?? 0, n("chroma") ?? 0);
|
|
398
|
+
else if (n("kelvin", "k", "K", "cct") !== undefined)
|
|
399
|
+
c = white(n("kelvin", "k", "K", "cct"));
|
|
400
|
+
else if (has("x", "y"))
|
|
401
|
+
c = xy(n("x"), n("y"), n("Y") !== undefined ? n("Y") * SCREEN_WHITE_LM : SCREEN_WHITE_LM);
|
|
402
|
+
else if (has("a", "b") || n("lm", "lumens", "brightness") !== undefined)
|
|
403
|
+
c = new Color(n("a") ?? 0, n("b") ?? 0, SCREEN_WHITE_LM);
|
|
404
|
+
else
|
|
405
|
+
throw new Error(`${JSON.stringify(o)} isn't a color I can read: give it { a, b, lm }, { r, g, b }, { h, s, l }, { l, c, h }, { hue, chroma }, { kelvin }, or { x, y }`);
|
|
406
|
+
const lm = n("lm", "lumens", "lumen");
|
|
407
|
+
const brightness = n("brightness");
|
|
408
|
+
if (lm !== undefined)
|
|
409
|
+
c = c.at(lm);
|
|
410
|
+
else if (brightness !== undefined)
|
|
411
|
+
c = c.at(brightness * LUMENS_PER_BRIGHTNESS);
|
|
412
|
+
const fidelity = n("fidelity");
|
|
413
|
+
if (fidelity !== undefined)
|
|
414
|
+
c = c.with({ fidelity });
|
|
415
|
+
return c;
|
|
416
|
+
}
|
|
417
|
+
const isPositioned = (s) => Array.isArray(s) && s.length === 2 && typeof s[0] === "number";
|
|
418
|
+
const isRgbList = (s) => Array.isArray(s) && s.length >= 3 && s.every((v) => typeof v === "number");
|
|
419
|
+
/** Whether v is a palette. */
|
|
420
|
+
export const isPalette = (v) => typeof v === "function" && Array.isArray(v.stops);
|
|
421
|
+
/**
|
|
422
|
+
* A palette through these colors (evenly spaced from 0 to 1 unless placed,
|
|
423
|
+
* [0.3, "#f80"]), mixed between them; outside 0–1 it holds the ends.
|
|
424
|
+
* palette(a, b, c) and palette([a, b, c]) are the same.
|
|
425
|
+
*/
|
|
426
|
+
export function palette(...given) {
|
|
427
|
+
const list = given.length === 1 && Array.isArray(given[0]) && !isPositioned(given[0]) && !isRgbList(given[0]) ? given[0] : given;
|
|
428
|
+
if (list.length === 0)
|
|
429
|
+
throw new Error("palette(): give it at least one color");
|
|
430
|
+
const placed = list
|
|
431
|
+
.map((s, i) => (isPositioned(s) ? { at: clamp(s[0]), c: color(s[1]) } : { at: list.length > 1 ? i / (list.length - 1) : 0, c: color(s) }))
|
|
432
|
+
.sort((x, y) => x.at - y.at);
|
|
433
|
+
const stops = Object.freeze(placed.map((p) => Object.freeze(p.c)));
|
|
434
|
+
const positions = Object.freeze(placed.map((p) => p.at));
|
|
435
|
+
const at = (x) => {
|
|
436
|
+
const along = typeof x === "number" ? x : x?.u;
|
|
437
|
+
const v = typeof along === "number" && Number.isFinite(along) ? along : 0;
|
|
438
|
+
if (v <= positions[0])
|
|
439
|
+
return stops[0];
|
|
440
|
+
for (let i = 1; i < stops.length; i++) {
|
|
441
|
+
if (v <= positions[i]) {
|
|
442
|
+
const span = positions[i] - positions[i - 1];
|
|
443
|
+
return span > 0 ? stops[i - 1].mix(stops[i], (v - positions[i - 1]) / span) : stops[i];
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return stops[stops.length - 1];
|
|
447
|
+
};
|
|
448
|
+
return Object.assign(at, { stops, positions });
|
|
449
|
+
}
|
|
450
|
+
/** A palette from anything that says one: a palette, a list of stops, or one color (from dark at 0 to it at 1). */
|
|
451
|
+
export function toPalette(v) {
|
|
452
|
+
if (isPalette(v))
|
|
453
|
+
return v;
|
|
454
|
+
if (Array.isArray(v) && !isRgbList(v))
|
|
455
|
+
return palette(v);
|
|
456
|
+
return palette(off, v);
|
|
457
|
+
}
|
|
458
|
+
/** Palettes to start from, each dark to bright, within what the color bulbs make. */
|
|
459
|
+
export const palettes = {
|
|
460
|
+
/** Deep violet, magenta, orange, warm white. */
|
|
461
|
+
dusk: palette(lemtone(285, 0.3, 6), lemtone(330, 0.28, 30), lemtone(45, 0.2, 150), white(2700, 400)),
|
|
462
|
+
/** Deep blue, teal, pale cyan, cool white. */
|
|
463
|
+
sea: palette(lemtone(265, 0.35, 6), lemtone(220, 0.2, 40), lemtone(195, 0.12, 160), white(6500, 400)),
|
|
464
|
+
/** Ember red, orange, firelight, warm white. */
|
|
465
|
+
ember: palette(lemtone(30, 0.32, 4), lemtone(45, 0.28, 30), white(2000, 80), white(2700, 400)),
|
|
466
|
+
/** Green, teal, violet: northern lights. */
|
|
467
|
+
aurora: palette(lemtone(150, 0.25, 8), lemtone(165, 0.22, 60), lemtone(200, 0.15, 90), lemtone(300, 0.2, 40)),
|
|
468
|
+
};
|
|
469
|
+
// vivid() keeps this far inside what the table says, for the few percent it can be over.
|
|
470
|
+
const MARGIN = 0.97;
|
|
471
|
+
/**
|
|
472
|
+
* The color bulbs (br30), from vibes' model of Walter's solve: asked for more
|
|
473
|
+
* than they can make of a color, they keep the lumens and pale it toward
|
|
474
|
+
* white. A strong blue tops out near 30 lm; whites near 950 at 4000 K, 500 at
|
|
475
|
+
* 2700 K.
|
|
476
|
+
*/
|
|
477
|
+
export const br30 = {
|
|
478
|
+
type: "br30",
|
|
479
|
+
maxLm: (c) => {
|
|
480
|
+
const k = color(c);
|
|
481
|
+
return maxLmOf(k.a, k.b);
|
|
482
|
+
},
|
|
483
|
+
edgeOf,
|
|
484
|
+
vivid(input) {
|
|
485
|
+
const c = color(input);
|
|
486
|
+
const { hue, chroma } = hueChroma(c.a, c.b);
|
|
487
|
+
const edge = edgeOf(hue) * 0.999;
|
|
488
|
+
const made = chroma > edge ? c.with({ chroma: edge }) : c;
|
|
489
|
+
const most = maxLmOf(made.a, made.b) * MARGIN;
|
|
490
|
+
return made.lm > most ? made.at(most) : made;
|
|
491
|
+
},
|
|
492
|
+
};
|
package/dist/field.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { type Bulb, type Color, type ColorInput, type ColorLike, type Palette, type Stop } from "./color.ts";
|
|
2
|
+
import type { Here, Light } from "./here.ts";
|
|
3
|
+
/** A number, or a number that depends on here (a field, or any function of here). */
|
|
4
|
+
export type Param = number | ((here: Here) => number);
|
|
5
|
+
/** A color (anything that says one), or one that depends on here. */
|
|
6
|
+
export type ColorParam = ColorInput | ((here: Here) => ColorInput);
|
|
7
|
+
export interface Field<T> {
|
|
8
|
+
(here: Here): T;
|
|
9
|
+
/** Turned by `degrees` around the room's center (clockwise on the plan). */
|
|
10
|
+
rotate(degrees: Param): Field<T>;
|
|
11
|
+
/** k times as big, from the room's center. */
|
|
12
|
+
scale(k: Param): Field<T>;
|
|
13
|
+
/** Moved dx meters right and dy down. */
|
|
14
|
+
move(dx: Param, dy?: Param): Field<T>;
|
|
15
|
+
/** Folded into n mirrored slices around the room's center, like a kaleidoscope. */
|
|
16
|
+
kaleid(n: Param): Field<T>;
|
|
17
|
+
/** 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. */
|
|
18
|
+
warp(by: (here: Here) => number, amount?: Param): Field<T>;
|
|
19
|
+
/** `seconds` behind: .delay(({ u }) => 2 * u) runs two seconds later at the room's right than its left. */
|
|
20
|
+
delay(seconds: Param): Field<T>;
|
|
21
|
+
/** k times as fast. */
|
|
22
|
+
speed(k: Param): Field<T>;
|
|
23
|
+
/** Its value, changed by your own function. */
|
|
24
|
+
map<U>(fn: (value: T, here: Here) => U): Field<U>;
|
|
25
|
+
/** Only on the lights that pass `test`; the rest are left as they are below. */
|
|
26
|
+
only(test: (light: Light) => unknown): Field<T | null>;
|
|
27
|
+
/** Raised to the power k: above 1 darkens the middle, below 1 lifts it. */
|
|
28
|
+
pow(this: Field<number>, k: Param): Field<number>;
|
|
29
|
+
/** Cut into n flat steps from 0 to 1. */
|
|
30
|
+
steps(this: Field<number>, n: Param): Field<number>;
|
|
31
|
+
/** 1 at or above `level` (default 0.5), 0 below: hard edges. */
|
|
32
|
+
thresh(this: Field<number>, level?: Param): Field<number>;
|
|
33
|
+
/** 1 minus it. */
|
|
34
|
+
invert(this: Field<number>): Field<number>;
|
|
35
|
+
/** Times k. */
|
|
36
|
+
mul(this: Field<number>, k: Param): Field<number>;
|
|
37
|
+
/** Plus another number, or (for colors) another light, shining together. */
|
|
38
|
+
add(this: Field<number>, other: Param): Field<number>;
|
|
39
|
+
add(this: Field<ColorLike>, other: ColorParam): Field<Color>;
|
|
40
|
+
/** Toward `other` by t (0.5 if not given), from 0 (all this) to 1 (all other). */
|
|
41
|
+
mix(this: Field<number>, other: Param, t?: Param): Field<number>;
|
|
42
|
+
mix(this: Field<ColorLike>, other: ColorParam, t?: Param): Field<Color>;
|
|
43
|
+
/** Made a color: by a palette (0 to 1 along it), or your own function. */
|
|
44
|
+
color(this: Field<number>, by: (value: number, here: Here) => ColorInput): Field<Color>;
|
|
45
|
+
/** Made a color by a palette that changes: a field of palettes, like clock(96).every(16).seq([...]). */
|
|
46
|
+
color(this: Field<number>, by: Field<Palette>): Field<Color>;
|
|
47
|
+
/** Made a color by a palette's stops ([dark, "#f80", "white"]), or by one color, from dark at 0 to it at 1. */
|
|
48
|
+
color(this: Field<number>, by: readonly Stop[] | ColorLike): Field<Color>;
|
|
49
|
+
/** At `lm` lumens. */
|
|
50
|
+
at(this: Field<ColorLike>, lm: Param): Field<Color>;
|
|
51
|
+
/** k times the light. */
|
|
52
|
+
dim(this: Field<ColorLike>, k: Param): Field<Color>;
|
|
53
|
+
/** Looks k times as bright. */
|
|
54
|
+
fade(this: Field<ColorLike>, k: Param): Field<Color>;
|
|
55
|
+
/** Brighter by `amount` (default 1, twice the light) times `by`: .boost(beat, 0.8) lifts it 80% on each beat. */
|
|
56
|
+
boost(this: Field<ColorLike>, by: Param, amount?: Param): Field<Color>;
|
|
57
|
+
/** The hue turned by `degrees`. */
|
|
58
|
+
shiftHue(this: Field<ColorLike>, degrees: Param): Field<Color>;
|
|
59
|
+
/** k times the chroma. */
|
|
60
|
+
saturate(this: Field<ColorLike>, k: Param): Field<Color>;
|
|
61
|
+
/** Dimmed where need be to what a bulb makes (a color bulb, br30, unless told), so its colors aren't paled. */
|
|
62
|
+
vivid(this: Field<ColorLike>, bulb?: Bulb): Field<Color>;
|
|
63
|
+
}
|
|
64
|
+
/** Your own function of here as a field, to chain onto. */
|
|
65
|
+
export declare function field<T>(fn: (here: Here) => T): Field<T>;
|
|
66
|
+
/** Whether v is a field (made by a source, field(), or a chain). */
|
|
67
|
+
export declare const isField: (v: unknown) => v is Field<unknown>;
|
|
68
|
+
/** Smooth bands, `n` across the room (left to right), each light going round `speed` times a second. */
|
|
69
|
+
export declare function osc(n?: Param, speed?: Param, phase?: Param): Field<number>;
|
|
70
|
+
/** Smooth noise, blobs about `n` across the room, changing about `speed` times a second. */
|
|
71
|
+
export declare function noise(n?: Param, speed?: Param): Field<number>;
|
|
72
|
+
/** Rings out from the room's center, `n` of them out to its left and right ends, moving out `speed` times a second. */
|
|
73
|
+
export declare function rings(n?: Param, speed?: Param): Field<number>;
|
|
74
|
+
/** 0 to 1 across the room: left to right at 0°, top to bottom at 90°. */
|
|
75
|
+
export declare function gradient(degrees?: Param): Field<number>;
|
|
76
|
+
/** Cells, about `n` across the room, their centers drifting `speed` times a second: 0 at a cell's center, rising toward its edges. */
|
|
77
|
+
export declare function voronoi(n?: Param, speed?: Param): Field<number>;
|
|
78
|
+
/** The same value everywhere: solid(0.5), or a color, solid(white(2700, 300)). */
|
|
79
|
+
export declare function solid<T>(value: T): Field<T>;
|
|
80
|
+
/** Here, looking as if the pattern were turned by `degrees` around the room's center. */
|
|
81
|
+
export declare function rotate(here: Here, degrees: number): Here;
|
|
82
|
+
/** Here, looking as if the pattern were k times as big, from the room's center. */
|
|
83
|
+
export declare function scale(here: Here, k: number): Here;
|
|
84
|
+
/** Here, looking as if the pattern were moved dx meters right and dy down. */
|
|
85
|
+
export declare function move(here: Here, dx: number, dy?: number): Here;
|
|
86
|
+
/** Here, folded into n mirrored slices around the room's center. */
|
|
87
|
+
export declare function kaleid(here: Here, n: number): Here;
|
|
88
|
+
/** Here, pushed up to `amount` meters each way by `by` (none where it's 0.5). */
|
|
89
|
+
export declare function warp(here: Here, by: (here: Here) => number, amount?: number): Here;
|
|
90
|
+
/** Here, `seconds` earlier. */
|
|
91
|
+
export declare const delay: (here: Here, seconds: number) => Here;
|
|
92
|
+
/** Here, with time going k times as fast. */
|
|
93
|
+
export declare const speed: (here: Here, k: number) => Here;
|
|
94
|
+
//# sourceMappingURL=field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"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"}
|