xxxxxxxxxx
197
function GEN(min, max) {
return new Dusa(`
#builtin INT_MINUS minus
max is ${max}.
min is ${min}.
dim max.
dim (minus N 1) :- dim N, N > min.
choice is? N :- dim N.
`);
}
const WIDTH = 25;
const HEIGHT = 100;
const GRID_SIZE = 15;
function* coords() {
for (let y = 0; y < HEIGHT; y++) {
for (let x = 0; x < WIDTH; x++) {
yield [x, y];
}
}
}
let yHues;
let xLightnesses;
let iter;
function setup() {
createCanvas(WIDTH * GRID_SIZE, HEIGHT * GRID_SIZE);
xLightnesses = [
Iterator.from(GEN(0, 100))
.take(WIDTH)
.map((sol) => sol.get("choice") / 100),
];
yHues = [
Iterator.from(GEN(0, 360))
.take(HEIGHT)
.map((sol) => sol.get("choice")),
];
iter = coords();
background(255);
noStroke();
fill(0);
}
function draw() {
const now = iter.next();
if (!now.done) {
const [col, row] = now.value;
// Pick a random chroma that actually works for this L and H.
const zChroma = Iterator.from(GEN(0, 40)).map(sol => sol.get("choice")/100);
for(;;) {
const chr = zChroma.next();
if (chr.done) break;
const oklch = [xLightnesses[col], chr.value, yHues[row]];
const rgb = oklch2rgb(oklch);
if (rgb.some(v => v < 0 || v > 1)) continue;
fill(color(rgb[0] * 255, rgb[1] * 255, rgb[2] * 255));
circle((col + 0.5) * GRID_SIZE, (row + 0.5) * GRID_SIZE, GRID_SIZE - 2);
break;
}
}
}
const multiplyMatrices = (A, B) => {
return [
A[0] * B[0] + A[1] * B[1] + A[2] * B[2],
A[3] * B[0] + A[4] * B[1] + A[5] * B[2],
A[6] * B[0] + A[7] * B[1] + A[8] * B[2],
];
};
const oklch2oklab = ([l, c, h]) => [
l,
isNaN(h) ? 0 : c * Math.cos((h * Math.PI) / 180),
isNaN(h) ? 0 : c * Math.sin((h * Math.PI) / 180),
];
const oklab2oklch = ([l, a, b]) => [
l,
Math.sqrt(a ** 2 + b ** 2),
Math.abs(a) < 0.0002 && Math.abs(b) < 0.0002
? NaN
: ((((Math.atan2(b, a) * 180) / Math.PI) % 360) + 360) % 360,
];
const rgb2srgbLinear = (rgb) =>
rgb.map((c) =>
Math.abs(c) <= 0.04045
? c / 12.92
: (c < 0 ? -1 : 1) * ((Math.abs(c) + 0.055) / 1.055) ** 2.4
);
const srgbLinear2rgb = (rgb) =>
rgb.map((c) =>
Math.abs(c) > 0.0031308
? (c < 0 ? -1 : 1) * (1.055 * Math.abs(c) ** (1 / 2.4) - 0.055)
: 12.92 * c
);
const oklab2xyz = (lab) => {
const LMSg = multiplyMatrices(
[
1,
0.3963377773761749,
0.2158037573099136,
1,
-0.1055613458156586,
-0.0638541728258133,
1,
-0.0894841775298119,
-1.2914855480194092,
],
lab
);
const LMS = LMSg.map((val) => val ** 3);
return multiplyMatrices(
[
1.2268798758459243,
-0.5578149944602171,
0.2813910456659647,
-0.0405757452148008,
1.112286803280317,
-0.0717110580655164,
-0.0763729366746601,
-0.4214933324022432,
1.5869240198367816,
],
LMS
);
};
const xyz2oklab = (xyz) => {
const LMS = multiplyMatrices(
[
0.819022437996703,
0.3619062600528904,
-0.1288737815209879,
0.0329836539323885,
0.9292868615863434,
0.0361446663506424,
0.0481771893596242,
0.2642395317527308,
0.6335478284694309,
],
xyz
);
const LMSg = LMS.map((val) => Math.cbrt(val));
return multiplyMatrices(
[
0.210454268309314,
0.7936177747023054,
-0.0040720430116193,
1.9779985324311684,
-2.4285922420485799,
0.450593709617411,
0.0259040424655478,
0.7827717124575296,
-0.8086757549230774,
],
LMSg
);
};
const xyz2rgbLinear = (xyz) => {
return multiplyMatrices(
[
3.2409699419045226,
-1.537383177570094,
-0.4986107602930034,
-0.9692436362808796,
1.8759675015077202,
0.04155505740717559,
0.05563007969699366,
-0.20397695888897652,
1.0569715142428786,
],
xyz
);
};
const rgbLinear2xyz = (rgb) => {
return multiplyMatrices(
[
0.41239079926595934,
0.357584339383878,
0.1804807884018343,
0.21263900587151027,
0.715168678767756,
0.07219231536073371,
0.01933081871559182,
0.11919477979462598,
0.9505321522496607,
],
rgb
);
};
const oklch2rgb = (lch) =>
srgbLinear2rgb(xyz2rgbLinear(oklab2xyz(oklch2oklab(lch))));
const rgb2oklch = (rgb) =>
oklab2oklch(xyz2oklab(rgbLinear2xyz(rgb2srgbLinear(rgb))));