ink.js
2.4 kB · javascript · 85 lines
1import { CLEAR } from "./frame.js";23const NAMES = ["ground", "panel", "line", "fg", "dim", "red", "orange", "yellow", "green", "mint", "teal", "cyan", "blue", "indigo", "purple", "pink", "brown", "gray"];45// COLOR67const byte = (v) => {8 const r = Math.round(v);9 return r > 255 ? 255 : r > 0 ? r : 0;10};11const clamp = (v) => (v < 0 ? 0 : v > 1 ? 1 : v);1213function color(value, name) {14 if (typeof value === "string" && /^#?([0-9a-f]{6}|[0-9a-f]{8})$/i.test(value)) {15 const hex = value.replace("#", "");16 const n = (i) => parseInt(hex.slice(i, i + 2), 16);17 return Object.freeze([n(0), n(2), n(4), hex.length === 8 ? n(6) : 255]);18 }19 if (value && typeof value === "object" && "r" in value) return Object.freeze([value.r, value.g, value.b, value.a ?? 255]);20 if (value && value.length >= 3) return Object.freeze([value[0], value[1], value[2], value[3] ?? 255]);21 throw new Error(`ink: ${name} is not a color: ${value}`);22}2324// MIXING2526export function mix(a, b, t) {27 const s = clamp(t);28 const lerp = (x, y) => byte(x + (y - x) * s);29 return [lerp(a[0], b[0]), lerp(a[1], b[1]), lerp(a[2], b[2]), lerp(a[3], b[3])];30}3132export function fade(c, alpha) {33 return [c[0], c[1], c[2], byte(255 * clamp(alpha))];34}3536// RAMP3738export class Ramp {39 constructor(stops, ground = CLEAR) {40 this.stops = stops;41 this.ground = ground;42 }4344 at(t) {45 const n = this.stops.length;46 if (n === 0) return this.ground;47 if (n === 1) return this.stops[0];48 const s = clamp(t) * (n - 1);49 const i = Math.min(Math.floor(s) || 0, n - 2);50 return mix(this.stops[i], this.stops[i + 1], s - i);51 }52}5354// INK5556export function ink(palette) {57 const out = {};58 for (const name of NAMES) {59 if (palette[name] === undefined) throw new Error(`ink: the palette has no ${name}`);60 out[name] = color(palette[name], name);61 }62 const { ground, fg, blue, orange, yellow, green, pink, indigo } = out;63 class Bound extends Ramp {64 constructor(stops) {65 super(stops, ground);66 }6768 static heat() {69 return new Bound([ground, blue, yellow, fg]);70 }7172 static fire() {73 return new Bound([ground, orange, yellow, fg]);74 }7576 static diverge() {77 return new Bound([blue, ground, orange]);78 }7980 static tone(a, b) {81 return new Bound([a, b]);82 }83 }84 return Object.freeze({ ...out, inks: Object.freeze([blue, orange, yellow, green, pink, indigo]), mix, fade, Ramp: Bound });85}