rampensau
Version:
Color ramp generator using curves within the HSL color model
120 lines (101 loc) • 3.27 kB
text/typescript
import { makeCurveEasings } from "./utils";
import { normalizeHue } from "./colorUtils";
import type { Vector2, Vector3 } from "./colorUtils";
import type { CurveMethod } from "./utils";
export type ModifiedEasingFn = (x: number, fr?: number) => number;
export type hueArguments = {
hStart?: number;
hStartCenter?: number;
hCycles?: number;
hEasing?: ModifiedEasingFn;
};
export type presetHues = {
hueList: number[];
};
export type saturationArguments = {
sRange?: Vector2;
sEasing?: ModifiedEasingFn;
};
export type lightnessArguments = {
lRange?: Vector2;
lEasing?: ModifiedEasingFn;
};
type BaseGenerateColorRampArgument = {
total?: number;
transformFn?: (hsl: Vector3, i?: number) => Vector3 | string;
} & hueArguments &
saturationArguments &
lightnessArguments;
export type GenerateColorRampArgument = BaseGenerateColorRampArgument & {
hueList?: never;
};
export type GenerateColorRampArgumentFixedHues = BaseGenerateColorRampArgument &
presetHues;
/**
* Generates a color ramp based on the HSL color space.
* @param {GenerateColorRampArgument} args - The arguments to generate the ramp.
* @returns {Array<number>} - The color ramp.
*/
export function generateColorRamp({
total = 9,
hStart = Math.random() * 360,
hStartCenter = 0.5,
hEasing = (x) => x,
hCycles = 1,
sRange = [0.4, 0.35],
sEasing = (x) => Math.pow(x, 2),
lRange = [Math.random() * 0.1, 0.9],
lEasing = (x) => Math.pow(x, 1.5),
transformFn = ([h, s, l]) => [h, s, l],
hueList,
}:
| GenerateColorRampArgument
| GenerateColorRampArgumentFixedHues = {}): Vector3[] {
// creates a range of lightness and saturation based on the corresponding min and max values
const lDiff: number = lRange[1] - lRange[0];
const sDiff: number = sRange[1] - sRange[0];
// if hueList is provided, use it's length as the length of the ramp
const length = hueList && hueList.length > 0 ? hueList.length : total;
return Array.from({ length }, (_, i) => {
const relI = length > 1 ? i / (length - 1) : 0;
const fraction = 1 / length;
const hue = hueList
? (hueList[i] as number)
: normalizeHue(
hStart + // Add the starting hue
(1 - hEasing(relI, fraction) - hStartCenter) * (360 * hCycles) // Calculate the hue based on the easing function
);
const saturation = sRange[0] + sDiff * sEasing(relI, fraction);
const lightness = lRange[0] + lDiff * lEasing(relI, fraction);
return transformFn([hue, saturation, lightness], i) as Vector3; // Ensure the array is of type Vector3
});
}
export const generateColorRampWithCurve = ({
total = 9,
hStart = Math.random() * 360,
hStartCenter = 0.5,
hCycles = 1,
sRange = [0.4, 0.35],
lRange = [Math.random() * 0.1, 0.9],
hueList,
curveMethod = "lamé",
curveAccent = 0.5,
transformFn = ([h, s, l]) => [h, s, l],
}: (GenerateColorRampArgument | GenerateColorRampArgumentFixedHues) & {
curveMethod?: CurveMethod;
curveAccent?: number;
} = {}): Vector3[] => {
const { sEasing, lEasing } = makeCurveEasings(curveMethod, curveAccent);
return generateColorRamp({
total,
hStart,
hStartCenter,
hCycles,
sRange,
lRange,
sEasing,
lEasing,
transformFn,
hueList,
});
};