@versatiles/style 5.8.3 → 5.8.4
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.
- package/README.md +14 -14
- package/dist/index.d.ts +1 -1
- package/dist/index.js +570 -338
- package/dist/index.js.map +1 -1
- package/package.json +12 -7
- package/src/color/abstract.ts +1 -1
- package/src/color/hsl.test.ts +10 -16
- package/src/color/hsl.ts +11 -15
- package/src/color/hsv.test.ts +4 -10
- package/src/color/hsv.ts +38 -22
- package/src/color/index.test.ts +2 -4
- package/src/color/index.ts +1 -1
- package/src/color/random.test.ts +1 -1
- package/src/color/random.ts +127 -25
- package/src/color/rgb.test.ts +55 -36
- package/src/color/rgb.ts +35 -48
- package/src/color/utils.test.ts +4 -6
- package/src/color/utils.ts +1 -3
- package/src/guess_style/guess_style.test.ts +49 -43
- package/src/guess_style/guess_style.ts +64 -21
- package/src/guess_style/index.ts +0 -1
- package/src/index.test.ts +34 -7
- package/src/index.ts +29 -19
- package/src/lib/utils.test.ts +2 -2
- package/src/lib/utils.ts +2 -1
- package/src/shortbread/index.ts +0 -1
- package/src/shortbread/layers.test.ts +15 -1
- package/src/shortbread/layers.ts +204 -199
- package/src/shortbread/properties.test.ts +3 -4
- package/src/shortbread/properties.ts +18 -4
- package/src/shortbread/template.test.ts +7 -2
- package/src/shortbread/template.ts +7 -14
- package/src/style_builder/decorator.test.ts +4 -4
- package/src/style_builder/decorator.ts +29 -21
- package/src/style_builder/recolor.test.ts +6 -31
- package/src/style_builder/recolor.ts +20 -20
- package/src/style_builder/style_builder.test.ts +50 -13
- package/src/style_builder/style_builder.ts +29 -31
- package/src/style_builder/types.ts +85 -2
- package/src/styles/LICENSE.md +15 -15
- package/src/styles/colorful.test.ts +91 -0
- package/src/styles/colorful.ts +229 -122
- package/src/styles/eclipse.ts +1 -1
- package/src/styles/empty.ts +1 -1
- package/src/styles/graybeard.ts +2 -2
- package/src/styles/index.ts +0 -3
- package/src/styles/neutrino.ts +14 -16
- package/src/styles/shadow.ts +2 -2
- package/src/types/index.ts +0 -1
- package/src/types/maplibre.ts +17 -3
- package/src/types/tilejson.test.ts +8 -5
- package/src/types/tilejson.ts +13 -13
- package/src/types/vector_layer.test.ts +4 -1
- package/src/types/vector_layer.ts +7 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@versatiles/style",
|
|
3
|
-
"version": "5.8.
|
|
3
|
+
"version": "5.8.4",
|
|
4
4
|
"description": "Generate StyleJSON for MapLibre",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"check": "npm run lint && npm run build && npm run test ",
|
|
14
|
+
"check": "npm run lint && npm run build && npm run test && npm run format:check",
|
|
15
15
|
"build": "rm -rf release; npm run build-browser && npm run build-node && npm run build-styles && npm run build-sprites && npm run doc",
|
|
16
16
|
"build-browser": "rollup -c --environment BUILD:browser && $(cd release/versatiles-style; tar -cf - versatiles-style.* | gzip -9 > ../versatiles-style.tar.gz)",
|
|
17
17
|
"build-node": "rm -rf dist && rollup -c --environment BUILD:node && chmod +x dist/index.js && rm -r dist/declaration",
|
|
@@ -22,13 +22,16 @@
|
|
|
22
22
|
"doc-graph": "vrt deps-graph | vrt doc-insert README.md '### Dependency Graph'",
|
|
23
23
|
"doc-screenshots": "tsx scripts/screenshots.ts",
|
|
24
24
|
"doc-typescript": "vrt doc-typescript -f html",
|
|
25
|
+
"format": "prettier --write .",
|
|
26
|
+
"format:check": "prettier --check .",
|
|
25
27
|
"lint": "eslint --color .",
|
|
26
28
|
"prepack": "npm run build",
|
|
27
29
|
"release": "vrt release-npm",
|
|
28
30
|
"test": "npm run test-typescript",
|
|
29
31
|
"test-coverage": "vitest run --coverage",
|
|
30
32
|
"test-typescript": "vitest run",
|
|
31
|
-
"upgrade": "vrt deps-upgrade"
|
|
33
|
+
"upgrade": "vrt deps-upgrade",
|
|
34
|
+
"prepare": "husky"
|
|
32
35
|
},
|
|
33
36
|
"repository": {
|
|
34
37
|
"type": "git",
|
|
@@ -54,16 +57,18 @@
|
|
|
54
57
|
"@types/bin-pack": "^1.0.3",
|
|
55
58
|
"@types/brace-expansion": "^1.1.2",
|
|
56
59
|
"@types/inquirer": "^9.0.9",
|
|
57
|
-
"@types/node": "^24.10.
|
|
60
|
+
"@types/node": "^24.10.2",
|
|
58
61
|
"@types/tar-stream": "^3.1.4",
|
|
59
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
60
|
-
"@typescript-eslint/parser": "^8.
|
|
62
|
+
"@typescript-eslint/eslint-plugin": "^8.49.0",
|
|
63
|
+
"@typescript-eslint/parser": "^8.49.0",
|
|
61
64
|
"@versatiles/release-tool": "^2.5.0",
|
|
62
65
|
"@vitest/coverage-v8": "^4.0.15",
|
|
63
66
|
"bin-pack": "^1.0.2",
|
|
64
67
|
"esbuild": "^0.27.1",
|
|
65
68
|
"eslint": "^9.39.1",
|
|
69
|
+
"husky": "^9.1.7",
|
|
66
70
|
"inquirer": "^13.0.2",
|
|
71
|
+
"prettier": "^3.7.4",
|
|
67
72
|
"rollup": "^4.53.3",
|
|
68
73
|
"rollup-plugin-dts": "^6.3.0",
|
|
69
74
|
"rollup-plugin-sourcemaps2": "^0.5.4",
|
|
@@ -71,7 +76,7 @@
|
|
|
71
76
|
"tar-stream": "^3.1.7",
|
|
72
77
|
"tsx": "^4.21.0",
|
|
73
78
|
"typescript": "^5.9.3",
|
|
74
|
-
"typescript-eslint": "^8.
|
|
79
|
+
"typescript-eslint": "^8.49.0",
|
|
75
80
|
"vitest": "^4.0.15"
|
|
76
81
|
}
|
|
77
82
|
}
|
package/src/color/abstract.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { RGB } from './rgb.js';
|
|
|
6
6
|
* The abstract `Color` class provides a blueprint for color manipulation and conversion.
|
|
7
7
|
* It includes methods for converting between different color models ({@link HSL}, {@link HSV}, {@link RGB}),
|
|
8
8
|
* as well as various color transformations such as inversion, rotation, saturation, and blending.
|
|
9
|
-
*
|
|
9
|
+
*
|
|
10
10
|
* @abstract
|
|
11
11
|
*/
|
|
12
12
|
/**
|
package/src/color/hsl.test.ts
CHANGED
|
@@ -4,9 +4,7 @@ import { HSV } from './hsv.js';
|
|
|
4
4
|
import { RGB } from './rgb.js';
|
|
5
5
|
|
|
6
6
|
describe('HSL Class', () => {
|
|
7
|
-
|
|
8
7
|
describe('constructor', () => {
|
|
9
|
-
|
|
10
8
|
it('should initialize small HSL values correctly', () => {
|
|
11
9
|
const color = new HSL(10, 20, 30, 0.4);
|
|
12
10
|
expect(color.asArray()).toStrictEqual([10, 20, 30, 0.4]);
|
|
@@ -21,7 +19,7 @@ describe('HSL Class', () => {
|
|
|
21
19
|
const color = new HSL(-60, -10, -10, -1);
|
|
22
20
|
expect(color.asArray()).toStrictEqual([300, 0, 0, 0]);
|
|
23
21
|
});
|
|
24
|
-
})
|
|
22
|
+
});
|
|
25
23
|
|
|
26
24
|
it('clone should return a new HSL instance with identical values', () => {
|
|
27
25
|
const color = new HSL(120, 50, 50, 0.5);
|
|
@@ -32,7 +30,6 @@ describe('HSL Class', () => {
|
|
|
32
30
|
});
|
|
33
31
|
|
|
34
32
|
describe('conversion', () => {
|
|
35
|
-
|
|
36
33
|
it('asString should return correct HSL and HSLA strings', () => {
|
|
37
34
|
const color1 = new HSL(120, 50, 50);
|
|
38
35
|
expect(color1.asString()).toBe('hsl(120,50%,50%)');
|
|
@@ -52,8 +49,7 @@ describe('HSL Class', () => {
|
|
|
52
49
|
const hsl = new HSL(...input);
|
|
53
50
|
const hsv = hsl.asHSV();
|
|
54
51
|
expect(hsv).toBeInstanceOf(HSV);
|
|
55
|
-
expect(hsv.asArray().map(Math.round))
|
|
56
|
-
.toStrictEqual([...output, 1]);
|
|
52
|
+
expect(hsv.asArray().map(Math.round)).toStrictEqual([...output, 1]);
|
|
57
53
|
}
|
|
58
54
|
|
|
59
55
|
check([10, 0, 0], [10, 0, 0]);
|
|
@@ -69,11 +65,10 @@ describe('HSL Class', () => {
|
|
|
69
65
|
|
|
70
66
|
it('asRGB should correctly convert HSL to RGB', () => {
|
|
71
67
|
function check(input: [number, number, number], output: [number, number, number]) {
|
|
72
|
-
const hsl = new HSL(...input)
|
|
68
|
+
const hsl = new HSL(...input);
|
|
73
69
|
const rgb = hsl.asRGB();
|
|
74
70
|
expect(rgb).toBeInstanceOf(RGB);
|
|
75
|
-
expect(rgb.asArray().map(Math.round))
|
|
76
|
-
.toStrictEqual([...output, 1]);
|
|
71
|
+
expect(rgb.asArray().map(Math.round)).toStrictEqual([...output, 1]);
|
|
77
72
|
}
|
|
78
73
|
|
|
79
74
|
check([10, 0, 0], [0, 0, 0]);
|
|
@@ -86,8 +81,7 @@ describe('HSL Class', () => {
|
|
|
86
81
|
check([17, 100, 50], [255, 72, 0]);
|
|
87
82
|
check([18, 100, 100], [255, 255, 255]);
|
|
88
83
|
});
|
|
89
|
-
|
|
90
|
-
})
|
|
84
|
+
});
|
|
91
85
|
|
|
92
86
|
describe('should parse valid HSL and HSLA strings', () => {
|
|
93
87
|
function check(str: string, result: number[]) {
|
|
@@ -95,7 +89,7 @@ describe('HSL Class', () => {
|
|
|
95
89
|
const color = HSL.parse(str);
|
|
96
90
|
expect(color).toBeInstanceOf(HSL);
|
|
97
91
|
expect(color.asArray()).toStrictEqual(result);
|
|
98
|
-
})
|
|
92
|
+
});
|
|
99
93
|
}
|
|
100
94
|
|
|
101
95
|
check('hsl(240,100%,50%)', [240, 100, 50, 1]);
|
|
@@ -109,7 +103,7 @@ describe('HSL Class', () => {
|
|
|
109
103
|
|
|
110
104
|
describe('invertLuminosity', () => {
|
|
111
105
|
let color: HSL;
|
|
112
|
-
beforeEach(() => color = new HSL(120, 50, 50, 0.8));
|
|
106
|
+
beforeEach(() => (color = new HSL(120, 50, 50, 0.8)));
|
|
113
107
|
|
|
114
108
|
it('inverts luminosity correctly', () => {
|
|
115
109
|
expect(color.invertLuminosity().asArray()).toStrictEqual([120, 50, 50, 0.8]); // Luminosity inverted to 50%
|
|
@@ -126,7 +120,7 @@ describe('HSL Class', () => {
|
|
|
126
120
|
|
|
127
121
|
describe('rotateHue', () => {
|
|
128
122
|
let color: HSL;
|
|
129
|
-
beforeEach(() => color = new HSL(120, 50, 50, 0.8));
|
|
123
|
+
beforeEach(() => (color = new HSL(120, 50, 50, 0.8)));
|
|
130
124
|
|
|
131
125
|
it('rotates hue correctly within the range of 0-360', () => {
|
|
132
126
|
expect(color.rotateHue(180).asArray()).toStrictEqual([300, 50, 50, 0.8]); // Hue rotated by 180 degrees
|
|
@@ -144,7 +138,7 @@ describe('HSL Class', () => {
|
|
|
144
138
|
describe('saturate', () => {
|
|
145
139
|
let color: HSL, grey: HSL;
|
|
146
140
|
beforeEach(() => {
|
|
147
|
-
color = new HSL(120, 50, 50, 0.8)
|
|
141
|
+
color = new HSL(120, 50, 50, 0.8);
|
|
148
142
|
grey = new HSL(120, 0, 50, 0.8);
|
|
149
143
|
});
|
|
150
144
|
|
|
@@ -166,7 +160,7 @@ describe('HSL Class', () => {
|
|
|
166
160
|
|
|
167
161
|
describe('fade', () => {
|
|
168
162
|
let color: HSL;
|
|
169
|
-
beforeEach(() => color = new HSL(120, 50, 50, 0.8));
|
|
163
|
+
beforeEach(() => (color = new HSL(120, 50, 50, 0.8)));
|
|
170
164
|
|
|
171
165
|
it('reduces alpha correctly', () => {
|
|
172
166
|
expect(color.fade(0.5).asArray()).toStrictEqual([120, 50, 50, 0.4]); // Alpha reduced by 50%
|
package/src/color/hsl.ts
CHANGED
|
@@ -56,12 +56,7 @@ export class HSL extends Color {
|
|
|
56
56
|
* @returns A new HSL color with rounded hue, saturation, lightness, and alpha components.
|
|
57
57
|
*/
|
|
58
58
|
round(): HSL {
|
|
59
|
-
return new HSL(
|
|
60
|
-
Math.round(this.h),
|
|
61
|
-
Math.round(this.s),
|
|
62
|
-
Math.round(this.l),
|
|
63
|
-
Math.round(this.a * 1000) / 1000,
|
|
64
|
-
);
|
|
59
|
+
return new HSL(Math.round(this.h), Math.round(this.s), Math.round(this.l), Math.round(this.a * 1000) / 1000);
|
|
65
60
|
}
|
|
66
61
|
|
|
67
62
|
/**
|
|
@@ -105,7 +100,8 @@ export class HSL extends Color {
|
|
|
105
100
|
* @returns A new HSV color representing the same color.
|
|
106
101
|
*/
|
|
107
102
|
asHSV(): HSV {
|
|
108
|
-
const s = this.s / 100,
|
|
103
|
+
const s = this.s / 100,
|
|
104
|
+
l = this.l / 100;
|
|
109
105
|
const v = l + s * Math.min(l, 1 - l);
|
|
110
106
|
const sv = v === 0 ? 0 : 2 * (1 - l / v);
|
|
111
107
|
return new HSV(this.h, sv * 100, v * 100, this.a);
|
|
@@ -136,12 +132,7 @@ export class HSL extends Color {
|
|
|
136
132
|
};
|
|
137
133
|
|
|
138
134
|
// Convert to RGB in the 0-255 range and return
|
|
139
|
-
return new RGB(
|
|
140
|
-
255 * hueToRgb(h + 1 / 3),
|
|
141
|
-
255 * hueToRgb(h),
|
|
142
|
-
255 * hueToRgb(h - 1 / 3),
|
|
143
|
-
this.a
|
|
144
|
-
);
|
|
135
|
+
return new RGB(255 * hueToRgb(h + 1 / 3), 255 * hueToRgb(h), 255 * hueToRgb(h - 1 / 3), this.a);
|
|
145
136
|
}
|
|
146
137
|
|
|
147
138
|
/**
|
|
@@ -162,7 +153,12 @@ export class HSL extends Color {
|
|
|
162
153
|
|
|
163
154
|
match = input.match(/^hsla\((?<h>[-+0-9.]+)(?:deg)?,(?<s>[-+0-9.]+)%,(?<l>[-+0-9.]+)%,(?<a>[-+0-9.]+)\)$/);
|
|
164
155
|
if (match) {
|
|
165
|
-
return new HSL(
|
|
156
|
+
return new HSL(
|
|
157
|
+
parseFloat(match.groups!.h),
|
|
158
|
+
parseFloat(match.groups!.s),
|
|
159
|
+
parseFloat(match.groups!.l),
|
|
160
|
+
parseFloat(match.groups!.a)
|
|
161
|
+
);
|
|
166
162
|
}
|
|
167
163
|
|
|
168
164
|
throw new Error(`Invalid HSL color string: "${input}"`);
|
|
@@ -202,4 +198,4 @@ export class HSL extends Color {
|
|
|
202
198
|
fade(value: number): HSL {
|
|
203
199
|
return new HSL(this.h, this.s, this.l, this.a * (1 - value));
|
|
204
200
|
}
|
|
205
|
-
}
|
|
201
|
+
}
|
package/src/color/hsv.test.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { HSL } from './hsl.js';
|
|
|
4
4
|
import { RGB } from './rgb.js';
|
|
5
5
|
|
|
6
6
|
describe('HSV Class', () => {
|
|
7
|
-
|
|
8
7
|
it('constructor initializes values correctly with clamping', () => {
|
|
9
8
|
const color = new HSV(400, 120, 120, 2);
|
|
10
9
|
expect(color.asArray()).toStrictEqual([40, 100, 100, 1]);
|
|
@@ -63,14 +62,12 @@ describe('HSV Class', () => {
|
|
|
63
62
|
});
|
|
64
63
|
|
|
65
64
|
describe('color conversion', () => {
|
|
66
|
-
|
|
67
65
|
it('asHSL converts HSV to HSL correctly', () => {
|
|
68
66
|
function check(input: [number, number, number], output: [number, number, number]) {
|
|
69
67
|
const hsv = new HSV(...input);
|
|
70
68
|
const hsl = hsv.asHSL();
|
|
71
69
|
expect(hsl).toBeInstanceOf(HSL);
|
|
72
|
-
expect(hsl.asArray().map(Math.round))
|
|
73
|
-
.toStrictEqual([...output, 1]);
|
|
70
|
+
expect(hsl.asArray().map(Math.round)).toStrictEqual([...output, 1]);
|
|
74
71
|
|
|
75
72
|
expect(hsv.asHex()).toStrictEqual(hsl.asHex());
|
|
76
73
|
}
|
|
@@ -90,8 +87,7 @@ describe('HSV Class', () => {
|
|
|
90
87
|
const color = new HSV(120, 100, 100);
|
|
91
88
|
const rgb = color.asRGB();
|
|
92
89
|
expect(rgb).toBeInstanceOf(RGB);
|
|
93
|
-
expect(rgb.asArray().map(value => Math.round(value)))
|
|
94
|
-
.toStrictEqual([0, 255, 0, 1]);
|
|
90
|
+
expect(rgb.asArray().map((value) => Math.round(value))).toStrictEqual([0, 255, 0, 1]);
|
|
95
91
|
});
|
|
96
92
|
|
|
97
93
|
it('asHSV and toHSV return the same instance or clone', () => {
|
|
@@ -104,8 +100,7 @@ describe('HSV Class', () => {
|
|
|
104
100
|
function check(input: [number, number, number], output: [number, number, number]) {
|
|
105
101
|
const color = new HSV(...input);
|
|
106
102
|
const rgb = color.asRGB();
|
|
107
|
-
expect(rgb.asArray().map(value => Math.round(value)))
|
|
108
|
-
.toStrictEqual([...output, 1]);
|
|
103
|
+
expect(rgb.asArray().map((value) => Math.round(value))).toStrictEqual([...output, 1]);
|
|
109
104
|
}
|
|
110
105
|
|
|
111
106
|
check([10, 0, 0], [0, 0, 0]);
|
|
@@ -143,7 +138,6 @@ describe('HSV Class', () => {
|
|
|
143
138
|
check(360, '#FF0000');
|
|
144
139
|
check(361, '#FF0400');
|
|
145
140
|
});
|
|
146
|
-
|
|
147
141
|
});
|
|
148
142
|
|
|
149
143
|
describe('parse errors and validations', () => {
|
|
@@ -172,4 +166,4 @@ describe('HSV Class', () => {
|
|
|
172
166
|
expect(transparent.fade(0.5).asArray()).toStrictEqual([0, 50, 50, 0]); // Remains fully transparent
|
|
173
167
|
});
|
|
174
168
|
});
|
|
175
|
-
});
|
|
169
|
+
});
|
package/src/color/hsv.ts
CHANGED
|
@@ -57,12 +57,7 @@ export class HSV extends Color {
|
|
|
57
57
|
* @returns A new HSV color with rounded components.
|
|
58
58
|
*/
|
|
59
59
|
round(): HSV {
|
|
60
|
-
return new HSV(
|
|
61
|
-
Math.round(this.h),
|
|
62
|
-
Math.round(this.s),
|
|
63
|
-
Math.round(this.v),
|
|
64
|
-
Math.round(this.a * 1000) / 1000
|
|
65
|
-
);
|
|
60
|
+
return new HSV(Math.round(this.h), Math.round(this.s), Math.round(this.v), Math.round(this.a * 1000) / 1000);
|
|
66
61
|
}
|
|
67
62
|
|
|
68
63
|
/**
|
|
@@ -89,13 +84,8 @@ export class HSV extends Color {
|
|
|
89
84
|
const s = this.s / 100;
|
|
90
85
|
const v = this.v / 100;
|
|
91
86
|
const k = (2 - s) * v;
|
|
92
|
-
const q = k < 1 ? k : 2 - k
|
|
93
|
-
return new HSL(
|
|
94
|
-
this.h,
|
|
95
|
-
q == 0 ? 0 : 100 * s * v / q,
|
|
96
|
-
100 * k / 2,
|
|
97
|
-
this.a
|
|
98
|
-
);
|
|
87
|
+
const q = k < 1 ? k : 2 - k;
|
|
88
|
+
return new HSL(this.h, q == 0 ? 0 : (100 * s * v) / q, (100 * k) / 2, this.a);
|
|
99
89
|
}
|
|
100
90
|
|
|
101
91
|
/**
|
|
@@ -123,25 +113,51 @@ export class HSV extends Color {
|
|
|
123
113
|
const s = this.s / 100; // Normalize s to range [0, 1]
|
|
124
114
|
const v = this.v / 100; // Normalize v to range [0, 1]
|
|
125
115
|
|
|
126
|
-
let r = 0,
|
|
116
|
+
let r = 0,
|
|
117
|
+
g = 0,
|
|
118
|
+
b = 0;
|
|
127
119
|
|
|
128
120
|
if (s === 0) {
|
|
129
121
|
// Achromatic (grey)
|
|
130
122
|
r = g = b = v;
|
|
131
123
|
} else {
|
|
132
124
|
const i = Math.floor(h * 6); // Determine the sector of the color wheel
|
|
133
|
-
const f = h * 6 - i;
|
|
125
|
+
const f = h * 6 - i; // Fractional part of h * 6
|
|
134
126
|
const p = v * (1 - s);
|
|
135
127
|
const q = v * (1 - s * f);
|
|
136
128
|
const t = v * (1 - s * (1 - f));
|
|
137
129
|
|
|
138
130
|
switch (i % 6) {
|
|
139
|
-
case 0:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
case
|
|
131
|
+
case 0:
|
|
132
|
+
r = v;
|
|
133
|
+
g = t;
|
|
134
|
+
b = p;
|
|
135
|
+
break;
|
|
136
|
+
case 1:
|
|
137
|
+
r = q;
|
|
138
|
+
g = v;
|
|
139
|
+
b = p;
|
|
140
|
+
break;
|
|
141
|
+
case 2:
|
|
142
|
+
r = p;
|
|
143
|
+
g = v;
|
|
144
|
+
b = t;
|
|
145
|
+
break;
|
|
146
|
+
case 3:
|
|
147
|
+
r = p;
|
|
148
|
+
g = q;
|
|
149
|
+
b = v;
|
|
150
|
+
break;
|
|
151
|
+
case 4:
|
|
152
|
+
r = t;
|
|
153
|
+
g = p;
|
|
154
|
+
b = v;
|
|
155
|
+
break;
|
|
156
|
+
case 5:
|
|
157
|
+
r = v;
|
|
158
|
+
g = p;
|
|
159
|
+
b = q;
|
|
160
|
+
break;
|
|
145
161
|
}
|
|
146
162
|
}
|
|
147
163
|
|
|
@@ -170,4 +186,4 @@ export class HSV extends Color {
|
|
|
170
186
|
static randomColor(options?: RandomColorOptions): HSV {
|
|
171
187
|
return randomColor(options);
|
|
172
188
|
}
|
|
173
|
-
}
|
|
189
|
+
}
|
package/src/color/index.test.ts
CHANGED
|
@@ -4,9 +4,7 @@ import { HSL } from './hsl.js';
|
|
|
4
4
|
import { HSV } from './hsv.js';
|
|
5
5
|
import { RGB } from './rgb.js';
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
describe('Color Conversions', () => {
|
|
9
|
-
|
|
10
8
|
const scenarios: [number, number, number, number][] = [
|
|
11
9
|
[-100, 14, 15, 0],
|
|
12
10
|
[0, 0, 0, 0.1],
|
|
@@ -19,7 +17,7 @@ describe('Color Conversions', () => {
|
|
|
19
17
|
[700, 100, 50, 0.8],
|
|
20
18
|
[800, 100, 100, 0.9],
|
|
21
19
|
[900, 12, 13, 1.0],
|
|
22
|
-
]
|
|
20
|
+
];
|
|
23
21
|
it('test HSV -> HSL -> RGB', () => {
|
|
24
22
|
for (const v of scenarios) {
|
|
25
23
|
const hsv = new HSV(...v);
|
|
@@ -41,7 +39,7 @@ describe('Color Conversions', () => {
|
|
|
41
39
|
for (let i = 0; i < 4; i++) expect(a1[i]).toBeCloseTo(a2[i]);
|
|
42
40
|
}
|
|
43
41
|
});
|
|
44
|
-
})
|
|
42
|
+
});
|
|
45
43
|
|
|
46
44
|
describe('Color.parse', () => {
|
|
47
45
|
it('parses hexadecimal color strings correctly', () => {
|
package/src/color/index.ts
CHANGED
package/src/color/random.test.ts
CHANGED
|
@@ -20,7 +20,7 @@ describe('RandomColor', () => {
|
|
|
20
20
|
expect(array[2]).toBeGreaterThanOrEqual(0);
|
|
21
21
|
expect(array[2]).toBeLessThanOrEqual(100);
|
|
22
22
|
});
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
it('supports options for generating random colors', () => {
|
|
25
25
|
const random = randomColor({ hue: 'red', luminosity: 'bright' });
|
|
26
26
|
expect(random).toBeInstanceOf(HSV);
|
package/src/color/random.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { HSV } from './hsv.js';
|
|
2
2
|
import { mod } from './utils.js';
|
|
3
3
|
|
|
4
|
-
|
|
5
4
|
type Range = [number, number];
|
|
6
5
|
interface ColorInfo {
|
|
7
6
|
hueRange: Range | null;
|
|
@@ -33,8 +32,6 @@ export default function randomColor(options?: RandomColorOptions): HSV {
|
|
|
33
32
|
|
|
34
33
|
return new HSV(H, S, V, options.opacity ?? 1);
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
|
|
38
35
|
function pickHue(options: RandomColorOptions): number {
|
|
39
36
|
return mod(randomWithin(getHueRange(options.hue)), 360);
|
|
40
37
|
|
|
@@ -62,9 +59,15 @@ export default function randomColor(options?: RandomColorOptions): HSV {
|
|
|
62
59
|
if (options.saturation === 'strong') return sMax;
|
|
63
60
|
|
|
64
61
|
switch (options.luminosity) {
|
|
65
|
-
case 'bright':
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
case 'bright':
|
|
63
|
+
sMin = 55;
|
|
64
|
+
break;
|
|
65
|
+
case 'dark':
|
|
66
|
+
sMin = sMax - 10;
|
|
67
|
+
break;
|
|
68
|
+
case 'light':
|
|
69
|
+
sMax = 55;
|
|
70
|
+
break;
|
|
68
71
|
default:
|
|
69
72
|
}
|
|
70
73
|
|
|
@@ -72,17 +75,24 @@ export default function randomColor(options?: RandomColorOptions): HSV {
|
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
function pickBrightness(h: number, s: number, options: RandomColorOptions): number {
|
|
75
|
-
let bMin = getMinimumBrightness(h, s),
|
|
76
|
-
|
|
78
|
+
let bMin = getMinimumBrightness(h, s),
|
|
79
|
+
bMax = 100;
|
|
77
80
|
|
|
78
81
|
if (typeof options.luminosity === 'number') {
|
|
79
82
|
bMin = options.luminosity;
|
|
80
83
|
bMax = options.luminosity;
|
|
81
84
|
} else {
|
|
82
85
|
switch (options.luminosity) {
|
|
83
|
-
case 'dark':
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
case 'dark':
|
|
87
|
+
bMax = Math.min(100, bMin + 20);
|
|
88
|
+
break;
|
|
89
|
+
case 'light':
|
|
90
|
+
bMin = (bMax + bMin) / 2;
|
|
91
|
+
break;
|
|
92
|
+
case 'random':
|
|
93
|
+
bMin = 0;
|
|
94
|
+
bMax = 100;
|
|
95
|
+
break;
|
|
86
96
|
default:
|
|
87
97
|
}
|
|
88
98
|
}
|
|
@@ -96,7 +106,8 @@ export default function randomColor(options?: RandomColorOptions): HSV {
|
|
|
96
106
|
const [s1, v1] = lowerBounds[i];
|
|
97
107
|
const [s2, v2] = lowerBounds[i + 1];
|
|
98
108
|
if (s >= s1 && s <= s2) {
|
|
99
|
-
const m = (v2 - v1) / (s2 - s1),
|
|
109
|
+
const m = (v2 - v1) / (s2 - s1),
|
|
110
|
+
b = v1 - m * s1;
|
|
100
111
|
return m * s + b;
|
|
101
112
|
}
|
|
102
113
|
}
|
|
@@ -108,11 +119,10 @@ export default function randomColor(options?: RandomColorOptions): HSV {
|
|
|
108
119
|
function randomWithin(range: Range): number {
|
|
109
120
|
//Seeded random algorithm from http://indiegamr.com/generate-repeatable-random-numbers-in-js/
|
|
110
121
|
seed = (seed * 9301 + 49297) % 233280;
|
|
111
|
-
return Math.floor(range[0] + seed / 233280.0 * (range[1] - range[0]));
|
|
122
|
+
return Math.floor(range[0] + (seed / 233280.0) * (range[1] - range[0]));
|
|
112
123
|
}
|
|
113
124
|
}
|
|
114
125
|
|
|
115
|
-
|
|
116
126
|
function inputToSeed(input: number | string | null | undefined): number {
|
|
117
127
|
if (input == null) return 0;
|
|
118
128
|
if (typeof input === 'number') return input;
|
|
@@ -122,8 +132,6 @@ function inputToSeed(input: number | string | null | undefined): number {
|
|
|
122
132
|
return i;
|
|
123
133
|
}
|
|
124
134
|
|
|
125
|
-
|
|
126
|
-
|
|
127
135
|
function initColorDictionary(): Map<string, ColorInfo> {
|
|
128
136
|
const dict = new Map<string, ColorInfo>();
|
|
129
137
|
|
|
@@ -139,19 +147,113 @@ function initColorDictionary(): Map<string, ColorInfo> {
|
|
|
139
147
|
});
|
|
140
148
|
};
|
|
141
149
|
|
|
142
|
-
defineColor('monochrome', null, [
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
defineColor(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
defineColor('monochrome', null, [
|
|
151
|
+
[0, 0],
|
|
152
|
+
[100, 0],
|
|
153
|
+
]);
|
|
154
|
+
defineColor(
|
|
155
|
+
'red',
|
|
156
|
+
[-26, 18],
|
|
157
|
+
[
|
|
158
|
+
[20, 100],
|
|
159
|
+
[30, 92],
|
|
160
|
+
[40, 89],
|
|
161
|
+
[50, 85],
|
|
162
|
+
[60, 78],
|
|
163
|
+
[70, 70],
|
|
164
|
+
[80, 60],
|
|
165
|
+
[90, 55],
|
|
166
|
+
[100, 50],
|
|
167
|
+
]
|
|
168
|
+
);
|
|
169
|
+
defineColor(
|
|
170
|
+
'orange',
|
|
171
|
+
[18, 46],
|
|
172
|
+
[
|
|
173
|
+
[20, 100],
|
|
174
|
+
[30, 93],
|
|
175
|
+
[40, 88],
|
|
176
|
+
[50, 86],
|
|
177
|
+
[60, 85],
|
|
178
|
+
[70, 70],
|
|
179
|
+
[100, 70],
|
|
180
|
+
]
|
|
181
|
+
);
|
|
182
|
+
defineColor(
|
|
183
|
+
'yellow',
|
|
184
|
+
[46, 62],
|
|
185
|
+
[
|
|
186
|
+
[25, 100],
|
|
187
|
+
[40, 94],
|
|
188
|
+
[50, 89],
|
|
189
|
+
[60, 86],
|
|
190
|
+
[70, 84],
|
|
191
|
+
[80, 82],
|
|
192
|
+
[90, 80],
|
|
193
|
+
[100, 75],
|
|
194
|
+
]
|
|
195
|
+
);
|
|
196
|
+
defineColor(
|
|
197
|
+
'green',
|
|
198
|
+
[62, 178],
|
|
199
|
+
[
|
|
200
|
+
[30, 100],
|
|
201
|
+
[40, 90],
|
|
202
|
+
[50, 85],
|
|
203
|
+
[60, 81],
|
|
204
|
+
[70, 74],
|
|
205
|
+
[80, 64],
|
|
206
|
+
[90, 50],
|
|
207
|
+
[100, 40],
|
|
208
|
+
]
|
|
209
|
+
);
|
|
210
|
+
defineColor(
|
|
211
|
+
'blue',
|
|
212
|
+
[178, 257],
|
|
213
|
+
[
|
|
214
|
+
[20, 100],
|
|
215
|
+
[30, 86],
|
|
216
|
+
[40, 80],
|
|
217
|
+
[50, 74],
|
|
218
|
+
[60, 60],
|
|
219
|
+
[70, 52],
|
|
220
|
+
[80, 44],
|
|
221
|
+
[90, 39],
|
|
222
|
+
[100, 35],
|
|
223
|
+
]
|
|
224
|
+
);
|
|
225
|
+
defineColor(
|
|
226
|
+
'purple',
|
|
227
|
+
[257, 282],
|
|
228
|
+
[
|
|
229
|
+
[20, 100],
|
|
230
|
+
[30, 87],
|
|
231
|
+
[40, 79],
|
|
232
|
+
[50, 70],
|
|
233
|
+
[60, 65],
|
|
234
|
+
[70, 59],
|
|
235
|
+
[80, 52],
|
|
236
|
+
[90, 45],
|
|
237
|
+
[100, 42],
|
|
238
|
+
]
|
|
239
|
+
);
|
|
240
|
+
defineColor(
|
|
241
|
+
'pink',
|
|
242
|
+
[282, 334],
|
|
243
|
+
[
|
|
244
|
+
[20, 100],
|
|
245
|
+
[30, 90],
|
|
246
|
+
[40, 86],
|
|
247
|
+
[60, 84],
|
|
248
|
+
[80, 80],
|
|
249
|
+
[90, 75],
|
|
250
|
+
[100, 73],
|
|
251
|
+
]
|
|
252
|
+
);
|
|
150
253
|
|
|
151
254
|
return dict;
|
|
152
255
|
}
|
|
153
256
|
|
|
154
|
-
|
|
155
257
|
function getColorInfo(hue: number): ColorInfo {
|
|
156
258
|
hue = mod(hue, 360);
|
|
157
259
|
if (hue >= 334) hue -= 360;
|