textui/src/colors.ts
2021-09-07 00:35:15 +02:00

78 lines
2.3 KiB
TypeScript

import { Color, PaletteMap } from "./base.ts";
import { UIPalette } from "./config.ts";
import { cmp } from "../deps.ts";
import { Display } from "./display.ts";
export async function getPaletteMapping(
palette: UIPalette,
display: Display,
): Promise<PaletteMap> {
// get the colors supported by display
const app_colors = palette.map((c): Color[] => Array.isArray(c) ? c : [c]);
const all_colors = app_colors.reduce((acc, val) => acc.concat(val), []);
const display_colors = await display.setupPalette(all_colors);
// rank all supported colors by proximity to each app color
let ranked: {
color: Color;
idx: number;
penalty: number;
matches: { color: Color; idx: number; distance: number }[];
}[] = [];
app_colors.forEach((colors, idx) => {
colors.forEach((color, alt) => {
ranked.push({
color,
idx,
penalty: alt + 1,
matches: display_colors.map((display_color, didx) => {
return {
color: display_color,
idx: didx,
distance: colorDistance(color, display_color),
};
}).sort(cmp({ key: (info) => info.distance })),
});
});
});
// TODO negatively score colors too much near previously chosen ones
// find the best color mapping for each source color
const result = palette.map(() => -1);
while (ranked.length > 0) {
ranked.sort(
cmp({ key: (info) => info.matches[0].distance * info.penalty }),
);
const best = ranked[0];
const app_idx = best.idx;
const display_idx = best.matches[0].idx;
result[app_idx] = display_idx;
for (const color of ranked) {
color.matches = color.matches.filter((match) =>
match.idx !== display_idx
);
}
ranked = ranked.filter((color) =>
color.idx !== app_idx && color.matches.length > 0
);
}
return result;
}
function colorDistance(e1: Color, e2: Color): number {
/*return (e2.r - e1.r) * (e2.r - e1.r) +
(e2.g - e1.g) * (e2.g - e1.g) +
(e2.b - e1.b) * (e2.b - e1.b);*/
const c = (x: number) => Math.round(x * 255);
const rmean = (c(e1.r) + c(e2.r)) / 2;
const r = c(e1.r) - c(e2.r);
const g = c(e1.g) - c(e2.g);
const b = c(e1.b) - c(e2.b);
return Math.sqrt(
(((512 + rmean) * r * r) >> 8) + 4 * g * g + (((767 - rmean) * b * b) >> 8),
);
}