Skip to content

Commit

Permalink
feat(color): add oklch mode impl/support
Browse files Browse the repository at this point in the history
- add oklch() factory & class decl
- add oklab<>oklch conversions
- update analog() & rotate()
  • Loading branch information
postspectacular committed Mar 1, 2023
1 parent 733c3b4 commit 3e77420
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 2 deletions.
2 changes: 1 addition & 1 deletion packages/color/src/analog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const analog = defmulti<
Color
>(
__dispatch1,
{},
{ oklab: "lab50", oklch: "lch" },
{
hcy: analogHNN,
hsi: analogHNN,
Expand Down
1 change: 1 addition & 0 deletions packages/color/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export type ColorMode =
| "lab65"
| "lch"
| "oklab"
| "oklch"
| "rgb"
| "srgb"
| "xyy"
Expand Down
5 changes: 5 additions & 0 deletions packages/color/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ export * from "./oklab/oklab-rgb.js";
export * from "./oklab/oklab-xyz.js";
export * from "./oklab/oklab.js";

export * from "./oklch/oklch-css.js";
export * from "./oklch/oklch-oklab.js";
export * from "./oklch/oklab-oklch.js";
export * from "./oklch/oklch.js";

export * from "./alpha.js";
export * from "./analog.js";
export * from "./clamp.js";
Expand Down
25 changes: 25 additions & 0 deletions packages/color/src/oklch/oklab-oklch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { atan2Abs } from "@thi.ng/math/angle";
import { INV_TAU } from "@thi.ng/math/api";
import { setC4 } from "@thi.ng/vectors/setc";
import type { ColorOp } from "../api.js";
import { __ensureAlpha } from "../internal/ensure.js";

export const oklabOklch: ColorOp = (out, src) =>
setC4(
out || src,
src[0],
Math.hypot(src[1], src[2]),
atan2Abs(src[2], src[1]) * INV_TAU,
__ensureAlpha(src[3])
);

/*
export function OKLab_to_OKLCH(OKLab: Color): Color {
const hue = Math.atan2(OKLab[2], OKLab[1]) * 180 / Math.PI;
return [
OKLab[0], // L is still L
Math.sqrt(OKLab[1] ** 2 + OKLab[2] ** 2), // Chroma
hue >= 0 ? hue : hue + 360, // Hue, in degrees [0 to 360)
];
}
*/
12 changes: 12 additions & 0 deletions packages/color/src/oklch/oklch-css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { ReadonlyColor } from "../api.js";
import { lchCss } from "../lch/lch-css.js";

/**
* @remarks
* Only supported in CSS Color Level 4 onwards
* - https://www.w3.org/TR/css-color-4/#specifying-oklab-oklch
* - https://test.csswg.org/harness/results/css-color-4_dev/grouped/ (test reports)
*
* @param src -
*/
export const oklchCss = (src: ReadonlyColor) => "ok" + lchCss(src);
21 changes: 21 additions & 0 deletions packages/color/src/oklch/oklch-oklab.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { cossin } from "@thi.ng/math/angle";
import { TAU } from "@thi.ng/math/api";
import { setC4 } from "@thi.ng/vectors/setc";
import type { ColorOp } from "../api.js";
import { __ensureAlpha } from "../internal/ensure.js";

/**
* @remarks
* Reference:
* - https://github.com/w3c/csswg-drafts/blob/main/css-color-4/conversions.js
*
* @param out
* @param src
*/
export const oklchOklab: ColorOp = (out, src) =>
setC4(
out || src,
src[0],
...cossin(src[2] * TAU, src[1]),
__ensureAlpha(src[3])
);
51 changes: 51 additions & 0 deletions packages/color/src/oklch/oklch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { NumericArray } from "@thi.ng/api";
import type { IRandom } from "@thi.ng/random";
import type { Color, ColorFactory, ReadonlyColor, TypedColor } from "../api.js";
import { defColor } from "../defcolor.js";
import { oklabRgb } from "../oklab/oklab-rgb.js";
import { rgbOklab } from "../rgb/rgb-oklab.js";
import { oklabOklch } from "./oklab-oklch.js";
import { oklchOklab } from "./oklch-oklab.js";

export declare class Oklch implements TypedColor<Oklch> {
buf: NumericArray;
offset: number;
stride: number;
l: number;
c: number;
h: number;
alpha: number;
[id: number]: number;
readonly mode: "oklch";
readonly length: 4;
readonly range: [ReadonlyColor, ReadonlyColor];
[Symbol.iterator](): Iterator<number, any, undefined>;
clamp(): this;
copy(): Oklch;
copyView(): Oklch;
deref(): Color;
empty(): Oklch;
eqDelta(o: Oklch, eps?: number): boolean;
randomize(rnd?: IRandom): this;
set(src: ReadonlyColor): this;
toJSON(): number[];
}

/**
* Oklch color type aka polar version of {@link oklab}.
*
* @remarks
* Note: As with other hue-based color modes in this package, the hue is stored
* normalized (in [0..1] interval) and NOT as degrees.
*
* Reference: https://bottosson.github.io/posts/oklab/
*/
export const oklch = <ColorFactory<Oklch>>defColor({
mode: "oklch",
order: <const>["l", "c", "h", "alpha"],
from: {
oklab: oklabOklch,
rgb: (out, src) => oklabOklch(null, rgbOklab(out, src)),
},
toRgb: [oklchOklab, oklabRgb],
});
2 changes: 1 addition & 1 deletion packages/color/src/rotate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { __ensureAlpha } from "./internal/ensure.js";
*/
export const rotate = defmulti<Color | null, TypedColor<any>, number, Color>(
__dispatch1,
{ hsv: "hsl", hsi: "hsl", hcy: "hsl" },
{ hcy: "hsl", hsi: "hsl", hsv: "hsl", oklch: "lch" },
{
hsl: (out, src, theta) =>
setC4(
Expand Down

0 comments on commit 3e77420

Please sign in to comment.