@js-draw/math 1.0.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE +21 -0
- package/dist/cjs/Color4.d.ts +40 -0
- package/dist/cjs/Color4.js +102 -0
- package/dist/cjs/Color4.test.d.ts +1 -0
- package/dist/cjs/Mat33.test.d.ts +1 -0
- package/dist/cjs/Vec2.test.d.ts +1 -0
- package/dist/cjs/Vec3.test.d.ts +1 -0
- package/dist/cjs/polynomial/solveQuadratic.test.d.ts +1 -0
- package/dist/cjs/rounding.test.d.ts +1 -0
- package/dist/cjs/shapes/LineSegment2.test.d.ts +1 -0
- package/dist/cjs/shapes/Path.fromString.test.d.ts +1 -0
- package/dist/cjs/shapes/Path.test.d.ts +1 -0
- package/dist/cjs/shapes/Path.toString.test.d.ts +1 -0
- package/dist/cjs/shapes/QuadraticBezier.test.d.ts +1 -0
- package/dist/cjs/shapes/Rect2.test.d.ts +1 -0
- package/dist/cjs/shapes/Triangle.test.d.ts +1 -0
- package/dist/mjs/Color4.d.ts +40 -0
- package/dist/mjs/Color4.mjs +102 -0
- package/dist/mjs/Color4.test.d.ts +1 -0
- package/dist/mjs/Mat33.test.d.ts +1 -0
- package/dist/mjs/Vec2.test.d.ts +1 -0
- package/dist/mjs/Vec3.test.d.ts +1 -0
- package/dist/mjs/polynomial/solveQuadratic.test.d.ts +1 -0
- package/dist/mjs/rounding.test.d.ts +1 -0
- package/dist/mjs/shapes/LineSegment2.test.d.ts +1 -0
- package/dist/mjs/shapes/Path.fromString.test.d.ts +1 -0
- package/dist/mjs/shapes/Path.test.d.ts +1 -0
- package/dist/mjs/shapes/Path.toString.test.d.ts +1 -0
- package/dist/mjs/shapes/QuadraticBezier.test.d.ts +1 -0
- package/dist/mjs/shapes/Rect2.test.d.ts +1 -0
- package/dist/mjs/shapes/Triangle.test.d.ts +1 -0
- package/dist-test/test_imports/package-lock.json +13 -0
- package/dist-test/test_imports/package.json +12 -0
- package/dist-test/test_imports/test-imports.js +15 -0
- package/dist-test/test_imports/test-require.cjs +15 -0
- package/package.json +4 -3
- package/src/Color4.test.ts +94 -0
- package/src/Color4.ts +430 -0
- package/src/Mat33.test.ts +244 -0
- package/src/Mat33.ts +450 -0
- package/src/Vec2.test.ts +30 -0
- package/src/Vec2.ts +49 -0
- package/src/Vec3.test.ts +51 -0
- package/src/Vec3.ts +245 -0
- package/src/lib.ts +42 -0
- package/src/polynomial/solveQuadratic.test.ts +39 -0
- package/src/polynomial/solveQuadratic.ts +43 -0
- package/src/rounding.test.ts +65 -0
- package/src/rounding.ts +167 -0
- package/src/shapes/Abstract2DShape.ts +63 -0
- package/src/shapes/BezierJSWrapper.ts +93 -0
- package/src/shapes/CubicBezier.ts +35 -0
- package/src/shapes/LineSegment2.test.ts +99 -0
- package/src/shapes/LineSegment2.ts +232 -0
- package/src/shapes/Path.fromString.test.ts +223 -0
- package/src/shapes/Path.test.ts +309 -0
- package/src/shapes/Path.toString.test.ts +77 -0
- package/src/shapes/Path.ts +963 -0
- package/src/shapes/PointShape2D.ts +33 -0
- package/src/shapes/QuadraticBezier.test.ts +31 -0
- package/src/shapes/QuadraticBezier.ts +142 -0
- package/src/shapes/Rect2.test.ts +209 -0
- package/src/shapes/Rect2.ts +346 -0
- package/src/shapes/Triangle.test.ts +61 -0
- package/src/shapes/Triangle.ts +139 -0
package/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2022 Henry Heino
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
package/dist/cjs/Color4.d.ts
CHANGED
@@ -46,6 +46,25 @@ export default class Color4 {
|
|
46
46
|
* ```
|
47
47
|
*/
|
48
48
|
mix(other: Color4, fractionTo: number): Color4;
|
49
|
+
/**
|
50
|
+
* Ignoring this color's alpha component, returns a vector with components,
|
51
|
+
* $$
|
52
|
+
* \begin{pmatrix} \colorbox{#F44}{\tt r} \\ \colorbox{#4F4}{\tt g} \\ \colorbox{#44F}{\tt b} \end{pmatrix}
|
53
|
+
* $$
|
54
|
+
*/
|
55
|
+
get rgb(): Vec3;
|
56
|
+
/**
|
57
|
+
* Returns the [relative luminance](https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef)
|
58
|
+
* of this color in the sRGB color space.
|
59
|
+
*
|
60
|
+
* Ignores the alpha component.
|
61
|
+
*/
|
62
|
+
relativeLuminance(): number;
|
63
|
+
/**
|
64
|
+
* Returns the [contrast ratio](https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef)
|
65
|
+
* between `colorA` and `colorB`.
|
66
|
+
*/
|
67
|
+
static contrastRatio(colorA: Color4, colorB: Color4): number;
|
49
68
|
/**
|
50
69
|
* @returns the component-wise average of `colors`, or `Color4.transparent` if `colors` is empty.
|
51
70
|
*/
|
@@ -57,6 +76,27 @@ export default class Color4 {
|
|
57
76
|
* The resultant hue is represented in radians and is thus in $[0, 2\pi]$.
|
58
77
|
*/
|
59
78
|
asHSV(): Vec3;
|
79
|
+
/**
|
80
|
+
* Creates a new `Color4` from a representation [in $HSV$](https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB).
|
81
|
+
*
|
82
|
+
* [Algorithm](https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB).
|
83
|
+
*
|
84
|
+
* Note that hue must be given **in radians**. While non-standard, this is consistent with
|
85
|
+
* {@link asHSV}.
|
86
|
+
*
|
87
|
+
* `hue` and `value` should range from 0 to 1.
|
88
|
+
*
|
89
|
+
* @param hue $H \in [0, 2\pi]$
|
90
|
+
* @param saturation $S_V \in [0, 1]$
|
91
|
+
* @param value $V \in [0, 1]$
|
92
|
+
*/
|
93
|
+
static fromHSV(hue: number, saturation: number, value: number): Color4;
|
94
|
+
/**
|
95
|
+
* Equivalent to `ofRGB(rgb.x, rgb.y, rgb.z)`.
|
96
|
+
*
|
97
|
+
* All components should be in the range `[0, 1]` (0 to 1 inclusive).
|
98
|
+
*/
|
99
|
+
static fromRGBVector(rgb: Vec3, alpha?: number): Color4;
|
60
100
|
private hexString;
|
61
101
|
/**
|
62
102
|
* @returns a hexadecimal color string representation of `this`, in the form `#rrggbbaa`.
|
package/dist/cjs/Color4.js
CHANGED
@@ -144,6 +144,49 @@ class Color4 {
|
|
144
144
|
const fractionOfThis = 1 - fractionTo;
|
145
145
|
return new Color4(this.r * fractionOfThis + other.r * fractionTo, this.g * fractionOfThis + other.g * fractionTo, this.b * fractionOfThis + other.b * fractionTo, this.a * fractionOfThis + other.a * fractionTo);
|
146
146
|
}
|
147
|
+
/**
|
148
|
+
* Ignoring this color's alpha component, returns a vector with components,
|
149
|
+
* $$
|
150
|
+
* \begin{pmatrix} \colorbox{#F44}{\tt r} \\ \colorbox{#4F4}{\tt g} \\ \colorbox{#44F}{\tt b} \end{pmatrix}
|
151
|
+
* $$
|
152
|
+
*/
|
153
|
+
get rgb() {
|
154
|
+
return Vec3_1.default.of(this.r, this.g, this.b);
|
155
|
+
}
|
156
|
+
/**
|
157
|
+
* Returns the [relative luminance](https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef)
|
158
|
+
* of this color in the sRGB color space.
|
159
|
+
*
|
160
|
+
* Ignores the alpha component.
|
161
|
+
*/
|
162
|
+
relativeLuminance() {
|
163
|
+
// References:
|
164
|
+
// - https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
|
165
|
+
// - https://stackoverflow.com/a/9733420
|
166
|
+
// Normalize the components, as per above
|
167
|
+
const components = [this.r, this.g, this.b].map(component => {
|
168
|
+
if (component < 0.03928) {
|
169
|
+
return component / 12.92;
|
170
|
+
}
|
171
|
+
else {
|
172
|
+
return Math.pow((component + 0.055) / 1.055, 2.4);
|
173
|
+
}
|
174
|
+
});
|
175
|
+
// From w3.org,
|
176
|
+
// > For the sRGB colorspace, the relative luminance of a color is
|
177
|
+
// > defined as L = 0.2126 * R + 0.7152 * G + 0.0722 * B
|
178
|
+
// where R, G, B are defined in components above.
|
179
|
+
return 0.2126 * components[0] + 0.7152 * components[1] + 0.0722 * components[2];
|
180
|
+
}
|
181
|
+
/**
|
182
|
+
* Returns the [contrast ratio](https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef)
|
183
|
+
* between `colorA` and `colorB`.
|
184
|
+
*/
|
185
|
+
static contrastRatio(colorA, colorB) {
|
186
|
+
const L1 = colorA.relativeLuminance();
|
187
|
+
const L2 = colorB.relativeLuminance();
|
188
|
+
return (Math.max(L1, L2) + 0.05) / (Math.min(L1, L2) + 0.05);
|
189
|
+
}
|
147
190
|
/**
|
148
191
|
* @returns the component-wise average of `colors`, or `Color4.transparent` if `colors` is empty.
|
149
192
|
*/
|
@@ -229,6 +272,65 @@ class Color4 {
|
|
229
272
|
const saturation = value > 0 ? chroma / value : 0;
|
230
273
|
return Vec3_1.default.of(hue, saturation, value);
|
231
274
|
}
|
275
|
+
/**
|
276
|
+
* Creates a new `Color4` from a representation [in $HSV$](https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB).
|
277
|
+
*
|
278
|
+
* [Algorithm](https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB).
|
279
|
+
*
|
280
|
+
* Note that hue must be given **in radians**. While non-standard, this is consistent with
|
281
|
+
* {@link asHSV}.
|
282
|
+
*
|
283
|
+
* `hue` and `value` should range from 0 to 1.
|
284
|
+
*
|
285
|
+
* @param hue $H \in [0, 2\pi]$
|
286
|
+
* @param saturation $S_V \in [0, 1]$
|
287
|
+
* @param value $V \in [0, 1]$
|
288
|
+
*/
|
289
|
+
static fromHSV(hue, saturation, value) {
|
290
|
+
if (hue < 0) {
|
291
|
+
hue += Math.PI * 2;
|
292
|
+
}
|
293
|
+
hue %= Math.PI * 2;
|
294
|
+
// Clamp value and saturation to [0, 1]
|
295
|
+
value = Math.max(0, Math.min(1, value));
|
296
|
+
saturation = Math.max(0, Math.min(1, saturation));
|
297
|
+
// Formula from https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB
|
298
|
+
// Saturation can be thought of as scaled chroma. Unapply the scaling.
|
299
|
+
// See https://en.wikipedia.org/wiki/HSL_and_HSV#Saturation
|
300
|
+
const chroma = value * saturation;
|
301
|
+
// Determines which edge of the projected color cube
|
302
|
+
const huePrime = hue / (Math.PI / 3);
|
303
|
+
const secondLargestComponent = chroma * (1 - Math.abs((huePrime % 2) - 1));
|
304
|
+
let rgb;
|
305
|
+
if (huePrime < 1) {
|
306
|
+
rgb = [chroma, secondLargestComponent, 0];
|
307
|
+
}
|
308
|
+
else if (huePrime < 2) {
|
309
|
+
rgb = [secondLargestComponent, chroma, 0];
|
310
|
+
}
|
311
|
+
else if (huePrime < 3) {
|
312
|
+
rgb = [0, chroma, secondLargestComponent];
|
313
|
+
}
|
314
|
+
else if (huePrime < 4) {
|
315
|
+
rgb = [0, secondLargestComponent, chroma];
|
316
|
+
}
|
317
|
+
else if (huePrime < 5) {
|
318
|
+
rgb = [secondLargestComponent, 0, chroma];
|
319
|
+
}
|
320
|
+
else {
|
321
|
+
rgb = [chroma, 0, secondLargestComponent];
|
322
|
+
}
|
323
|
+
const adjustment = value - chroma;
|
324
|
+
return Color4.ofRGB(rgb[0] + adjustment, rgb[1] + adjustment, rgb[2] + adjustment);
|
325
|
+
}
|
326
|
+
/**
|
327
|
+
* Equivalent to `ofRGB(rgb.x, rgb.y, rgb.z)`.
|
328
|
+
*
|
329
|
+
* All components should be in the range `[0, 1]` (0 to 1 inclusive).
|
330
|
+
*/
|
331
|
+
static fromRGBVector(rgb, alpha) {
|
332
|
+
return Color4.ofRGBA(rgb.x, rgb.y, rgb.z, alpha ?? 1);
|
333
|
+
}
|
232
334
|
/**
|
233
335
|
* @returns a hexadecimal color string representation of `this`, in the form `#rrggbbaa`.
|
234
336
|
*
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
package/dist/mjs/Color4.d.ts
CHANGED
@@ -46,6 +46,25 @@ export default class Color4 {
|
|
46
46
|
* ```
|
47
47
|
*/
|
48
48
|
mix(other: Color4, fractionTo: number): Color4;
|
49
|
+
/**
|
50
|
+
* Ignoring this color's alpha component, returns a vector with components,
|
51
|
+
* $$
|
52
|
+
* \begin{pmatrix} \colorbox{#F44}{\tt r} \\ \colorbox{#4F4}{\tt g} \\ \colorbox{#44F}{\tt b} \end{pmatrix}
|
53
|
+
* $$
|
54
|
+
*/
|
55
|
+
get rgb(): Vec3;
|
56
|
+
/**
|
57
|
+
* Returns the [relative luminance](https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef)
|
58
|
+
* of this color in the sRGB color space.
|
59
|
+
*
|
60
|
+
* Ignores the alpha component.
|
61
|
+
*/
|
62
|
+
relativeLuminance(): number;
|
63
|
+
/**
|
64
|
+
* Returns the [contrast ratio](https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef)
|
65
|
+
* between `colorA` and `colorB`.
|
66
|
+
*/
|
67
|
+
static contrastRatio(colorA: Color4, colorB: Color4): number;
|
49
68
|
/**
|
50
69
|
* @returns the component-wise average of `colors`, or `Color4.transparent` if `colors` is empty.
|
51
70
|
*/
|
@@ -57,6 +76,27 @@ export default class Color4 {
|
|
57
76
|
* The resultant hue is represented in radians and is thus in $[0, 2\pi]$.
|
58
77
|
*/
|
59
78
|
asHSV(): Vec3;
|
79
|
+
/**
|
80
|
+
* Creates a new `Color4` from a representation [in $HSV$](https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB).
|
81
|
+
*
|
82
|
+
* [Algorithm](https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB).
|
83
|
+
*
|
84
|
+
* Note that hue must be given **in radians**. While non-standard, this is consistent with
|
85
|
+
* {@link asHSV}.
|
86
|
+
*
|
87
|
+
* `hue` and `value` should range from 0 to 1.
|
88
|
+
*
|
89
|
+
* @param hue $H \in [0, 2\pi]$
|
90
|
+
* @param saturation $S_V \in [0, 1]$
|
91
|
+
* @param value $V \in [0, 1]$
|
92
|
+
*/
|
93
|
+
static fromHSV(hue: number, saturation: number, value: number): Color4;
|
94
|
+
/**
|
95
|
+
* Equivalent to `ofRGB(rgb.x, rgb.y, rgb.z)`.
|
96
|
+
*
|
97
|
+
* All components should be in the range `[0, 1]` (0 to 1 inclusive).
|
98
|
+
*/
|
99
|
+
static fromRGBVector(rgb: Vec3, alpha?: number): Color4;
|
60
100
|
private hexString;
|
61
101
|
/**
|
62
102
|
* @returns a hexadecimal color string representation of `this`, in the form `#rrggbbaa`.
|
package/dist/mjs/Color4.mjs
CHANGED
@@ -138,6 +138,49 @@ class Color4 {
|
|
138
138
|
const fractionOfThis = 1 - fractionTo;
|
139
139
|
return new Color4(this.r * fractionOfThis + other.r * fractionTo, this.g * fractionOfThis + other.g * fractionTo, this.b * fractionOfThis + other.b * fractionTo, this.a * fractionOfThis + other.a * fractionTo);
|
140
140
|
}
|
141
|
+
/**
|
142
|
+
* Ignoring this color's alpha component, returns a vector with components,
|
143
|
+
* $$
|
144
|
+
* \begin{pmatrix} \colorbox{#F44}{\tt r} \\ \colorbox{#4F4}{\tt g} \\ \colorbox{#44F}{\tt b} \end{pmatrix}
|
145
|
+
* $$
|
146
|
+
*/
|
147
|
+
get rgb() {
|
148
|
+
return Vec3.of(this.r, this.g, this.b);
|
149
|
+
}
|
150
|
+
/**
|
151
|
+
* Returns the [relative luminance](https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef)
|
152
|
+
* of this color in the sRGB color space.
|
153
|
+
*
|
154
|
+
* Ignores the alpha component.
|
155
|
+
*/
|
156
|
+
relativeLuminance() {
|
157
|
+
// References:
|
158
|
+
// - https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
|
159
|
+
// - https://stackoverflow.com/a/9733420
|
160
|
+
// Normalize the components, as per above
|
161
|
+
const components = [this.r, this.g, this.b].map(component => {
|
162
|
+
if (component < 0.03928) {
|
163
|
+
return component / 12.92;
|
164
|
+
}
|
165
|
+
else {
|
166
|
+
return Math.pow((component + 0.055) / 1.055, 2.4);
|
167
|
+
}
|
168
|
+
});
|
169
|
+
// From w3.org,
|
170
|
+
// > For the sRGB colorspace, the relative luminance of a color is
|
171
|
+
// > defined as L = 0.2126 * R + 0.7152 * G + 0.0722 * B
|
172
|
+
// where R, G, B are defined in components above.
|
173
|
+
return 0.2126 * components[0] + 0.7152 * components[1] + 0.0722 * components[2];
|
174
|
+
}
|
175
|
+
/**
|
176
|
+
* Returns the [contrast ratio](https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef)
|
177
|
+
* between `colorA` and `colorB`.
|
178
|
+
*/
|
179
|
+
static contrastRatio(colorA, colorB) {
|
180
|
+
const L1 = colorA.relativeLuminance();
|
181
|
+
const L2 = colorB.relativeLuminance();
|
182
|
+
return (Math.max(L1, L2) + 0.05) / (Math.min(L1, L2) + 0.05);
|
183
|
+
}
|
141
184
|
/**
|
142
185
|
* @returns the component-wise average of `colors`, or `Color4.transparent` if `colors` is empty.
|
143
186
|
*/
|
@@ -223,6 +266,65 @@ class Color4 {
|
|
223
266
|
const saturation = value > 0 ? chroma / value : 0;
|
224
267
|
return Vec3.of(hue, saturation, value);
|
225
268
|
}
|
269
|
+
/**
|
270
|
+
* Creates a new `Color4` from a representation [in $HSV$](https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB).
|
271
|
+
*
|
272
|
+
* [Algorithm](https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB).
|
273
|
+
*
|
274
|
+
* Note that hue must be given **in radians**. While non-standard, this is consistent with
|
275
|
+
* {@link asHSV}.
|
276
|
+
*
|
277
|
+
* `hue` and `value` should range from 0 to 1.
|
278
|
+
*
|
279
|
+
* @param hue $H \in [0, 2\pi]$
|
280
|
+
* @param saturation $S_V \in [0, 1]$
|
281
|
+
* @param value $V \in [0, 1]$
|
282
|
+
*/
|
283
|
+
static fromHSV(hue, saturation, value) {
|
284
|
+
if (hue < 0) {
|
285
|
+
hue += Math.PI * 2;
|
286
|
+
}
|
287
|
+
hue %= Math.PI * 2;
|
288
|
+
// Clamp value and saturation to [0, 1]
|
289
|
+
value = Math.max(0, Math.min(1, value));
|
290
|
+
saturation = Math.max(0, Math.min(1, saturation));
|
291
|
+
// Formula from https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB
|
292
|
+
// Saturation can be thought of as scaled chroma. Unapply the scaling.
|
293
|
+
// See https://en.wikipedia.org/wiki/HSL_and_HSV#Saturation
|
294
|
+
const chroma = value * saturation;
|
295
|
+
// Determines which edge of the projected color cube
|
296
|
+
const huePrime = hue / (Math.PI / 3);
|
297
|
+
const secondLargestComponent = chroma * (1 - Math.abs((huePrime % 2) - 1));
|
298
|
+
let rgb;
|
299
|
+
if (huePrime < 1) {
|
300
|
+
rgb = [chroma, secondLargestComponent, 0];
|
301
|
+
}
|
302
|
+
else if (huePrime < 2) {
|
303
|
+
rgb = [secondLargestComponent, chroma, 0];
|
304
|
+
}
|
305
|
+
else if (huePrime < 3) {
|
306
|
+
rgb = [0, chroma, secondLargestComponent];
|
307
|
+
}
|
308
|
+
else if (huePrime < 4) {
|
309
|
+
rgb = [0, secondLargestComponent, chroma];
|
310
|
+
}
|
311
|
+
else if (huePrime < 5) {
|
312
|
+
rgb = [secondLargestComponent, 0, chroma];
|
313
|
+
}
|
314
|
+
else {
|
315
|
+
rgb = [chroma, 0, secondLargestComponent];
|
316
|
+
}
|
317
|
+
const adjustment = value - chroma;
|
318
|
+
return Color4.ofRGB(rgb[0] + adjustment, rgb[1] + adjustment, rgb[2] + adjustment);
|
319
|
+
}
|
320
|
+
/**
|
321
|
+
* Equivalent to `ofRGB(rgb.x, rgb.y, rgb.z)`.
|
322
|
+
*
|
323
|
+
* All components should be in the range `[0, 1]` (0 to 1 inclusive).
|
324
|
+
*/
|
325
|
+
static fromRGBVector(rgb, alpha) {
|
326
|
+
return Color4.ofRGBA(rgb.x, rgb.y, rgb.z, alpha ?? 1);
|
327
|
+
}
|
226
328
|
/**
|
227
329
|
* @returns a hexadecimal color string representation of `this`, in the form `#rrggbbaa`.
|
228
330
|
*
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,12 @@
|
|
1
|
+
{
|
2
|
+
"name": "js-draw-math-test-imports",
|
3
|
+
"version": "0.0.1",
|
4
|
+
"description": "Test module and CommonJS imports",
|
5
|
+
"author": "Henry Heino",
|
6
|
+
"license": "MIT",
|
7
|
+
"private": true,
|
8
|
+
"type": "module",
|
9
|
+
"scripts": {
|
10
|
+
"test": "node test-imports.js && node test-require.cjs"
|
11
|
+
}
|
12
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
console.log('Testing imports...');
|
2
|
+
|
3
|
+
import { Vec2, Color4, Mat33 } from '@js-draw/math';
|
4
|
+
|
5
|
+
if (Vec2.of(1, 1).x !== 1) {
|
6
|
+
throw new Error('Failed to import module Vec2');
|
7
|
+
}
|
8
|
+
|
9
|
+
if (!Mat33.identity) {
|
10
|
+
throw new Error('Failed to import Mat33 via CommonJS');
|
11
|
+
}
|
12
|
+
|
13
|
+
if (!Color4.red) {
|
14
|
+
throw new Error('Failed to import Color4 from js-draw');
|
15
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
console.log('Testing require()...');
|
2
|
+
|
3
|
+
const { Vec2, Color4, Mat33 } = require('@js-draw/math');
|
4
|
+
|
5
|
+
if (Vec2.of(1, 1).x !== 1) {
|
6
|
+
throw new Error('Failed to import module Vec2');
|
7
|
+
}
|
8
|
+
|
9
|
+
if (!Mat33.identity) {
|
10
|
+
throw new Error('Failed to import Mat33 via CommonJS');
|
11
|
+
}
|
12
|
+
|
13
|
+
if (!Color4.red) {
|
14
|
+
throw new Error('Failed to import Color4 from js-draw');
|
15
|
+
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@js-draw/math",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.2.0",
|
4
4
|
"description": "A math library for js-draw. ",
|
5
5
|
"types": "./dist/mjs/lib.d.ts",
|
6
6
|
"main": "./dist/cjs/lib.js",
|
@@ -28,7 +28,7 @@
|
|
28
28
|
"bezier-js": "6.1.3"
|
29
29
|
},
|
30
30
|
"devDependencies": {
|
31
|
-
"@js-draw/build-tool": "^1.0.
|
31
|
+
"@js-draw/build-tool": "^1.0.2",
|
32
32
|
"@types/bezier-js": "4.1.0",
|
33
33
|
"@types/jest": "29.5.3",
|
34
34
|
"@types/jsdom": "21.1.1"
|
@@ -44,5 +44,6 @@
|
|
44
44
|
"freehand",
|
45
45
|
"svg",
|
46
46
|
"math"
|
47
|
-
]
|
47
|
+
],
|
48
|
+
"gitHead": "3e77c7d833ecdc13bcb57e905280ba547629680a"
|
48
49
|
}
|
@@ -0,0 +1,94 @@
|
|
1
|
+
import Color4 from './Color4';
|
2
|
+
import Vec3 from './Vec3';
|
3
|
+
|
4
|
+
describe('Color4', () => {
|
5
|
+
it('should convert to #RRGGBB-format hex strings (when no alpha)', () => {
|
6
|
+
expect(Color4.black.toHexString()).toBe('#000000');
|
7
|
+
expect(Color4.fromHex('#f0f').toHexString()).toBe('#f000f0');
|
8
|
+
});
|
9
|
+
|
10
|
+
it('should create #RRGGBBAA-format hex strings when there is an alpha component', () => {
|
11
|
+
expect(Color4.ofRGBA(1, 1, 1, 0.5).toHexString()).toBe('#ffffff80');
|
12
|
+
});
|
13
|
+
|
14
|
+
it('should parse rgb and rgba-format strings', () => {
|
15
|
+
expect(Color4.fromString('rgb(0, 0, 0)')).objEq(Color4.black);
|
16
|
+
expect(Color4.fromString('rgb ( 255, 0,\t 0)')).objEq(Color4.ofRGBA(1, 0, 0, 1));
|
17
|
+
expect(Color4.fromString('rgba ( 255, 0,\t 0, 0.5)')).objEq(Color4.ofRGBA(1, 0, 0, 0.5));
|
18
|
+
expect(Color4.fromString('rgba( 0, 0, 128, 0)')).objEq(Color4.ofRGBA(0, 0, 128/255, 0));
|
19
|
+
});
|
20
|
+
|
21
|
+
it('should parse transparent/none as completely transparent', () => {
|
22
|
+
expect(Color4.fromString('none')).toBe(Color4.transparent);
|
23
|
+
expect(Color4.fromString('transparent')).toBe(Color4.transparent);
|
24
|
+
});
|
25
|
+
|
26
|
+
it('should mix blue and red to get dark purple', () => {
|
27
|
+
expect(Color4.ofRGB(1, 0, 0).mix(Color4.ofRGB(0, 0, 1), 0.5)).objEq(Color4.ofRGB(0.5, 0, 0.5));
|
28
|
+
expect(Color4.ofRGB(1, 0, 0).mix(Color4.ofRGB(0, 0, 1), 0.1)).objEq(Color4.ofRGB(0.9, 0, 0.1));
|
29
|
+
});
|
30
|
+
|
31
|
+
it('should mix red and green to get yellow', () => {
|
32
|
+
expect(Color4.ofRGB(1, 0, 0).mix(Color4.ofRGB(0, 1, 0), 0.3)).objEq(
|
33
|
+
Color4.ofRGB(0.7, 0.3, 0)
|
34
|
+
);
|
35
|
+
});
|
36
|
+
|
37
|
+
it('should mix red with nothing and get red', () => {
|
38
|
+
expect(Color4.average([ Color4.red ])).objEq(Color4.red);
|
39
|
+
});
|
40
|
+
|
41
|
+
it('different colors should be different', () => {
|
42
|
+
expect(Color4.red.eq(Color4.red)).toBe(true);
|
43
|
+
expect(Color4.red.eq(Color4.green)).toBe(false);
|
44
|
+
expect(Color4.fromString('#ff000000').eq(Color4.transparent)).toBe(true);
|
45
|
+
});
|
46
|
+
|
47
|
+
it('should correctly convert to hsv', () => {
|
48
|
+
expect(Color4.red.asHSV()).objEq(Vec3.of(0, 1, 1));
|
49
|
+
expect(Color4.ofRGB(0.5, 0.5, 0.5).asHSV()).objEq(Vec3.of(0, 0, 0.5));
|
50
|
+
expect(Color4.ofRGB(0.5, 0.25, 0.5).asHSV()).objEq(Vec3.of(Math.PI * 5 / 3, 0.5, 0.5), 0.1);
|
51
|
+
});
|
52
|
+
|
53
|
+
it('fromHSV(color.asHSV) should return the original color', () => {
|
54
|
+
const testColors = [
|
55
|
+
Color4.red, Color4.green, Color4.blue,
|
56
|
+
Color4.white, Color4.black,
|
57
|
+
];
|
58
|
+
|
59
|
+
const testWithColor = (color: Color4) => {
|
60
|
+
expect(Color4.fromHSV(...color.asHSV().asArray())).objEq(color);
|
61
|
+
};
|
62
|
+
|
63
|
+
for (const color of testColors) {
|
64
|
+
testWithColor(color);
|
65
|
+
}
|
66
|
+
|
67
|
+
for (let i = 0; i <= 6; i++) {
|
68
|
+
testWithColor(Color4.fromHSV(i * Math.PI / 7, 0.5, 0.5));
|
69
|
+
testWithColor(Color4.fromHSV(i * Math.PI / 6, 0.5, 0.5));
|
70
|
+
}
|
71
|
+
});
|
72
|
+
|
73
|
+
it('.rgb should return a 3-component vector', () => {
|
74
|
+
expect(Color4.red.rgb).objEq(Vec3.of(1, 0, 0));
|
75
|
+
expect(Color4.green.rgb).objEq(Vec3.of(0, 1, 0));
|
76
|
+
expect(Color4.blue.rgb).objEq(Vec3.of(0, 0, 1));
|
77
|
+
});
|
78
|
+
|
79
|
+
it('should return correct contrast ratios', () => {
|
80
|
+
// Expected values from https://webaim.org/resources/contrastchecker/
|
81
|
+
const testCases: [ Color4, Color4, number ][] = [
|
82
|
+
[ Color4.white, Color4.black, 21 ],
|
83
|
+
[ Color4.fromHex('#FF0000'), Color4.black, 5.25 ],
|
84
|
+
[ Color4.fromHex('#FF0000'), Color4.fromHex('#0000FF'), 2.14 ],
|
85
|
+
[ Color4.fromHex('#300000'), Color4.fromHex('#003000'), 1.26 ],
|
86
|
+
[ Color4.fromHex('#300000'), Color4.fromHex('#003000'), 1.26 ],
|
87
|
+
[ Color4.fromHex('#D60000'), Color4.fromHex('#003000'), 2.71 ],
|
88
|
+
];
|
89
|
+
|
90
|
+
for (const [ colorA, colorB, expectedContrast ] of testCases) {
|
91
|
+
expect(Color4.contrastRatio(colorA, colorB)).toBeCloseTo(expectedContrast, 1);
|
92
|
+
}
|
93
|
+
});
|
94
|
+
});
|