toosoon-utils
Version:
Utility functions & classes
348 lines (347 loc) • 14.2 kB
JavaScript
import { clamp, lerp } from '../../maths';
import Color from './Color';
/**
* Utility class for generating and processing color scales
*
* @exports
* @class ColorScale
*/
export default class ColorScale {
isColorScale = true;
type = 'ColorScale';
/**
* Array of colors composing this color scale
*/
colors = [];
*[Symbol.iterator]() {
yield* this.colors;
}
/**
* Pick an interpolated color from this color scale
*
* @param {number} t Normalized time value to interpolate
* @returns {Color} Interpolated color on this scale
*/
getColor(t) {
const index = lerp(t, 0, this.length - 1);
return this.colors[index];
}
/**
* Set this color scale colors to an array of interpolated colors
*
* @param {ColorInterpolation} interpolation Type of interpolation used for generation
* @param {number} length Amount of colors to generate
* @param {Color} color1 Start color
* @param {Color} color2 End color
* @param {object} [params] Interpolation parameters
* @returns {this}
*/
generate(interpolation, length, color1, color2, params) {
this.colors = ColorScale.generate(interpolation, length, color1, color2, params);
return this;
}
/**
* Set this color scale colors to an array of interpolated colors in the RGB color space
*
* @param {number} length Amount of colors to generate
* @param {Color} color1 Start color
* @param {Color} color2 End color
* @param {object} [params] Interpolation parameters
* @param {number} [params.power] Interpolation exponent
* @returns {this}
*/
generateRgb(length, color1, color2, params) {
this.colors = ColorScale.generateRgb(length, color1, color2, params);
return this;
}
/**
* Set this color scale colors to an array of interpolated colors in the HSL color space
*
* @param {number} length Amount of colors to generate
* @param {Color} color1 Start color
* @param {Color} color2 End color
* @param {object} [params] Interpolation parameters
* @param {number|number[]} [params.power] Interpolation exponent(s) : [h, s, l]
* @param {string} [params.hueMode] Hue interpolation mode. Can be 'direct' | 'shortest' | 'longest'
* @returns {this}
*/
generateHsl(length, color1, color2, params) {
this.colors = ColorScale.generateHsl(length, color1, color2, params);
return this;
}
/**
* Set this color scale colors to an array of interpolated colors in the HSB color space
*
* @param {number} length Amount of colors to generate
* @param {Color} color1 Start color
* @param {Color} color2 End color
* @param {object} [params] Interpolation parameters
* @param {number|number[]} [params.power] Interpolation exponent(s) : [h, s, b]
* @param {string} [params.hueMode] Hue interpolation mode. Can be 'direct' | 'shortest' | 'longest'
* @returns {this}
*/
generateHsb(length, color1, color2, params) {
this.colors = ColorScale.generateHsb(length, color1, color2, params);
return this;
}
/**
* Set this color scale colors to an array of interpolated colors following HCL Qualitative color palettes algorithm
*
* @param {number} length Amount of colors to generate
* @param {Color} color1 Start color
* @param {Color} color2 End color
* @param {object} [params] Interpolation parameters
* @param {string} [params.hueMode] Hue interpolation mode. Can be 'direct' | 'shortest' | 'longest'
* @returns {this}
*/
generateQualitative(length, color1, color2, params) {
this.colors = ColorScale.generateQualitative(length, color1, color2, params);
return this;
}
/**
* Set this color scale colors to an array of interpolated colors following HCL Sequential color palettes algorithm
*
* @param {number} length Amount of colors to generate
* @param {Color} color1 Start color
* @param {Color} color2 End color
* @param {number|number[]} [params.power] Interpolation exponent(s) : [c, l]
* @param {string} [params.hueMode] Hue interpolation mode. Can be 'direct' | 'shortest' | 'longest'
* @param {number} [params.chromaMax] Maximum chroma value
* @returns {this}
*/
generateSequential(length, color1, color2, params) {
this.colors = ColorScale.generateSequential(length, color1, color2, params);
return this;
}
/**
* Set this color scale colors to an array of interpolated colors following HCL Diverging color palettes algorithm
*
* @param {number} length Amount of colors to generate
* @param {Color} color1 Start color
* @param {Color} color2 End color
* @param {object} [params] Interpolation parameters
* @param {number|number[]} [params.power] Interpolation exponent(s) : ([c, l])
* @returns {this}
*/
generateDiverging(length, color1, color2, params) {
this.colors = ColorScale.generateDiverging(length, color1, color2, params);
return this;
}
/**
* Process all colors composing this scale
*
* @param {ColorScaleProcessingParameters} [params] Processing parameters
* @returns {this}
*/
process(params) {
ColorScale.process(this, params);
return this;
}
/**
* Amount of colors composing this color scale
*/
get length() {
return this.colors.length;
}
/**
* Generate an array of interpolated colors
*
* @param {ColorInterpolation} interpolation Type of interpolation used for generation
* @param {number} length Amount of colors to generate
* @param {Color} color1 Start color
* @param {Color} color2 End color
* @param {object} [params] Interpolation parameters
* @returns {Color[]} Generated color scale
*/
static generate(interpolation, length, color1, color2, params) {
const interpolate = ColorScale._getInterpolateFunction(interpolation, color1, color2, params);
const colors = [];
for (let i = 0; i < length; i++) {
const t = i / (length - 1);
const color = interpolate(t);
colors.push(color);
}
return colors;
}
/**
* Generate an array of interpolated colors in the RGB color space
*
* @param {number} length Amount of colors to generate
* @param {Color} color1 Start color
* @param {Color} color2 End color
* @param {object} [params] Interpolation parameters
* @param {number} [params.power] Interpolation exponent
* @returns {Color[]} Generated RGB color scale
*/
static generateRgb(length, color1, color2, params) {
return ColorScale.generate('rgb', length, color1, color2, params);
}
/**
* Generate an array of interpolated colors in the HSL color space
*
* @param {number} length Amount of colors to generate
* @param {Color} color1 Start color
* @param {Color} color2 End color
* @param {object} [params] Interpolation parameters
* @param {number|number[]} [params.power] Interpolation exponent(s) : [h, s, l]
* @param {string} [params.hueMode] Hue interpolation mode. Can be 'direct' | 'shortest' | 'longest'
* @returns {Color[]} Generated HSL color scale
*/
static generateHsl(length, color1, color2, params) {
return ColorScale.generate('hsl', length, color1, color2, params);
}
/**
* Generate an array of interpolated colors in the HSB color space
*
* @param {number} length Amount of colors to generate
* @param {Color} color1 Start color
* @param {Color} color2 End color
* @param {object} [params] Interpolation parameters
* @param {number|number[]} [params.power] Interpolation exponent(s) : [h, s, b]
* @param {string} [params.hueMode] Hue interpolation mode. Can be 'direct' | 'shortest' | 'longest'
* @returns {Color[]} Generated HSB color scale
*/
static generateHsb(length, color1, color2, params) {
return ColorScale.generate('hsb', length, color1, color2, params);
}
/**
* Generate an array of interpolated colors following HCL Qualitative color palettes algorithm
*
* @param {number} length Amount of colors to generate
* @param {Color} color1 Start color
* @param {Color} color2 End color
* @param {object} [params] Interpolation parameters
* @param {string} [params.hueMode] Hue interpolation mode. Can be 'direct' | 'shortest' | 'longest'
* @returns {Color[]} Generated qualitative color scale
*/
static generateQualitative(length, color1, color2, params) {
return ColorScale.generate('qualitative', length, color1, color2, params);
}
/**
* Generate an array of interpolated colors following HCL Sequential color palettes algorithm
*
* @param {number} length Amount of colors to generate
* @param {Color} color1 Start color
* @param {Color} color2 End color
* @param {object} [params] Interpolation parameters
* @param {number|number[]} [params.power] Interpolation exponent(s) : [c, l]
* @param {string} [params.hueMode] Hue interpolation mode. Can be 'direct' | 'shortest' | 'longest'
* @param {number} [params.chromaMax] Maximum chroma value
* @returns {Color[]} Generated sequential color scale
*/
static generateSequential(length, color1, color2, params) {
return ColorScale.generate('sequential', length, color1, color2, params);
}
/**
* Generate an array of interpolated colors following HCL Diverging color palettes algorithm
*
* @param {number} length Amount of colors to generate
* @param {Color} color1 Start color
* @param {Color} color2 End color
* @param {object} [params] Interpolation parameters
* @param {number|number[]} [params.power] Interpolation exponent(s) : ([c, l])
* @returns {Color[]} Generated diverging color scale
*/
static generateDiverging(length, color1, color2, params) {
return ColorScale.generate('diverging', length, color1, color2, params);
}
/**
* Process a given value
*
* @param {number} value Value to process
* @param {ColorScaleProcessingTarget} [target] Processing target
* @returns {number} Processed value
*/
static processValue(value, target) {
if (typeof target === 'undefined')
return value;
if (typeof target === 'number')
return target;
if (Array.isArray(target))
return clamp(value, target[0], target[1]);
return value;
}
/**
* Process a given color
*
* @param {Color} color Color to process
* @param {ColorScaleProcessingParameters} [params] Processing parameters
* @returns {Color} Processed color
*/
static processColor(color, params = {}) {
if (params.hsl) {
let [h, s, l] = color.hsl;
h = this.processValue(h, params.hsl.hue);
s = this.processValue(s, params.hsl.saturation);
l = this.processValue(l, params.hsl.lightness);
color.setHsl([h, s, l]);
}
if (params.hsb) {
let [h, s, b] = color.hsb;
h = this.processValue(h, params.hsb.hue);
s = this.processValue(s, params.hsb.saturation);
b = this.processValue(b, params.hsb.brightness);
color.setHsb([h, s, b]);
}
if (params.hcl) {
let [h, c, l] = color.hcl;
h = this.processValue(h, params.hcl.hue);
c = this.processValue(c, params.hcl.chroma);
l = this.processValue(l, params.hcl.luminance);
color.setHcl([h, c, l]);
}
return color;
}
/**
* Process all colors composing a given scale
*
* @param {ColorScale} scale Color scale to process
* @param {ColorScaleProcessingParameters} [params] Processing parameters
* @returns {ColorScale} Processed color scale
*/
static process(scale, params = {}) {
scale.colors.forEach((color) => this.processColor(color, params));
return scale;
}
static _getInterpolateFunction(interpolation, color1, color2, params) {
switch (interpolation) {
case 'rgb':
default:
return (t) => {
const rgb = Color.lerpRgb(t, color1.rgb, color2.rgb, params);
const color = new Color().setRgb(rgb);
return color;
};
case 'hsl':
return (t) => {
const hsl = Color.lerpHsl(t, color1.hsl, color2.hsl, params);
const color = new Color().setHsl(hsl);
return color;
};
case 'hsb':
return (t) => {
const hsb = Color.lerpHsb(t, color1.hsb, color2.hsb, params);
const color = new Color().setHsb(hsb);
return color;
};
case 'qualitative':
return (t) => {
const hcl = Color.interpolateQualitative(t, color1.hcl, color2.hcl, params);
const color = new Color().setHcl(hcl);
return color;
};
case 'sequential':
return (t) => {
const hcl = Color.interpolateSequential(t, color1.hcl, color2.hcl, params);
const color = new Color().setHcl(hcl);
return color;
};
case 'diverging':
return (t) => {
const hcl = Color.interpolateDiverging(t, color1.hcl, color2.hcl, params);
const color = new Color().setHcl(hcl);
return color;
};
}
}
}