@absulit/points
Version:
A Generative Art library made in WebGPU
200 lines (161 loc) • 5.47 kB
JavaScript
/**
* @class RGBAColor
* @ignore
*/
class RGBAColor {
#value;
constructor(r = 0, g = 0, b = 0, a = 1) {
if (r > 1 && g > 1 && b > 1) {
r /= 255;
g /= 255;
b /= 255;
if (a > 1) {
a /= 255;
}
}
this.#value = [r, g, b, a];
}
set r(value) {
this.#value[0] = value;
}
set g(value) {
this.#value[1] = value;
}
set b(value) {
this.#value[2] = value;
}
set a(value) {
this.#value[3] = value;
}
get r() {
return this.#value[0];
}
get g() {
return this.#value[1];
}
get b() {
return this.#value[2];
}
get a() {
return this.#value[3];
}
get value() {
return this.#value;
}
get brightness() {
// #Standard
// LuminanceA = (0.2126*R) + (0.7152*G) + (0.0722*B)
// #Percieved A
// LuminanceB = (0.299*R + 0.587*G + 0.114*B)
// #Perceived B, slower to calculate
// LuminanceC = sqrt(0.299*(R**2) + 0.587*(G**2) + 0.114*(B**2))
let [r, g, b, a] = this.#value;
return (0.2126 * r) + (0.7152 * g) + (0.0722 * b);
}
set brightness(value) {
this.#value = [value, value, value, 1];
}
set(r, g, b, a) {
this.#value = [r, g, b, a]
}
setColor(color) {
this.#value = [color.r, color.g, color.b, color.a];
}
add(color) {
let [r, g, b, a] = this.#value;
//this.#value = [(r + color.r)/2, (g + color.g)/2, (b + color.b)/2, (a + color.a)/2];
//this.#value = [(r*a + color.r*color.a), (g*a + color.g*color.a), (b*a + color.b*color.a), 1];
this.#value = [(r + color.r), (g + color.g), (b + color.b), (a + color.a)];
}
blend(color) {
let [r0, g0, b0, a0] = this.#value;
let [r1, b1, g1, a1] = color.value;
let a01 = (1 - a0) * a1 + a0
let r01 = ((1 - a0) * a1 * r1 + a0 * r0) / a01
let g01 = ((1 - a0) * a1 * g1 + a0 * g0) / a01
let b01 = ((1 - a0) * a1 * b1 + a0 * b0) / a01
this.#value = [r01, g01, b01, a01];
}
additive(color) {
// https://gist.github.com/JordanDelcros/518396da1c13f75ee057
let base = this.#value;
let added = color.value;
let mix = [];
mix[3] = 1 - (1 - added[3]) * (1 - base[3]); // alpha
mix[0] = Math.round((added[0] * added[3] / mix[3]) + (base[0] * base[3] * (1 - added[3]) / mix[3])); // red
mix[1] = Math.round((added[1] * added[3] / mix[3]) + (base[1] * base[3] * (1 - added[3]) / mix[3])); // green
mix[2] = Math.round((added[2] * added[3] / mix[3]) + (base[2] * base[3] * (1 - added[3]) / mix[3])); // blue
this.#value = mix;
}
equal(color) {
return (this.#value[0] == color.r) && (this.#value[1] == color.g) && (this.#value[2] == color.b) && (this.#value[3] == color.a);
}
static average(colors) {
// https://sighack.com/post/averaging-rgb-colors-the-right-way
let r = 0, g = 0, b = 0, a = 0;
for (let index = 0; index < colors.length; index++) {
const color = colors[index];
//if (!color.isNull()) {
r += color.r * color.r;
g += color.g * color.g;
b += color.b * color.b;
//a += color.a * color.a;
//}
}
return new RGBAColor(
Math.sqrt(r / colors.length),
Math.sqrt(g / colors.length),
Math.sqrt(b / colors.length)
//Math.sqrt(a),
);
}
static difference(c1, c2) {
let r = 0;
let g = 0;
let b = 0;
if(c1 && !c1.isNull() && c2 && !c2.isNull()){
const { r: r1, g: g1, b: b1 } = c1;
const { r: r2, g: g2, b: b2 } = c2;
r = r1 - r2;
g = g1 - g2;
b = b1 - b2;
}
return new RGBAColor(r, g, b);
}
isNull() {
const [r, g, b, a] = this.#value;
return !(isNaN(r) && isNaN(g) && isNaN(b) && isNaN(a))
}
static colorRGBEuclideanDistance(c1, c2) {
return Math.sqrt(Math.pow(c1.r - c2.r, 2) +
Math.pow(c1.g - c2.g, 2) +
Math.pow(c1.b - c2.b, 2));
}
/**
* Checks how close two colors are. Closest is `0`.
* @param {RGBAColor} color : Color to check distance;
* @returns Number distace up to `1.42` I think...
*/
euclideanDistance(color) {
const [r, g, b] = this.#value;
return Math.sqrt(Math.pow(r - color.r, 2) +
Math.pow(g - color.g, 2) +
Math.pow(b - color.b, 2));
}
static getClosestColorInPalette(color, palette) {
if(!palette){
throw('Palette should be an array of `RGBA`s')
}
let distance = 100;
let selectedColor = null;
palette.forEach(paletteColor => {
let currentDistance = color.euclideanDistance(paletteColor);
if (currentDistance < distance) {
selectedColor = paletteColor;
distance = currentDistance;
}
})
return selectedColor;
}
}
export default RGBAColor;