Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added Hue XY Conversions #155

Merged
merged 4 commits into from
Sep 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 50 additions & 25 deletions packages/color-convert/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,30 @@ validHex("#8c0dba") //=> true
rgbaToHexa({ b: 26, g: 2, r: 209, a: 1 }) // => '#d1021aff'
```

### `hexToXY`

```js
hexToXY('#4780f1') // => { x: 0.261, y: 0.231, bri: 0.863 }
```

### `xyToHex`

```js
xyToHex({ x: 0.261, y: 0.231, bri: 0.863 }) // => #4780f1
```

### `rgbToXY`

```js
rgbToXY({ r: 71, g: 128, b: 241 }) // => { x: 0.261, y: 0.231, bri: 0.863 }
```

### `xyToRgb`

```js
xyToRgb({ x: 0.261, y: 0.231, bri: 0.863 }) // => { r: 71, g: 128, b: 241 }
```

#### `color`

```js
Expand All @@ -221,37 +245,32 @@ const { rgb, rgba, hsl, hsv, hsla, hsva } = color('#d1021a');
## type

```ts
export declare const equalColorObjects: (first: ObjectColor, second: ObjectColor) => boolean;
export declare const equalColorString: (first: string, second: string) => boolean;
export declare const equalHex: (first: string, second: string) => boolean;
export declare const validHex: (hex: string) => boolean;
export declare const getContrastingColor: (str: string | HsvaColor) => "#ffffff" | "#000000";
export type ObjectColor = RgbColor | HslColor | HsvColor | RgbaColor | HslaColor | HsvaColor;
export type ColorResult = {
rgb: RgbColor;
hsl: HslColor;
hsv: HsvColor;
rgba: RgbaColor;
hsla: HslaColor;
hsva: HsvaColor;
hex: string;
hexa: string;
rgb: RgbColor;
hsl: HslColor;
hsv: HsvColor;
rgba: RgbaColor;
hsla: HslaColor;
hsva: HsvaColor;
hex: string;
hexa: string;
};
export interface HsvColor {
h: number;
s: number;
v: number;
h: number;
s: number;
v: number;
}
export interface HsvaColor extends HsvColor {
a: number;
a: number;
}
export interface RgbColor {
r: number;
g: number;
b: number;
r: number;
g: number;
b: number;
}
export interface RgbaColor extends RgbColor {
a: number;
a: number;
}
/**
* ```js
Expand All @@ -268,12 +287,12 @@ export declare const hslaStringToHsva: (hslString: string) => HsvaColor;
export declare const hslStringToHsva: (hslString: string) => HsvaColor;
export declare const hslaToHsva: ({ h, s, l, a }: HslaColor) => HsvaColor;
export interface HslColor {
h: number;
s: number;
l: number;
h: number;
s: number;
l: number;
}
export interface HslaColor extends HslColor {
a: number;
a: number;
}
export declare const hsvaToHsla: ({ h, s, v, a }: HsvaColor) => HslaColor;
export declare const hsvaStringToHsva: (hsvString: string) => HsvaColor;
Expand All @@ -284,6 +303,7 @@ export declare const rgbStringToHsva: (rgbaString: string) => HsvaColor;
/** Converts an RGBA color plus alpha transparency to hex */
export declare const rgbaToHex: ({ r, g, b }: RgbaColor) => string;
export declare const rgbaToHexa: ({ r, g, b, a }: RgbaColor) => string;
export type HexColor = `#${string}`;
export declare const hexToHsva: (hex: string) => HsvaColor;
export declare const hexToRgba: (hex: string) => RgbaColor;
/**
Expand All @@ -299,6 +319,11 @@ export declare const hsvaToHex: (hsva: HsvaColor) => string;
export declare const hsvaToHexa: (hsva: HsvaColor) => string;
export declare const hsvaToHsv: ({ h, s, v }: HsvaColor) => HsvColor;
export declare const color: (str: string | HsvaColor) => ColorResult;
export declare const getContrastingColor: (str: string | HsvaColor) => "#ffffff" | "#000000";
export declare const equalColorObjects: (first: ObjectColor, second: ObjectColor) => boolean;
export declare const equalColorString: (first: string, second: string) => boolean;
export declare const equalHex: (first: string, second: string) => boolean;
export declare const validHex: (hex: string) => hex is HexColor;
```

## Contributors
Expand Down
58 changes: 57 additions & 1 deletion packages/color-convert/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type ColorResult = {
rgba: RgbaColor;
hsla: HslaColor;
hsva: HsvaColor;
xy: XYColor;
hex: string;
hexa: string;
};
Expand All @@ -33,6 +34,12 @@ export interface RgbaColor extends RgbColor {
a: number;
}

export interface XYColor {
x: number;
y: number;
bri?: number;
}

/**
* ```js
* rgbaToHsva({ r: 255, g: 255, b: 255, a: 1 }) //=> { h: 0, s: 0, v: 100, a: 1 }
Expand Down Expand Up @@ -273,6 +280,53 @@ export const hslaToHsl = ({ h, s, l }: HslaColor): HslColor => ({ h, s, l });
export const hsvaToHex = (hsva: HsvaColor): string => rgbaToHex(hsvaToRgba(hsva));
export const hsvaToHexa = (hsva: HsvaColor): string => rgbaToHexa(hsvaToRgba(hsva));
export const hsvaToHsv = ({ h, s, v }: HsvaColor): HsvColor => ({ h, s, v });
export const hexToXY = (hex: string): XYColor => rgbToXY(rgbaToRgb(hexToRgba(hex)));
export const xyToHex = (xy: XYColor): string =>
rgbaToHex({
...xyToRgb(xy),
a: 255,
});

/**
* Converts XY to RGB. Based on formula from https://developers.meethue.com/develop/application-design-guidance/color-conversion-formulas-rgb-to-xy-and-back/
* @param color XY color and brightness as an array [0-1, 0-1, 0-1]
*/
export const xyToRgb = ({ x, y, bri = 255 }: XYColor): RgbColor => {
const red = x * 3.2406255 + y * -1.537208 + bri * -0.4986286;
const green = x * -0.9689307 + y * 1.8757561 + bri * 0.0415175;
const blue = x * 0.0557101 + y * -0.2040211 + bri * 1.0569959;

const translate = function (color: number) {
return color <= 0.0031308 ? 12.92 * color : 1.055 * Math.pow(color, 1 / 2.4) - 0.055;
};

return {
r: Math.round(255 * translate(red)),
g: Math.round(255 * translate(green)),
b: Math.round(255 * translate(blue)),
};
};

