color-tf
Version:
RGB, HSL, HSV, HWB and more color models convertors
104 lines (90 loc) • 3.11 kB
JavaScript
;
var hsl2hsv = require('./hsl2hsv.js');
var hsl2rgb = require('./hsl2rgb.js');
var hsv2hsl = require('./hsv2hsl.js');
var hsv2hwb = require('./hsv2hwb.js');
var hsv2rgb = require('./hsv2rgb.js');
var hwb2hsv = require('./hwb2hsv.js');
var hwb2rgb = require('./hwb2rgb.js');
var rgb2hsl = require('./rgb2hsl.js');
var rgb2hsv = require('./rgb2hsv.js');
var rgb2hwb = require('./rgb2hwb.js');
var rgbToHex = require('./rgbToHex.js');
var hexToRgb = require('./hexToRgb.js');
var lib = /*#__PURE__*/Object.freeze({
hsl2hsv: hsl2hsv,
hsl2rgb: hsl2rgb,
hsv2hsl: hsv2hsl,
hsv2hwb: hsv2hwb,
hsv2rgb: hsv2rgb,
hwb2hsv: hwb2hsv,
hwb2rgb: hwb2rgb,
rgb2hsl: rgb2hsl,
rgb2hsv: rgb2hsv,
rgb2hwb: rgb2hwb
});
/**
* @return fns to compose between 2 keys in lib object e.g. fns.reduceRight((fn, f) => (...a) => f(...fn(...a)))
*/
var getFnPath = (lib, fromKey, toKey) => {
let nodes = [fromKey];
const visited = new Map(); // map node key => parent key
while (nodes.length) {
// search breadth-first
const newNodes = [];
for (const k of nodes) {
if (lib[k + '2' + toKey]) {
// done, we can stop
const fns = [`${k}2${toKey}`];
for (let key = k; visited.has(key) && key !== fromKey; key = visited.get(key)) {
fns.push(`${visited.get(key)}2${key}`);
}
return fns;
}
Object.keys(lib)
.filter(s => s.slice(0, 3) === k)
.map(s => s.slice(4))
.filter(key => !visited.has(key))
.forEach(key => {
visited.set(key, k);
newNodes.push(key);
});
}
nodes = newNodes;
}
};
const roundH = ([h, s, l]) => [Math.round(360 * h) % 360, Math.round(100 * s), Math.round(100 * l)];
/**
* all functions available from a Proxy (to generate missing ones dynamically)
* foo2bar for functions with input/output in [0, 1]
* fooToBar for functions with natural inputs [0,255] for r,g,b, [0,360[ for hue, [0, 100] for the rest
*/
var proxy = new Proxy(
new Map([...Object.entries(lib), ['rgbToHex', rgbToHex], ['hexToRgb', hexToRgb]]),
{
get: (map, key) => {
if (typeof key !== 'string') return map;
if (map.has(key)) return map.get(key);
const fromKey = key.slice(0, 3);
const toKey = key.slice(-3).toLowerCase();
const k = fromKey + '2' + toKey;
let fn = lib[k];
if (!fn) {
// todo check fromKey, toKey are in available keys, else getPath might be in infinite loop
const fns = getFnPath(lib, fromKey, toKey).map(n => lib[n]);
fn = fns.reduceRight((f, g) => (...a) => g(...f(...a)));
map.set(k, fn);
}
if (key[3] === '2') return fn;
const K = fromKey + 'To' + toKey[0].toUpperCase() + toKey.slice(1);
const FN = fromKey === 'rgb'
? (r, g, b) => roundH(fn(r / 255, g / 255, b / 255))
: toKey === 'rgb'
? (h, x, y) => fn(h / 360, x / 100, y / 100).map(v => Math.round(v * 255))
: (h, x, y) => roundH(fn(h / 360, x / 100, y / 100));
map.set(K, FN);
return FN;
}
}
);
module.exports = proxy;