toosoon-utils 1.4.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +27 -23
  2. package/package.json +8 -4
  3. package/tsconfig.json +1 -3
  4. package/lib/classes/_pool.d.ts +0 -56
  5. package/lib/classes/_pool.js +0 -92
  6. package/lib/classes/color-scale.d.ts +0 -52
  7. package/lib/classes/color-scale.js +0 -160
  8. package/lib/classes/frame-rate.d.ts +0 -25
  9. package/lib/classes/frame-rate.js +0 -48
  10. package/lib/colors.d.ts +0 -155
  11. package/lib/colors.js +0 -367
  12. package/lib/constants.d.ts +0 -162
  13. package/lib/constants.js +0 -170
  14. package/lib/dom.d.ts +0 -25
  15. package/lib/dom.js +0 -47
  16. package/lib/files.d.ts +0 -14
  17. package/lib/files.js +0 -38
  18. package/lib/functions.d.ts +0 -22
  19. package/lib/functions.js +0 -53
  20. package/lib/geometry.d.ts +0 -89
  21. package/lib/geometry.js +0 -128
  22. package/lib/index.d.ts +0 -10
  23. package/lib/index.js +0 -39
  24. package/lib/maths.d.ts +0 -161
  25. package/lib/maths.js +0 -219
  26. package/lib/now.d.ts +0 -5
  27. package/lib/now.js +0 -28
  28. package/lib/prng.d.ts +0 -124
  29. package/lib/prng.js +0 -234
  30. package/lib/random.d.ts +0 -91
  31. package/lib/random.js +0 -162
  32. package/lib/strings.d.ts +0 -14
  33. package/lib/strings.js +0 -18
  34. package/lib/tsconfig.tsbuildinfo +0 -1
  35. package/lib/types.d.ts +0 -18
  36. package/lib/types.js +0 -1
  37. package/src/classes/_pool.ts +0 -92
  38. package/src/classes/color-scale.ts +0 -181
  39. package/src/classes/frame-rate.ts +0 -49
  40. package/src/colors.ts +0 -389
  41. package/src/constants.ts +0 -172
  42. package/src/dom.ts +0 -50
  43. package/src/files.ts +0 -42
  44. package/src/functions.ts +0 -56
  45. package/src/geometry.ts +0 -160
  46. package/src/maths.ts +0 -241
  47. package/src/prng.ts +0 -250
  48. package/src/random.ts +0 -162
  49. package/src/strings.ts +0 -19
  50. package/src/types.ts +0 -33
