diff --git a/packages/color/src/api.ts b/packages/color/src/api.ts index 5da90f7b98..3cf54a52f3 100644 --- a/packages/color/src/api.ts +++ b/packages/color/src/api.ts @@ -5,7 +5,12 @@ import type { IVector, ReadonlyVec, Vec } from "@thi.ng/vectors"; export type Color = Vec; export type ReadonlyColor = ReadonlyVec; -export type MaybeColor = string | number | ReadonlyColor | IColor; +export type MaybeColor = + | TypedColor + | IParsedColor + | ReadonlyColor + | string + | number; export type ColorOp = (out: Color | null, src: ReadonlyColor) => Color; @@ -64,13 +69,8 @@ export interface ColorSpec { } export interface ColorFactory> { - ( - col: TypedColor | ParsedColor | string | number, - buf?: Color, - idx?: number, - stride?: number - ): T; - (col?: Color, idx?: number, stride?: number): T; + (col: MaybeColor, buf?: Vec, idx?: number, stride?: number): T; + (col?: Vec, idx?: number, stride?: number): T; (a: number, b: number, c: number, ...xs: number[]): T; /** @@ -124,7 +124,14 @@ export interface TypedColor extends IColor, IDeref, IVector { * Step size between channels */ stride: number; - random(rnd?: IRandom): this; + /** + * Randomizes all color channels based on channel ranges defined for this + * color type (usually [0..1] interval). Alpha channel will remain + * untouched. + * + * @param rnd + */ + randomize(rnd?: IRandom): this; /** * Copies `src` into this color's array. * @@ -138,7 +145,13 @@ export interface TypedColor extends IColor, IDeref, IVector { toJSON(): number[]; } -export class ParsedColor implements IDeref { +export interface IParsedColor extends IColor, IDeref {} + +/** + * Result type returned by {@link parseCss}, a simple wrapper for a raw color + * array and color mode. + */ +export class ParsedColor implements IParsedColor { constructor(public readonly mode: ColorMode, public value: Color) {} deref() { diff --git a/packages/color/src/css/css.ts b/packages/color/src/css/css.ts index 8d62d615d5..31faa468f3 100644 --- a/packages/color/src/css/css.ts +++ b/packages/color/src/css/css.ts @@ -1,6 +1,6 @@ import type { Fn } from "@thi.ng/api"; import { isNumber, isString } from "@thi.ng/checks"; -import type { ColorMode, TypedColor, ReadonlyColor } from "../api"; +import type { ColorMode, IParsedColor, MaybeColor, TypedColor } from "../api"; import { convert } from "../convert"; import { hslCss } from "../hsl/hsl-css"; import { hsvCss } from "../hsv/hsv-css"; @@ -19,7 +19,18 @@ const CSS_CONVERSIONS: Partial>> = { srgb: srgbCss, }; -export const css = (src: TypedColor | ReadonlyColor | string | number) => { +/** + * Takes a color in one of the following formats and tries to convert it + * to a CSS string: + * + * - any {@link TypedColor} instance + * - raw sRGB(A) vector + * - number (packed 0xaarrggbb int, MUST provide alpha channel) + * - string (passthrough) + * + * @param col - source color + */ +export const css = (src: Exclude) => { if (isString(src)) return src; if (isNumber(src)) return int32Css(src); if ((>src).mode) { diff --git a/packages/color/src/css/parse-css.ts b/packages/color/src/css/parse-css.ts index 41e67b1123..5b000eab68 100644 --- a/packages/color/src/css/parse-css.ts +++ b/packages/color/src/css/parse-css.ts @@ -3,7 +3,7 @@ import { assert } from "@thi.ng/api"; import { isString } from "@thi.ng/checks"; import { illegalArgs, unsupported } from "@thi.ng/errors"; import { clamp01, TAU } from "@thi.ng/math"; -import { ParsedColor } from "../api"; +import { IParsedColor, ParsedColor } from "../api"; import { CSS_NAMES } from "../api/names"; import { CSS_SYSTEM_COLORS } from "../api/system"; import { int32Srgb } from "../int/int-srgb"; @@ -43,7 +43,7 @@ import { ensureHue } from "../internal/ensure-hue"; * * @param src */ -export const parseCss = (src: string | IDeref) => { +export const parseCss = (src: string | IDeref): IParsedColor => { src = (isString(src) ? src : src.deref()).toLowerCase(); const named = (CSS_NAMES)[src] || (CSS_SYSTEM_COLORS)[src]; if (named || src[0] === "#") diff --git a/packages/color/src/css/resolve.ts b/packages/color/src/css/resolve.ts deleted file mode 100644 index 1255563ad5..0000000000 --- a/packages/color/src/css/resolve.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { isArrayLike, isString } from "@thi.ng/checks"; -import type { IColor, ReadonlyColor } from "../api"; -import { css } from "./css"; -import { int32Css } from "../int/int-css"; -import { srgbCss } from "../srgb/srgb-css"; - -/** - * Takes a color in one of the following formats and tries to convert it - * to a CSS string: - * - * - any IColor instance - * - raw sRGB vector - * - number (packed 0xaarrggbb int) - * - string (unchanged) - * - * @param col - source color - */ -export const resolveAsCss = (col: string | number | ReadonlyColor | IColor) => - isString(col) - ? col - : isArrayLike(col) - ? isString((col).mode) - ? css(col) - : srgbCss(col) - : int32Css(col); diff --git a/packages/color/src/defcolor.ts b/packages/color/src/defcolor.ts index f750fa2282..eae3675e5f 100644 --- a/packages/color/src/defcolor.ts +++ b/packages/color/src/defcolor.ts @@ -1,4 +1,4 @@ -import type { FloatArray, IDeref } from "@thi.ng/api"; +import type { FloatArray } from "@thi.ng/api"; import { implementsFunction, isArrayLike, @@ -11,9 +11,9 @@ import type { IRandom } from "@thi.ng/random"; import { declareIndices, eqDelta, - IVector, mapStridedBuffer, randMinMax, + set, stridedValues, } from "@thi.ng/vectors"; import type { @@ -21,13 +21,14 @@ import type { ColorFactory, ColorMode, ColorSpec, - IColor, + MaybeColor, ReadonlyColor, + TypedColor, } from "./api"; import { CONVERSIONS, convert } from "./convert"; +import { parseCss } from "./css/parse-css"; import { int32Rgb } from "./int/int-rgba"; import { ensureArgs } from "./internal/ensure-args"; -import { parseCss } from "./css/parse-css"; type $DefColor = { [k in K]: number; @@ -36,9 +37,7 @@ type $DefColor = { random(rnd?: IRandom): $DefColor; set(src: ReadonlyColor): $DefColor; toJSON(): number[]; -} & IColor & - IDeref & - IVector<$DefColor>; +} & TypedColor<$DefColor>; export const defColor = ( spec: ColorSpec @@ -50,8 +49,7 @@ export const defColor = ( ); const max = spec.order.map((id) => (spec.channels[id].range || [0, 1])[1]); - const $clazz = class - implements IColor, IVector<$DefColor>, IDeref { + const $clazz = class implements TypedColor<$DefColor> { buf: Color; offset: number; stride: number; @@ -92,15 +90,12 @@ export const defColor = ( return new $clazz(); } - deref(): Color { + deref() { return [this[0], this[1], this[2], this[3]]; } set(src: ReadonlyColor) { - this[0] = src[0]; - this[1] = src[1]; - this[2] = src[2]; - this[3] = src[3]; + set(this, src); return this; } @@ -112,9 +107,8 @@ export const defColor = ( return this.deref(); } - random(rnd?: IRandom) { - randMinMax(this, min, max, rnd); - return this; + randomize(rnd?: IRandom): this { + return randMinMax(this, min, max, rnd); } }; declareIndices($clazz.prototype, channels); @@ -129,10 +123,7 @@ export const defColor = ( return res; }; - const factory = ( - src?: string | number | ReadonlyColor | IColor, - ...xs: any[] - ): $DefColor => { + const factory = (src?: MaybeColor, ...xs: any[]): $DefColor => { if (src == null) return new $clazz(); if (isString(src)) { return factory(parseCss(src), ...xs); @@ -152,9 +143,7 @@ export const defColor = ( ) { return new $clazz(...ensureArgs([src, ...xs])); } - // FIXME disallow (since wrong for anything but rgb) - // or enforce mandatory `from` int conversion - return factory(int32Rgb([], src), ...xs); + return fromColor(int32Rgb([], src), "rgb", xs); } illegalArgs(`can't create a ${spec.mode} color from: ${src}`); }; @@ -164,11 +153,7 @@ export const defColor = ( buf?: Color, idx?: number, stride?: number - ) => { - const res = new $clazz(buf, idx, stride); - randMinMax(res, min, max, rnd); - return res; - }; + ) => new $clazz(buf, idx, stride).randomize(rnd); factory.mapBuffer = ( buf: FloatArray, diff --git a/packages/color/src/hcy/hcy.ts b/packages/color/src/hcy/hcy.ts index 15d0f39c45..bf90a76765 100644 --- a/packages/color/src/hcy/hcy.ts +++ b/packages/color/src/hcy/hcy.ts @@ -21,7 +21,7 @@ export declare class HCY implements TypedColor { deref(): Color; empty(): HCY; eqDelta(o: HCY, eps?: number): boolean; - random(rnd?: IRandom): this; + randomize(rnd?: IRandom): this; set(src: ReadonlyColor): this; toJSON(): number[]; } diff --git a/packages/color/src/hsi/hsi.ts b/packages/color/src/hsi/hsi.ts index 2b66982097..99507ba368 100644 --- a/packages/color/src/hsi/hsi.ts +++ b/packages/color/src/hsi/hsi.ts @@ -21,7 +21,7 @@ export declare class HSI implements TypedColor { deref(): Color; empty(): HSI; eqDelta(o: HSI, eps?: number): boolean; - random(rnd?: IRandom): this; + randomize(rnd?: IRandom): this; set(src: ReadonlyColor): this; toJSON(): number[]; } diff --git a/packages/color/src/hsl/hsl.ts b/packages/color/src/hsl/hsl.ts index 2418efee3a..7d338afbea 100644 --- a/packages/color/src/hsl/hsl.ts +++ b/packages/color/src/hsl/hsl.ts @@ -53,7 +53,7 @@ export declare class HSL implements TypedColor { deref(): Color; empty(): HSL; eqDelta(o: HSL, eps?: number): boolean; - random(rnd?: IRandom): this; + randomize(rnd?: IRandom): this; set(src: ReadonlyColor): this; toJSON(): number[]; } diff --git a/packages/color/src/hsv/hsv.ts b/packages/color/src/hsv/hsv.ts index c1fff47021..a09f370753 100644 --- a/packages/color/src/hsv/hsv.ts +++ b/packages/color/src/hsv/hsv.ts @@ -53,7 +53,7 @@ export declare class HSV implements TypedColor { deref(): Color; empty(): HSV; eqDelta(o: HSV, eps?: number): boolean; - random(rnd?: IRandom): this; + randomize(rnd?: IRandom): this; set(src: ReadonlyColor): this; toJSON(): number[]; } diff --git a/packages/color/src/index.ts b/packages/color/src/index.ts index 04a677e1c5..6e8db11891 100644 --- a/packages/color/src/index.ts +++ b/packages/color/src/index.ts @@ -9,7 +9,6 @@ export * from "./defcolor"; export * from "./css/css"; export * from "./css/parse-css"; -export * from "./css/resolve"; export * from "./hcy/hcy-rgb"; export * from "./hcy/hcy"; diff --git a/packages/color/src/lab/lab.ts b/packages/color/src/lab/lab.ts index 68c300665a..1536136ff1 100644 --- a/packages/color/src/lab/lab.ts +++ b/packages/color/src/lab/lab.ts @@ -23,7 +23,7 @@ export declare class Lab implements TypedColor { deref(): Color; empty(): Lab; eqDelta(o: Lab, eps?: number): boolean; - random(rnd?: IRandom): this; + randomize(rnd?: IRandom): this; set(src: ReadonlyColor): this; toJSON(): number[]; } diff --git a/packages/color/src/lch/lch.ts b/packages/color/src/lch/lch.ts index 5ec4c8f33f..d07726c072 100644 --- a/packages/color/src/lch/lch.ts +++ b/packages/color/src/lch/lch.ts @@ -22,7 +22,7 @@ export declare class LCH implements TypedColor { deref(): Color; empty(): LCH; eqDelta(o: LCH, eps?: number): boolean; - random(rnd?: IRandom): this; + randomize(rnd?: IRandom): this; set(src: ReadonlyColor): this; toJSON(): number[]; } diff --git a/packages/color/src/oklab/oklab.ts b/packages/color/src/oklab/oklab.ts index e363248e13..7087aefc07 100644 --- a/packages/color/src/oklab/oklab.ts +++ b/packages/color/src/oklab/oklab.ts @@ -22,7 +22,7 @@ export declare class Oklab implements TypedColor { deref(): Color; empty(): Oklab; eqDelta(o: Oklab, eps?: number): boolean; - random(rnd?: IRandom): this; + randomize(rnd?: IRandom): this; set(src: ReadonlyColor): this; toJSON(): number[]; } diff --git a/packages/color/src/ops/luminance.ts b/packages/color/src/ops/luminance.ts index fbef87d1a8..bd0f9e1e0b 100644 --- a/packages/color/src/ops/luminance.ts +++ b/packages/color/src/ops/luminance.ts @@ -1,7 +1,7 @@ import { DEFAULT, defmulti, MultiFn1 } from "@thi.ng/defmulti"; -import type { IColor, ReadonlyColor } from "../api"; -import { luminanceRgb, luminanceSrgb } from "./luminance-rgb"; +import type { MaybeColor } from "../api"; import { rgb } from "../rgb/rgb"; +import { luminanceRgb, luminanceSrgb } from "./luminance-rgb"; /** * Multi-method to compute relative luminance from any supported input color @@ -13,10 +13,9 @@ import { rgb } from "../rgb/rgb"; * unless a direct implementation is available, colors will first be converted * to linear RGB. */ -export const luminance: MultiFn1< - number | string | ReadonlyColor | IColor, - number -> = defmulti((col: any) => col.mode); +export const luminance: MultiFn1 = defmulti( + (col: any) => col.mode +); luminance.addAll({ hcy: (x: any) => x[2], diff --git a/packages/color/src/rgb/rgb.ts b/packages/color/src/rgb/rgb.ts index 32f57fcb18..f2902fea04 100644 --- a/packages/color/src/rgb/rgb.ts +++ b/packages/color/src/rgb/rgb.ts @@ -29,7 +29,7 @@ export declare class RGB implements TypedColor { deref(): Color; empty(): RGB; eqDelta(o: RGB, eps?: number): boolean; - random(rnd?: IRandom): this; + randomize(rnd?: IRandom): this; set(src: ReadonlyColor): this; toJSON(): number[]; } diff --git a/packages/color/src/srgb/srgb.ts b/packages/color/src/srgb/srgb.ts index a954d18916..bed461af1d 100644 --- a/packages/color/src/srgb/srgb.ts +++ b/packages/color/src/srgb/srgb.ts @@ -21,7 +21,7 @@ export declare class SRGB implements TypedColor { deref(): Color; empty(): SRGB; eqDelta(o: SRGB, eps?: number): boolean; - random(rnd?: IRandom): this; + randomize(rnd?: IRandom): this; set(src: ReadonlyColor): this; toJSON(): number[]; } diff --git a/packages/color/src/xyy/xyy.ts b/packages/color/src/xyy/xyy.ts index 3514c16b3a..beb2175078 100644 --- a/packages/color/src/xyy/xyy.ts +++ b/packages/color/src/xyy/xyy.ts @@ -22,7 +22,7 @@ export declare class XYY implements TypedColor { deref(): Color; empty(): XYY; eqDelta(o: XYY, eps?: number): boolean; - random(rnd?: IRandom): this; + randomize(rnd?: IRandom): this; set(src: ReadonlyColor): this; toJSON(): number[]; } diff --git a/packages/color/src/xyz/xyz.ts b/packages/color/src/xyz/xyz.ts index eb404b1b07..a42c894b5e 100644 --- a/packages/color/src/xyz/xyz.ts +++ b/packages/color/src/xyz/xyz.ts @@ -25,7 +25,7 @@ export declare class XYZ implements TypedColor { deref(): Color; empty(): XYZ; eqDelta(o: XYZ, eps?: number): boolean; - random(rnd?: IRandom): this; + randomize(rnd?: IRandom): this; set(src: ReadonlyColor): this; toJSON(): number[]; } diff --git a/packages/color/src/ycc/ycc.ts b/packages/color/src/ycc/ycc.ts index be05d78f60..1485da24f1 100644 --- a/packages/color/src/ycc/ycc.ts +++ b/packages/color/src/ycc/ycc.ts @@ -21,7 +21,7 @@ export declare class YCC implements TypedColor { deref(): Color; empty(): YCC; eqDelta(o: YCC, eps?: number): boolean; - random(rnd?: IRandom): this; + randomize(rnd?: IRandom): this; set(src: ReadonlyColor): this; toJSON(): number[]; }