/**
* Converts RGB to XY. Based on formula from https://developers.meethue.com/develop/application-design-guidance/color-conversion-formulas-rgb-to-xy-and-back/
* @param color RGB color as an array [0-255, 0-255, 0-255]
*/
export const rgbToXY = ({ r, g, b }: RgbColor): XYColor => {
const translateColor = function (color: number) {
return color <= 0.04045 ? color / 12.92 : Math.pow((color + 0.055) / 1.055, 2.4);
};

const red = translateColor(r / 255);
const green = translateColor(g / 255);
const blud = translateColor(b / 255);

const xyz = {} as XYColor;
xyz.x = red * 0.4124 + green * 0.3576 + blud * 0.1805;
xyz.y = red * 0.2126 + green * 0.7152 + blud * 0.0722;
xyz.bri = red * 0.0193 + green * 0.1192 + blud * 0.9505;

return xyz;
};

export const color = (str: string | HsvaColor): ColorResult => {
let rgb!: RgbColor;
Expand All @@ -281,6 +335,7 @@ export const color = (str: string | HsvaColor): ColorResult => {
let rgba!: RgbaColor;
let hsla!: HslaColor;
let hsva!: HsvaColor;
let xy!: XYColor;
let hex!: string;
let hexa!: string;
if (typeof str === 'string' && validHex(str)) {
Expand All @@ -297,8 +352,9 @@ export const color = (str: string | HsvaColor): ColorResult => {
hex = hsvaToHex(hsva);
hsl = hslaToHsl(hsla);
rgb = rgbaToRgb(rgba);
xy = rgbToXY(rgb);
}
return { rgb, hsl, hsv, rgba, hsla, hsva, hex, hexa };
return { rgb, hsl, hsv, rgba, hsla, hsva, hex, hexa, xy };
};

export const getContrastingColor = (str: string | HsvaColor) => {
Expand Down
5 changes: 5 additions & 0 deletions test/circle.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ it('Circle colors checked', async () => {
v: 95.68627450980392,
a: 1,
},
xy: {
bri: 0.06811140344707436,
x: 0.40822033351750947,
y: 0.24997641962334327,
},
hex: '#f44e3b',
hexa: '#f44e3bff',
});
Expand Down
41 changes: 38 additions & 3 deletions test/convert.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { color, getContrastingColor } from '../packages/color-convert/src';
// HEX
import { hexToHsva, hexToRgba, hsvaToHex, hsvaToHexa } from '../packages/color-convert/src';
import { hexToHsva, hexToRgba, hsvaToHex, hsvaToHexa, hexToXY } from '../packages/color-convert/src';
import { equalHex } from '../packages/color-convert/src';
// HSLA
import { hsvaToHsla, hslaToHsva, HsvaColor, HslaColor } from '../packages/color-convert/src';
Expand All @@ -15,7 +15,7 @@ import { hsvaToRgba, rgbaToHsva, RgbaColor } from '../packages/color-convert/src
// RGBA string
import { hsvaToRgbaString, rgbaStringToHsva } from '../packages/color-convert/src';
// RGB
import { rgbaToRgb, rgbaToHex, rgbaToHexa } from '../packages/color-convert/src';
import { rgbaToRgb, rgbaToHex, rgbaToHexa, rgbToXY } from '../packages/color-convert/src';
// RGB string
import { hsvaToRgbString, rgbStringToHsva } from '../packages/color-convert/src';
// HSVA String
Expand All @@ -25,6 +25,8 @@ import { hsvaToHsv } from '../packages/color-convert/src';
// HSV string
import { hsvaToHsvString, hsvStringToHsva } from '../packages/color-convert/src';
import { equalColorString, equalColorObjects, validHex } from '../packages/color-convert/src';
// XY
import { xyToHex, xyToRgb } from '../packages/color-convert/src';

it('Converts color => getContrastingColor', () => {
expect(getContrastingColor('#d0021b')).toEqual('#ffffff');
Expand All @@ -41,7 +43,7 @@ it('Converts color => hslString To Hsl', () => {
});

it('Converts color => HEX to ColorResult', () => {
const { rgb, rgba, hex, hexa, hsl, hsla, hsv, hsva } = color('#d1021a');
const { rgb, rgba, hex, hexa, hsl, hsla, hsv, hsva, xy } = color('#d1021a');
expect(hex).toEqual('#d1021a');
expect(hexa).toEqual('#d1021aff');
expect(color({ h: 353.04347826086956, s: 99.04306220095694, v: 81.96078431372548, a: 1 }).hex).toEqual('#d1021a');
Expand All @@ -52,6 +54,7 @@ it('Converts color => HEX to ColorResult', () => {
expect(hsla).toEqual({ h: 353.04347826086956, l: 41.37254901960784, s: 98.10426540284361, a: 1 });
expect(hsv).toEqual({ h: 353.04347826086956, s: 99.04306220095694, v: 81.96078431372548 });
expect(hsva).toEqual({ h: 353.04347826086956, s: 99.04306220095694, v: 81.96078431372548, a: 1 });
expect(xy).toEqual({ x: 0.26502656639062083, y: 0.13673307363113865, bri: 0.022196477290623278 });
});

it('Converts color => HEXA to ColorResult', () => {
Expand Down Expand Up @@ -95,6 +98,16 @@ it('Converts RGBA to HEXA', () => {
expect(rgbaToHexa({ b: 26, g: 2, r: 209 } as any)).toEqual('#d1021a');
});

it('Converts RGB to XY', () => {
expect(rgbToXY({ r: 255, g: 255, b: 255 })).toMatchObject({ x: 0.9505, y: 1, bri: 1.089 });
expect(rgbToXY({ r: 0, g: 0, b: 0 })).toMatchObject({ x: 0.0, y: 0.0, bri: 0 });
expect(rgbToXY({ r: 71, g: 128, b: 241 })).toMatchObject({
x: 0.26194888875915034,
y: 0.23128809648982562,
bri: 0.863027753196167,
});
});

it('Converts HEX to RGBA', () => {
expect(hsvaToHslString(hexToHsva('#d0021b'))).toEqual('hsl(352.71844660194176, 98%, 41%)');
expect(hsvaToHex(rgbaToHsva(hexToRgba('#d0021b')))).toEqual('#d0021b');
Expand All @@ -115,6 +128,12 @@ it('Converts HEX to HSVA', () => {
expect(hexToHsva('#c62182')).toMatchObject({ h: 324.72727272727275, s: 83.33333333333334, v: 77.64705882352942, a: 1 });
});

it('Converts HEX to XY', () => {
expect(hexToXY('#ffffff')).toMatchObject({ x: 0.9505, y: 1, bri: 1.089 });
expect(hexToXY('#000000')).toMatchObject({ x: 0.0, y: 0.0, bri: 0 });
expect(hexToXY('#4780f1')).toMatchObject({ x: 0.26194888875915034, y: 0.23128809648982562, bri: 0.863027753196167 });
});

it('Converts shorthand HEX to HSVA', () => {
expect(hexToHsva('#FFF')).toMatchObject({ h: 0, s: 0, v: 100, a: 1 });
expect(hexToHsva('#FF0')).toMatchObject({ h: 60, s: 100, v: 100, a: 1 });
Expand Down Expand Up @@ -409,3 +428,19 @@ it('Validates HEX colors', () => {
// @ts-ignore
expect(validHex()).toBe(false);
});

it('Converts XY to RGB', () => {
expect(xyToRgb({ x: 0.9505, y: 1, bri: 1.089 })).toMatchObject({ r: 255, g: 255, b: 255 });
expect(xyToRgb({ x: 0.0, y: 0.0, bri: 0 })).toMatchObject({ r: 0, g: 0, b: 0 });
expect(xyToRgb({ x: 0.26194888875915034, y: 0.23128809648982562, bri: 0.863027753196167 })).toMatchObject({
r: 71,
g: 128,
b: 241,
});
});

it('Converts XY to HEX', () => {
expect(xyToHex({ x: 0.9505, y: 1, bri: 1.089 })).toBe('#ffffff');
expect(xyToHex({ x: 0.0, y: 0.0, bri: 0 })).toBe('#000000');
expect(xyToHex({ x: 0.26194888875915034, y: 0.23128809648982562, bri: 0.863027753196167 })).toBe('#4780f1');
});
Loading