@@ -1,92 +0,0 @@
1
- // *********************
2
- // WIP
3
- // *********************
4
-
5
- export type PoolSettings = {
6
- max?: number;
7
- };
8
-
9
- export const defaultSettings: Required<PoolSettings> = {
10
- max: Infinity
11
- };
12
-
13
- interface PoolItem {
14
- setup?: () => void;
15
- reset?: () => void;
16
- dispose?: () => void;
17
- }
18
-
19
- /**
20
- * Abstract class for manipulating pool items
21
- *
22
- * @exports
23
- * @class Pool
24
- */
25
- export default abstract class Pool<I extends PoolItem> {
26
- public items: I[] = [];
27
- public pool: I[] = [];
28
-
29
- settings: Required<PoolSettings> = { ...defaultSettings };
30
-
31
- constructor(settings: PoolSettings = { ...defaultSettings }) {
32
- this.settings = Object.assign(this.settings, settings);
33
- }
34
-
35
- /**
36
- * Abstract method to implement custom item creation
37
- *
38
- * @returns {PoolItem}
39
- */
40
- protected abstract create(): I;
41
-
42
- /**
43
- * Add an item to the active items
44
- *
45
- * @param {PoolItem} item Item to add to the active items
46
- */
47
- protected add(item: I): void {
48
- this.items.push(item);
49
- }
50
-
51
- /**
52
- * Remove an item from the active items
53
- *
54
- * @param {PoolItem} item Item to remove from the active items
55
- */
56
- protected remove(item: I): void {
57
- this.items = this.items.filter((_item) => _item !== item);
58
- }
59
-
60
- /**
61
- * Return an item from pool or create a new one
62
- *
63
- * @returns {PoolItem|undefined}
64
- */
65
- public get(): I | undefined {
66
- if (this.items.length >= this.settings.max) return;
67
- const item = this.pool.pop() ?? this.create();
68
- item.setup?.();
69
- this.add(item);
70
- return item;
71
- }
72
-
73
- /**
74
- * Release an item from the active items and add it to the pool
75
- *
76
- * @param {PoolItem} item Item to release
77
- */
78
- public release(item: I): void {
79
- this.pool.push(item);
80
- item.reset?.();
81
- this.remove(item);
82
- }
83
-
84
- /**
85
- * Dispose all items
86
- */
87
- public dispose(): void {
88
- [...this.items, ...this.pool].forEach((item: I) => item.dispose?.());
89
- this.items = [];
90
- this.pool = [];
91
- }
92
- }
@@ -1,181 +0,0 @@
1
- import { lerp, triLerp } from '../maths';
2
- import { hclToRgb, hsbToRgb, hslToRgb, normalizeColor, rgbToHcl, rgbToHsb, rgbToHsl } from '../colors';
3
- import { ColorRepresentation } from '../types';
4
-
5
- export type ColorScaleSettings =
6
- | {
7
- colorSpace: 'rgb' | 'hsl' | 'hsb';
8
- }
9
- | {
10
- colorSpace: 'hcl';
11
- mode?: 'qualitative' | 'sequential' | 'diverging';
12
- triangular?: number;
13
- powerStrength?: number;
14
- hueOffset?: number;
15
- chromaOffset?: number;
16
- luminanceOffset?: number;
17
- };
18
-
19
- export const defaultSettings: Required<ColorScaleSettings> = {
20
- colorSpace: 'rgb'
21
- };
22
-
23
- /**
24
- * Utility class for generating color scales and interpolating between colors
25
- *
26
- * @exports
27
- * @class ColorScale
28
- */
29
- export default class ColorScale {
30
- /**
31
- * Array of colors composing the color scale
32
- */
33
- public colors: Array<[number, number, number]> = [];
34
-
35
- /**
36
- * @param {ColorRepresentation} input Input color representation
37
- * @param {ColorRepresentation} target Target color representation
38
- * @param {number} [length=5] Amount of colors composing the color scale
39
- * @param {ColorScaleSettings} [settings] Color scale generation settings
40
- */
41
- constructor(
42
- input: ColorRepresentation,
43
- target: ColorRepresentation,
44
- length: number = 5,
45
- settings: ColorScaleSettings = { ...defaultSettings }
46
- ) {
47
- this.colors = ColorScale.generate(input, target, length, settings);
48
- }
49
-
50
- /**
51
- * Static method for generating a color scale
52
- *
53
- * @param {ColorRepresentation} input Input color representation
54
- * @param {ColorRepresentation} target Target color representation
55
- * @param {number} length Amount of colors composing the color scale
56
- * @param {ColorScaleSettings} [settings] Color scale generation settings
57
- * @returns {Array<[number, number, number]>} Color scale colors
58
- */
59
- static generate(
60
- input: ColorRepresentation,
61
- target: ColorRepresentation,
62
- length: number,
63
- settings: ColorScaleSettings = { ...defaultSettings }
64
- ): Array<[number, number, number]> {
65
- const colors: Array<[number, number, number]> = [];
66
-
67
- const inputColor = normalizeColor(input);
68
- const targetColor = normalizeColor(target);
69
-
70
- for (let i = 0; i < length; i++) {
71
- const value = i / Math.floor(length);
72
- colors.push(ColorScale.interpolate(inputColor, targetColor, value, settings));
73
- }
74
-
75
- return colors;
76
- }
77
-
78
- /**
79
- * Static method for interpolating between colors
80
- *
81
- * @param {[number,number,number]} inputColor Input color
82
- * @param {[number,number,number]} targetColor Target color
83
- * @param {number} value Interpolation normalized value
84
- * @param {ColorScaleSettings} [settings] Color scale settings
85
- * @returns {[number,number,number]} Interpolated color
86
- */
87
- static interpolate(
88
- inputColor: [number, number, number],
89
- targetColor: [number, number, number],
90
- value: number,
91
- settings: ColorScaleSettings = { ...defaultSettings }
92
- ): [number, number, number] {
93
- switch (settings.colorSpace) {
94
- case 'rgb': {
95
- const r = lerp(value, inputColor[0], targetColor[0]);
96
- const g = lerp(value, inputColor[1], targetColor[1]);
97
- const b = lerp(value, inputColor[2], targetColor[2]);
98
- return [r, g, b];
99
- }
100
- case 'hsl': {
101
- const inputHsl = rgbToHsl(inputColor);
102
- const targetHsl = rgbToHsl(targetColor);
103
- const h = lerp(value, inputHsl[0], targetHsl[0]);
104
- const s = lerp(value, inputHsl[1], targetHsl[1]);
105
- const l = lerp(value, inputHsl[2], targetHsl[2]);
106
- return hslToRgb([h, s, l]);
107
- }
108
- case 'hsb': {
109
- const inputHsb = rgbToHsb(inputColor);
110
- const targetHsb = rgbToHsb(targetColor);
111
- const h = lerp(value, inputHsb[0], targetHsb[0]);
112
- const s = lerp(value, inputHsb[1], targetHsb[1]);
113
- const b = lerp(value, inputHsb[2], targetHsb[2]);
114
- return hsbToRgb([h, s, b]);
115
- }
116
- case 'hcl':
117
- const inputHcl = rgbToHcl(inputColor);
118
- const targetHcl = rgbToHcl(targetColor);
119
- const powerValue = Math.pow(value, settings.powerStrength ?? 1);
120
-
121
- const h1 = inputHcl[0];
122
- const c1 = inputHcl[1];
123
- const l1 = inputHcl[2];
124
- const h2 = targetHcl[0] + (settings.hueOffset ?? 0);
125
- const c2 = targetHcl[1] + (settings.chromaOffset ?? 0);
126
- const l2 = targetHcl[2] + (settings.luminanceOffset ?? 0);
127
-
128
- let h, c, l;
129
-
130
- // HCL color palettes
131
- // -> https://colorspace.r-forge.r-project.org/articles/hcl_palettes.html
132
- if (settings.mode === 'qualitative') {
133
- /**
134
- * Qualitative
135
- * Designed for coding categorical information,
136
- * where no particular ordering of categories is available
137
- * and every color should receive the same perceptual weight.
138
- *
139
- * - Hue: Linear
140
- * - Chroma: Constant
141
- * - Luminance: Constant
142
- */
143
- h = lerp(value, h1, h2);
144
- c = c1;
145
- l = l1;
146
- } else if (settings.mode === 'sequential') {
147
- /**
148
- * Sequential
149
- * Designed for coding ordered/numeric information,
150
- * going from high to low (or vice versa).
151
- *
152
- * - Hue: Constant | Linear
153
- * - Chroma: Linear (+power) | Triangular (+power)
154
- * - Luminance: Linear (+power)
155
- */
156
- h = lerp(value, h1, h2);
157
- c = settings.triangular ? triLerp(powerValue, c1, c2, settings.triangular) : lerp(powerValue, c1, c2);
158
- l = lerp(powerValue, l1, l2);
159
- } else if (settings.mode === 'diverging') {
160
- /**
161
- * Diverging
162
- * Designed for coding ordered/numeric information around a central neutral value,
163
- * where colors diverge from neutral to two extremes.
164
- *
165
- * - Hue: Constants (x2)
166
- * - Chroma: Linear (+power) | Triangular (+power)
167
- * - Luminance: Linear (+power)
168
- */
169
- h = value < 0.5 ? h1 : value > 0.5 ? h2 : lerp(0.5, h1, h2);
170
- c = settings.triangular ? triLerp(powerValue, c1, c2, settings.triangular) : lerp(powerValue, c1, c2);
171
- l = lerp(powerValue, l1, l2);
172
- } else {
173
- h = lerp(value, h1, h2);
174
- c = lerp(value, c1, c2);
175
- l = lerp(value, l1, l2);
176
- }
177
-
178
- return hclToRgb([h, c, l]);
179
- }
180
- }
181
- }
@@ -1,49 +0,0 @@
1
- import { now } from '../functions';
2
-
3
- /**
4
- * Utility class for controlling FPS calls
5
- *
6
- * @exports
7
- * @class FrameRate
8
- */
9
- export default class FrameRate {
10
- private _fps: number;
11
- private interval = 0;
12
- private time = 0;
13
- private elapsedTime = 0;
14
- private lastUpdate = 0;
15
-
16
- /**
17
- * @param {number} [fps=30] Frame per second limit
18
- */
19
- constructor(fps: number = 30) {
20
- this._fps = fps;
21
- this.fps = fps;
22
- }
23
-
24
- /**
25
- * Return true if elapsed time since last update is higher than current FPS
26
- *
27
- * @returns {boolean}
28
- */
29
- update(): boolean {
30
- this.time = now();
31
- this.elapsedTime = this.time - this.lastUpdate;
32
-
33
- if (this.elapsedTime < this.interval) {
34
- return false;
35
- }
36
-
37
- this.lastUpdate = this.time - (this.elapsedTime % this.interval);
38
- return true;
39
- }
40
-
41
- get fps(): number {
42
- return this._fps;
43
- }
44
-
45
- set fps(fps: number) {
46
- this._fps = fps;
47
- this.interval = 1000 / fps;
48
- }
49
- }
package/src/colors.ts DELETED
@@ -1,389 +0,0 @@
1
- import { W3CX11 } from './constants';
2
- import { toDegrees, toRadians } from './geometry';
3
- import { clamp } from './maths';
4
- import { ColorName, ColorRepresentation } from './types';
5
-
6
- /**
7
- * Normalize a color representation into RGB
8
- *
9
- * @param {ColorRepresentation} color Color representation
10
- * @returns {[number,number,number]} Normalized RGB color
11
- */
12
- export function normalizeColor(color: ColorRepresentation): [number, number, number] {
13
- if (typeof color === 'string') {
14
- return hexToRgb(W3CX11[color as ColorName] ?? color);
15
- } else if (typeof color === 'number') {
16
- return hexToRgb(color);
17
- } else {
18
- return color;
19
- }
20
- }
21
-
22
- // ******************************************
23
- // RGB & Hexadecimal color spaces
24
- // ******************************************
25
-
26
- /**
27
- * Normalize an hexadecimal string
28
- *
29
- * @param {string} hex Hexadecimal string
30
- * @returns {string} Normalized hexadecimal string
31
- */
32
- export function normalizeHexString(hex: string): string {
33
- let match: RegExpMatchArray | null;
34
- let result: string = '000000';
35
- hex = hex.toLocaleLowerCase();
36
-
37
- if ((match = hex.match(/(#|0x)?([a-f0-9]{6})/i))) {
38
- result = match[2];
39
- } else if ((match = hex.match(/^#?([a-f0-9])([a-f0-9])([a-f0-9])$/i))) {
40
- result = match[1] + match[1] + match[2] + match[2] + match[3] + match[3];
41
- } else if ((match = hex.match(/rgb\(\s*(\d*)\s*,\s*(\d*)\s*,\s*(\d*)\s*\)/))) {
42
- result =
43
- parseInt(match[1]).toString(16).padStart(2, '0') +
44
- parseInt(match[2]).toString(16).padStart(2, '0') +
45
- parseInt(match[3]).toString(16).padStart(2, '0');
46
- }
47
-
48
- return `#${result}`;
49
- }
50
-
51
- /**
52
- * Convert RGB to hexadecimal
53
- * Note: rgb values are contained in the interval [0, 1]
54
- *
55
- * @param {[number, number, number]} rgb RGB color
56
- * @returns {number} Hexadecimal color
57
- */
58
- export function rgbToHex([r, g, b]: [number, number, number]): number {
59
- return ((r * 255) << 16) ^ ((g * 255) << 8) ^ ((b * 255) << 0);
60
- }
61
-
62
- /**
63
- * Convert RGB to hexadecimal string
64
- * Note: rgb values are contained in the interval [0, 1]
65
- *
66
- * @param {[number, number, number]} rgb RGB color
67
- * @returns {string} Hexadecimal string
68
- */
69
- export function rgbToHexString([r, g, b]: [number, number, number]): string {
70
- r = clamp(Math.round(r * 255), 0, 255);
71
- g = clamp(Math.round(g * 255), 0, 255);
72
- b = clamp(Math.round(b * 255), 0, 255);
73
-
74
- const result = (b | (g << 8) | (r << 16) | (1 << 24)).toString(16).slice(1);
75
- return `#${result}`;
76
- }
77
-
78
- /**
79
- * Convert hexadecimal to RGB
80
- * Note: rgb values are contained in the interval [0, 1]
81
- *
82
- * @param {(number|string)} hex Hexadecimal color
83
- * @returns {[number, number, number]} RGB color
84
- */
85
- export function hexToRgb(hex: number | string): [number, number, number] {
86
- if (typeof hex === 'number') {
87
- hex = Math.floor(hex);
88
- } else if (typeof hex === 'string') {
89
- hex = normalizeHexString(hex).replace(/^#/, '');
90
- hex = parseInt(hex, 16);
91
- }
92
-
93
- const r = ((hex >> 16) & 255) / 255;
94
- const g = ((hex >> 8) & 255) / 255;
95
- const b = (hex & 255) / 255;
96
-
97
- return [r, g, b];
98
- }
99
-
100
- /**
101
- * Lighten a color
102
- *
103
- * @param {string} hex Hexadecimal string
104
- * @param {number} [amount=0] Amount of the color offset
105
- * @returns {string} Computed hexadecimal
106
- */
107
- export function lighten(hex: string, amount: number = 0): string {
108
- let prefix = '';
109
-
110
- if (hex[0] === '#') {
111
- hex = hex.slice(1);
112
- prefix = '#';
113
- }
114
-
115
- const value = parseInt(hex, 16);
116
-
117
- const r = clamp((value >> 16) + amount, 0, 255);
118
- const b = clamp(((value >> 8) & 0x00ff) + amount, 0, 255);
119
- const g = clamp((value & 0x0000ff) + amount, 0, 255);
120
-
121
- let result: number | string = g | (b << 8) | (r << 16);
122
- if (r === 0 && g === 0 && b === 0 && amount !== 0) {
123
- result = '000000';
124
- }
125
-
126
- return prefix + result.toString(16);
127
- }
128
-
129
- /**
130
- * Darken a color
131
- *
132
- * @param {string} hex Hexadecimal string
133
- * @param {number} [amount=0] Amount of the color offset
134
- * @returns {string} Computed hexadecimal
135
- */
136
- export function darken(hex: string, amount: number = 0): string {
137
- return lighten(hex, -amount);
138
- }
139
-
140
- // ***************************************************
141
- // RGB & Hue-Saturation-Lightness (HSL) color spaces
142
- // ***************************************************
143
-
144
- /**
145
- * Normalize an HSL string
146
- * Note: hsl values are contained in the intervals H: [0, 360], S: [0, 1], L: [0, 1]
147
- *
148
- * @param {string} hsl HSL string (format: 'hsl(360, 100%, 100%)')
149
- * @returns {[number, number, number]} Normalized HSL color
150
- */
151
- export function normalizeHslString(hsl: string): [number, number, number] {
152
- const [h, s, l] = hsl.match(/\d+/g)?.map(Number) ?? [0, 0, 0];
153
- return [h, s / 100, l / 100];
154
- }
155
-
156
- /**
157
- * Convert RGB to HSL
158
- * Notes:
159
- * - rgb values are contained in the interval [0, 1]
160
- * - hsl values are contained in the intervals H: [0, 360], S: [0, 1], L: [0, 1]
161
- *
162
- * @param {[number, number, number]} rgb RGB color
163
- * @returns {[number, number, number]} HSL color
164
- */
165
- export function rgbToHsl([r, g, b]: [number, number, number]): [number, number, number] {
166
- const l = Math.max(r, g, b);
167
- const s = l - Math.min(r, g, b);
168
- const h = s ? (l === r ? (g - b) / s : l === g ? 2 + (b - r) / s : 4 + (r - g) / s) : 0;
169
-
170
- return [
171
- 60 * h < 0 ? 60 * h + 360 : 60 * h,
172
- s ? (l <= 0.5 ? s / (2 * l - s) : s / (2 - (2 * l - s))) : 0,
173
- (2 * l - s) / 2
174
- ];
175
- }
176
-
177
- /**
178
- * Convert HSL to RGB
179
- * Notes:
180
- * - rgb values are contained in the interval [0, 1]
181
- * - hsl values are contained in the intervals H: [0, 360], S: [0, 1], L: [0, 1]
182
- *
183
- * @param {[number, number, number]} hsl HSL color
184
- * @returns {[number, number, number]} RGB color
185
- */
186
- export function hslToRgb([h, s, l]: [number, number, number]): [number, number, number] {
187
- const a = s * Math.min(l, 1 - l);
188
- const k = (v: number) => (v + h / 30) % 12;
189
- const f = (v: number) => l - a * Math.max(-1, Math.min(k(v) - 3, Math.min(9 - k(v), 1)));
190
- return [f(0), f(8), f(4)];
191
- }
192
-
193
- // ***************************************************
194
- // RGB & Hue-Saturation-Brightness (HSB) color spaces
195
- // ***************************************************
196
-
197
- /**
198
- * Convert RGB to HSB
199
- * Notes:
200
- * - rgb values are contained in the interval [0, 1]
201
- * - hsb values are contained in the intervals H: [0, 360], S: [0, 1], B: [0, 1]
202
- *
203
- * @param {[number, number, number]} rgb RGB color
204
- * @returns {[number, number, number]} HSB color
205
- */
206
- export function rgbToHsb([r, g, b]: [number, number, number]): [number, number, number] {
207
- const max = Math.max(r, g, b);
208
- const min = Math.min(r, g, b);
209
- const delta = max - min;
210
- const h =
211
- delta === 0 ? 0 : delta && max === r ? (g - b) / delta : max === g ? 2 + (b - r) / delta : 4 + (r - g) / delta;
212
- return [60 * (h < 0 ? h + 6 : h), max && delta / max, max];
213
- }
214
-
215
- /**
216
- * Convert HSB to RGB
217
- * Notes:
218
- * - rgb values are contained in the interval [0, 1]
219
- * - hsb values are contained in the intervals H: [0, 360], S: [0, 1], B: [0, 1]
220
- *
221
- * @param {[number, number, number]} hsb HSB color
222
- * @returns {[number, number, number]} RGB color
223
- */
224
- export function hsbToRgb([h, s, b]: [number, number, number]): [number, number, number] {
225
- const k = (v: number) => (v + h / 60) % 6;
226
- const f = (v: number) => b * (1 - s * Math.max(0, Math.min(k(v), 4 - k(v), 1)));
227
- return [f(5), f(3), f(1)];
228
- }
229
-
230
- // *********************************************
231
- // LAB & Hue-Chroma-Luminance (HCL) color spaces
232
- // *********************************************
233
-
234
- /**
235
- * Convert LAB to HCL
236
- * -> http://www.brucelindbloom.com/index.html?Eqn_Lab_to_LCH.html
237
- *
238
- * @param {[number, number, number]} lab LAB color
239
- * @returns {[number, number, number]} HCL color
240
- */
241
- export function labToHcl([l, a, b]: [number, number, number]): [number, number, number] {
242
- const c = Math.sqrt(a * a + b * b);
243
- const h = abToHue(a, b);
244
- return [h, c, l];
245
- }
246
-
247
- /**
248
- * Convert HCL to LAB
249
- * -> http://www.brucelindbloom.com/index.html?Eqn_LCH_to_Lab.html
250
- *
251
- * @param {[number, number, number]} hcl HCL color
252
- * @returns {[number, number, number]} LAB color space
253
- */
254
- export function hclToLab([h, c, l]: [number, number, number]): [number, number, number] {
255
- const a = c * Math.cos(toRadians(h));
256
- const b = c * Math.sin(toRadians(h));
257
- return [l, a, b];
258
- }
259
-
260
- /**
261
- * Convert A and B of LAB to Hue of LCH
262
- * -> https://stackoverflow.com/questions/53733379/conversion-of-cielab-to-cielchab-not-yielding-correct-result
263
- *
264
- * @param {number} a A value of LAB color
265
- * @param {number} b B value of LAB color
266
- * @returns {number} Hue value
267
- */
268
- function abToHue(a: number, b: number): number {
269
- if (a >= 0 && b === 0) {
270
- return 0;
271
- }
272
- if (a < 0 && b === 0) {
273
- return 180;
274
- }
275
- if (a === 0 && b > 0) {
276
- return 90;
277
- }
278
- if (a === 0 && b < 0) {
279
- return 270;
280
- }
281
-
282
- let xBias = 0;
283
- if (a > 0 && b > 0) {
284
- xBias = 0;
285
- } else if (a < 0) {
286
- xBias = 180;
287
- } else if (a > 0 && b < 0) {
288
- xBias = 360;
289
- }
290
-
291
- return toDegrees(Math.atan(b / a)) + xBias;
292
- }
293
-
294
- // ******************************************
295
- // LAB & RGB color spaces
296
- // ******************************************
297
- const f1 = (v: number) => (v * v * v > 0.008856 ? v * v * v : (v - 16 / 116) / 7.787);
298
- const f2 = (v: number) => (v > 0.0031308 ? 1.055 * Math.pow(v, 1 / 2.4) - 0.055 : 12.92 * v);
299
- const f3 = (v: number) => (v > 0.04045 ? Math.pow((v + 0.055) / 1.055, 2.4) : v / 12.92);
300
- const f4 = (v: number) => (v > 0.008856 ? Math.pow(v, 1 / 3) : 7.787 * v + 16 / 116);
301
-
302
- /**
303
- * Converts LAB to RGB
304
- *
305
- * @param {[number, number, number]} lab LAB color
306
- * @returns {[number, number, number]} RGB color
307
- */
308
- export function labToRgb([l, a, b]: [number, number, number]): [number, number, number] {
309
- let y = (l + 16) / 116;
310
- let x = a / 500 + y;
311
- let z = y - b / 200;
312
-
313
- x = 0.95047 * f1(x);
314
- y = 1.0 * f1(y);
315
- z = 1.08883 * f1(z);
316
-
317
- return [
318
- clamp(f2(x * 3.2406 + y * -1.5372 + z * -0.4986)),
319
- clamp(f2(x * -0.9689 + y * 1.8758 + z * 0.0415)),
320
- clamp(f2(x * 0.0557 + y * -0.204 + z * 1.057))
321
- ];
322
- }
323
-
324
- /**
325
- * Converts RGB to LAB
326
- *
327
- * @param {[number, number, number]} rgb RGB color
328
- * @returns {[number, number, number]} LAB color
329
- */
330
- export function rgbToLab([r, g, b]: [number, number, number]): [number, number, number] {
331
- r = f3(r);
332
- g = f3(g);
333
- b = f3(b);
334
-
335
- let x = f4((r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047);
336
- let y = f4((r * 0.2126 + g * 0.7152 + b * 0.0722) / 1);
337
- let z = f4((r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883);
338
-
339
- return [116 * y - 16, 500 * (x - y), 200 * (y - z)];
340
- }
341
-
342
- /**
343
- * Get the delta from two LAB colors
344
- *
345
- * @param {[number, number, number]} labA First LAB color
346
- * @param {[number, number, number]} labB Second LAB color
347
- * @returns {number} Delta
348
- */
349
- export function deltaE(labA: [number, number, number], labB: [number, number, number]): number {
350
- const deltaL = labA[0] - labB[0];
351
- const deltaA = labA[1] - labB[1];
352
- const deltaB = labA[2] - labB[2];
353
- const c1 = Math.sqrt(labA[1] * labA[1] + labA[2] * labA[2]);
354
- const c2 = Math.sqrt(labB[1] * labB[1] + labB[2] * labB[2]);
355
- const deltaC = c1 - c2;
356
- let deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC;
357
- deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH);
358
- const sc = 1.0 + 0.045 * c1;
359
- const sh = 1.0 + 0.015 * c1;
360
- const deltaLKlsl = deltaL / 1;
361
- const deltaCkcsc = deltaC / sc;
362
- const deltaHkhsh = deltaH / sh;
363
- const i = deltaLKlsl * deltaLKlsl + deltaCkcsc * deltaCkcsc + deltaHkhsh * deltaHkhsh;
364
- return i < 0 ? 0 : Math.sqrt(i);
365
- }
366
-
367
- // *********************************************
368
- // RGB & Hue-Chroma-Luminance (HCL) color spaces
369
- // *********************************************
370
-
371
- /**
372
- * Convert RGB to HCL
373
- *
374
- * @param {[number, number, number]} rgb RGB color
375
- * @returns {[number, number, number]} HCL color
376
- */
377
- export function rgbToHcl([r, g, b]: [number, number, number]): [number, number, number] {
378
- return labToHcl(rgbToLab([r, g, b]));
379
- }
380
-
381
- /**
382
- * Converts HCL to RGB
383
- *
384
- * @param {[number, number, number]} hcl RGB color
385
- * @returns {[number, number, number]} RGB color
386
- */
387
- export function hclToRgb([h, c, l]: [number, number, number]): [number, number, number] {
388
- return labToRgb(hclToLab([h, c, l]));
389
- }