Skip to content

Instantly share code, notes, and snippets.

@SephReed
Last active October 4, 2019 18:02
Show Gist options
  • Save SephReed/8c762f3434d683c66339f63342e8ae4b to your computer and use it in GitHub Desktop.
Save SephReed/8c762f3434d683c66339f63342e8ae4b to your computer and use it in GitHub Desktop.
Incomplete snippet of code used for HSLum
export function rangeLimit(num: number, min: number, max: number) {
return Math.max(min, Math.min(num, max));
}
export function rangeWrap(num: number, circumference: number) {
return ((num % 1) + 1) % 1;
}
const ratioR = 4;
const ratioG = 4;
const ratioB = 3;
const sum = ratioR + ratioG + ratioB;
const ColorResponse = {
r: ratioR / sum,
g: ratioG / sum,
b: ratioB / sum,
// b: .114,
// g: .587,
// r: .299,
};
export { ColorResponse };
import { IHSLA, IHSVA, IRGBA } from "../";
import { HSVA as HsvaTool, RGBA as RgbaTool} from "../ColorTools";
import { ColorResponse, rangeLimit, rangeWrap } from "./ColorMath";
export default class HSLATool {
public static toHex(hsla: IHSLA): string {
return RgbaTool.toHex(this.toRGBA(hsla));
}
public static toRGBA(hsla: IHSLA, mode: "fast" | "precise" = "precise"): IRGBA {
this.correctValues(hsla);
if (hsla.l <= 0) {
return { r: 0, g: 0, b: 0, a: hsla.a };
}
if (hsla.l >= 1) {
return { r: 255, g: 255, b: 255, a: hsla.a };
}
if (hsla.s === 0) {
return {
a: hsla.a,
b: Math.floor(255 * hsla.l),
g: Math.floor(255 * hsla.l),
r: Math.floor(255 * hsla.l),
};
}
interface ILumaColor {
color: string;
value: number;
lumaFix: number;
}
const rgba = HsvaTool.toRGBA({
h: hsla.h,
s: hsla.s,
v: 1,
});
const max = {} as any as ILumaColor;
const mid = {} as any as ILumaColor;
const min = {} as any as ILumaColor;
const maxMidMin = [max, mid, min];
([
[`r`, rgba.r, ColorResponse.r],
[`g`, rgba.g, ColorResponse.g],
[`b`, rgba.b, ColorResponse.b],
] as Array<[string, number, number]>).sort((a, b) => b[1] - a[1])
.forEach((item, i) => {
maxMidMin[i].color = item[0];
maxMidMin[i].value = item[1];
maxMidMin[i].lumaFix = item[2];
});
if (mode === "fast") {
let lumsAddedAtMaxScale = 0;
maxMidMin.forEach((item: ILumaColor) => {
lumsAddedAtMaxScale += (item.lumaFix * (item.value / max.value));
});
if (lumsAddedAtMaxScale >= hsla.l) {
const scale = hsla.l / lumsAddedAtMaxScale;
rgba.r = rgba.r * scale;
rgba.g = rgba.g * scale;
rgba.b = rgba.b * scale;
} else {
let remainder = hsla.l - lumsAddedAtMaxScale;
let lumaPerUnit = (mid.lumaFix + min.lumaFix) / 255;
const unitsToAdd = remainder / lumaPerUnit;
if (unitsToAdd <= 255 - rgba[mid.color]) {
rgba[mid.color] += unitsToAdd;
rgba[min.color] += unitsToAdd;
} else {
rgba[mid.color] = 255;
remainder = hsla.l - (max.lumaFix + mid.lumaFix);
lumaPerUnit = min.lumaFix / 255.0;
rgba[min.color] = remainder / lumaPerUnit;
}
}
RgbaTool.correctValues(rgba);
return rgba;
} else {
const ratioMid = mid.value / max.value;
const ratioMin = min.value / max.value;
const targetMax = Math.sqrt(
Math.pow(hsla.l, 2)
/ (max.lumaFix
+ (mid.lumaFix * Math.pow(ratioMid, 2))
+ (min.lumaFix * Math.pow(ratioMin, 2))
),
);
const scaledHue = {a: hsla.a} as any;
scaledHue[max.color] = targetMax * 255;
scaledHue[mid.color] = targetMax * ratioMid * 255;
scaledHue[min.color] = targetMax * ratioMin * 255;
RgbaTool.correctValues(scaledHue);
if (targetMax > 1) {
const targetWhiteLightLum = hsla.l - (max.lumaFix + (ratioMid * mid.lumaFix) + (ratioMin * min.lumaFix));
// const lightToAdd = targetWhiteLightLum / (mid.lumaFix + min.lumaFix);
const currentLum = RgbaTool.getLuminosity(scaledHue);
const mixAmount = (hsla.l - currentLum) / (1 - currentLum);
scaledHue[mid.color] += (255 - scaledHue[mid.color]) * mixAmount;
scaledHue[min.color] += (255 - scaledHue[min.color]) * mixAmount;
}
// if (targetMax > 1.3 - overlap) {
// const targetWhiteLightLum = hsla.l - max.lumaFix
// - (mid.lumaFix * ratioMid)
// - (min.lumaFix * ratioMin)
// + .03 - (.12 * hsla.s);
// const targetWhiteAdd = Math.sqrt(
// Math.pow(targetWhiteLightLum, 2)
// / (mid.lumaFix + min.lumaFix),
// );
// const whiteOut = {a: hsla.a} as any;
// whiteOut[max.color] = 255;
// whiteOut[mid.color] = (ratioMid + (targetWhiteAdd * (1 + hsla.s))) * 255;
// whiteOut[min.color] = (ratioMin + (targetWhiteAdd * (1 + hsla.s))) * 255;
// RgbaTool.correctValues(whiteOut);
// const mixAmount = (targetMax - (1 - overlap)) / overlap;
// if (mixAmount >= 1) {
// return whiteOut;
// } else {
// scaledHue.r += (scaledHue.r - whiteOut.r) * mixAmount;
// scaledHue.g += (scaledHue.g - whiteOut.g) * mixAmount;
// scaledHue.b += (scaledHue.b - whiteOut.b) * mixAmount;
// RgbaTool.correctValues(scaledHue);
// }
// }
RgbaTool.correctValues(scaledHue);
return scaledHue;
}
}
public static toHSVA(hsla: IHSLA): IHSVA {
throw new Error("Method not implemented.");
}
public static correctValues(hsla: IHSLA): void {
hsla.h = rangeWrap(hsla.h, 1);
hsla.s = rangeLimit(hsla.s, 0, 1);
hsla.l = rangeLimit(hsla.l, 0, 1);
hsla.a = hsla.a !== undefined ? rangeLimit(hsla.a, 0, 1) : 1;
}
public static getLuminosity(hsla: IHSLA): number {
return hsla.l;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment