@js-draw/math 1.0.0 → 1.2.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.
- 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
|
+
});
|