@js-draw/math 1.0.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/Color4.test.d.ts +1 -0
  3. package/dist/cjs/Mat33.test.d.ts +1 -0
  4. package/dist/cjs/Vec2.test.d.ts +1 -0
  5. package/dist/cjs/Vec3.test.d.ts +1 -0
  6. package/dist/cjs/polynomial/solveQuadratic.test.d.ts +1 -0
  7. package/dist/cjs/rounding.test.d.ts +1 -0
  8. package/dist/cjs/shapes/LineSegment2.test.d.ts +1 -0
  9. package/dist/cjs/shapes/Path.fromString.test.d.ts +1 -0
  10. package/dist/cjs/shapes/Path.test.d.ts +1 -0
  11. package/dist/cjs/shapes/Path.toString.test.d.ts +1 -0
  12. package/dist/cjs/shapes/QuadraticBezier.test.d.ts +1 -0
  13. package/dist/cjs/shapes/Rect2.test.d.ts +1 -0
  14. package/dist/cjs/shapes/Triangle.test.d.ts +1 -0
  15. package/dist/mjs/Color4.test.d.ts +1 -0
  16. package/dist/mjs/Mat33.test.d.ts +1 -0
  17. package/dist/mjs/Vec2.test.d.ts +1 -0
  18. package/dist/mjs/Vec3.test.d.ts +1 -0
  19. package/dist/mjs/polynomial/solveQuadratic.test.d.ts +1 -0
  20. package/dist/mjs/rounding.test.d.ts +1 -0
  21. package/dist/mjs/shapes/LineSegment2.test.d.ts +1 -0
  22. package/dist/mjs/shapes/Path.fromString.test.d.ts +1 -0
  23. package/dist/mjs/shapes/Path.test.d.ts +1 -0
  24. package/dist/mjs/shapes/Path.toString.test.d.ts +1 -0
  25. package/dist/mjs/shapes/QuadraticBezier.test.d.ts +1 -0
  26. package/dist/mjs/shapes/Rect2.test.d.ts +1 -0
  27. package/dist/mjs/shapes/Triangle.test.d.ts +1 -0
  28. package/dist-test/test_imports/package-lock.json +13 -0
  29. package/dist-test/test_imports/package.json +12 -0
  30. package/dist-test/test_imports/test-imports.js +15 -0
  31. package/dist-test/test_imports/test-require.cjs +15 -0
  32. package/package.json +4 -3
  33. package/src/Color4.test.ts +52 -0
  34. package/src/Color4.ts +318 -0
  35. package/src/Mat33.test.ts +244 -0
  36. package/src/Mat33.ts +450 -0
  37. package/src/Vec2.test.ts +30 -0
  38. package/src/Vec2.ts +49 -0
  39. package/src/Vec3.test.ts +51 -0
  40. package/src/Vec3.ts +245 -0
  41. package/src/lib.ts +42 -0
  42. package/src/polynomial/solveQuadratic.test.ts +39 -0
  43. package/src/polynomial/solveQuadratic.ts +43 -0
  44. package/src/rounding.test.ts +65 -0
  45. package/src/rounding.ts +167 -0
  46. package/src/shapes/Abstract2DShape.ts +63 -0
  47. package/src/shapes/BezierJSWrapper.ts +93 -0
  48. package/src/shapes/CubicBezier.ts +35 -0
  49. package/src/shapes/LineSegment2.test.ts +99 -0
  50. package/src/shapes/LineSegment2.ts +232 -0
  51. package/src/shapes/Path.fromString.test.ts +223 -0
  52. package/src/shapes/Path.test.ts +309 -0
  53. package/src/shapes/Path.toString.test.ts +77 -0
  54. package/src/shapes/Path.ts +963 -0
  55. package/src/shapes/PointShape2D.ts +33 -0
  56. package/src/shapes/QuadraticBezier.test.ts +31 -0
  57. package/src/shapes/QuadraticBezier.ts +142 -0
  58. package/src/shapes/Rect2.test.ts +209 -0
  59. package/src/shapes/Rect2.ts +346 -0
  60. package/src/shapes/Triangle.test.ts +61 -0
  61. 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.
@@ -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 @@
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,13 @@
1
+ {
2
+ "name": "js-draw-math-test-imports",
3
+ "version": "0.0.1",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "js-draw-math-test-imports",
9
+ "version": "0.0.1",
10
+ "license": "MIT"
11
+ }
12
+ }
13
+ }
@@ -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.0.0",
3
+ "version": "1.0.2",
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.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": "f5a92284625b49b2ef6541bdafce1bd926c10441"
48
49
  }
@@ -0,0 +1,52 @@
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
+ });
package/src/Color4.ts ADDED
@@ -0,0 +1,318 @@
1
+ import Vec3 from './Vec3';
2
+
3
+ /**
4
+ * Represents a color.
5
+ *
6
+ * @example
7
+ * ```ts,runnable,console
8
+ * import { Color4 } from '@js-draw/math';
9
+ *
10
+ * console.log('Red:', Color4.fromString('#f00'));
11
+ * console.log('Also red:', Color4.ofRGB(1, 0, 0), Color4.red);
12
+ * console.log('Mixing red and blue:', Color4.red.mix(Color4.blue, 0.5));
13
+ * console.log('To string:', Color4.orange.toHexString());
14
+ * ```
15
+ */
16
+ export default class Color4 {
17
+ private constructor(
18
+ /** Red component. Should be in the range [0, 1]. */
19
+ public readonly r: number,
20
+
21
+ /** Green component. ${\tt g} \in [0, 1]$ */
22
+ public readonly g: number,
23
+
24
+ /** Blue component. ${\tt b} \in [0, 1]$ */
25
+ public readonly b: number,
26
+
27
+ /** Alpha/transparent component. ${\tt a} \in [0, 1]$. 0 = transparent */
28
+ public readonly a: number
29
+ ) {
30
+ }
31
+
32
+ /**
33
+ * Create a color from red, green, blue components. The color is fully opaque (`a = 1.0`).
34
+ *
35
+ * Each component should be in the range [0, 1].
36
+ */
37
+ public static ofRGB(red: number, green: number, blue: number): Color4 {
38
+ return Color4.ofRGBA(red, green, blue, 1.0);
39
+ }
40
+
41
+ public static ofRGBA(red: number, green: number, blue: number, alpha: number): Color4 {
42
+ red = Math.max(0, Math.min(red, 1));
43
+ green = Math.max(0, Math.min(green, 1));
44
+ blue = Math.max(0, Math.min(blue, 1));
45
+ alpha = Math.max(0, Math.min(alpha, 1));
46
+
47
+ return new Color4(red, green, blue, alpha);
48
+ }
49
+
50
+ public static fromHex(hexString: string): Color4 {
51
+ // Remove starting '#' (if present)
52
+ hexString = (hexString.match(/^[#]?(.*)$/) ?? [])[1];
53
+ hexString = hexString.toUpperCase();
54
+
55
+ if (!hexString.match(/^[0-9A-F]+$/)) {
56
+ throw new Error(`${hexString} is not in a valid format.`);
57
+ }
58
+
59
+ // RGBA or RGB
60
+ if (hexString.length === 3 || hexString.length === 4) {
61
+ // Each character is a component
62
+ const components = hexString.split('');
63
+
64
+ // Convert to RRGGBBAA or RRGGBB format
65
+ hexString = components.map(component => `${component}0`).join('');
66
+ }
67
+
68
+ if (hexString.length === 6) {
69
+ // Alpha component
70
+ hexString += 'FF';
71
+ }
72
+
73
+ const components: number[] = [];
74
+ for (let i = 2; i <= hexString.length; i += 2) {
75
+ const chunk = hexString.substring(i - 2, i);
76
+ components.push(parseInt(chunk, 16) / 255);
77
+ }
78
+
79
+ if (components.length !== 4) {
80
+ throw new Error(`Unable to parse ${hexString}: Wrong number of components.`);
81
+ }
82
+
83
+ return Color4.ofRGBA(components[0], components[1], components[2], components[3]);
84
+ }
85
+
86
+ /** Like fromHex, but can handle additional colors if an `HTMLCanvasElement` is available. */
87
+ public static fromString(text: string): Color4 {
88
+ if (text.startsWith('#')) {
89
+ return Color4.fromHex(text);
90
+ }
91
+
92
+ if (text === 'none' || text === 'transparent') {
93
+ return Color4.transparent;
94
+ }
95
+
96
+ // rgba?: Match both rgb and rgba strings.
97
+ // ([,0-9.]+): Match any string of only numeric, '.' and ',' characters.
98
+ const rgbRegex = /^rgba?\(([,0-9.]+)\)$/i;
99
+ const rgbMatch = text.replace(/\s*/g, '').match(rgbRegex);
100
+
101
+ if (rgbMatch) {
102
+ const componentsListStr = rgbMatch[1];
103
+ const componentsList = JSON.parse(`[ ${componentsListStr} ]`);
104
+
105
+ if (componentsList.length === 3) {
106
+ return Color4.ofRGB(
107
+ componentsList[0] / 255, componentsList[1] / 255, componentsList[2] / 255
108
+ );
109
+ } else if (componentsList.length === 4) {
110
+ return Color4.ofRGBA(
111
+ componentsList[0] / 255, componentsList[1] / 255, componentsList[2] / 255, componentsList[3]
112
+ );
113
+ } else {
114
+ throw new Error(`RGB string, ${text}, has wrong number of components: ${componentsList.length}`);
115
+ }
116
+ }
117
+
118
+ // Otherwise, try to use an HTMLCanvasElement to determine the color.
119
+ // Note: We may be unable to create an HTMLCanvasElement if running as a unit test.
120
+ const canvas = document.createElement('canvas');
121
+ canvas.width = 1;
122
+ canvas.height = 1;
123
+
124
+ const ctx = canvas.getContext('2d')!;
125
+ ctx.fillStyle = text;
126
+ ctx.fillRect(0, 0, 1, 1);
127
+
128
+ const data = ctx.getImageData(0, 0, 1, 1);
129
+ const red = data.data[0] / 255;
130
+ const green = data.data[1] / 255;
131
+ const blue = data.data[2] / 255;
132
+ const alpha = data.data[3] / 255;
133
+
134
+ return Color4.ofRGBA(red, green, blue, alpha);
135
+ }
136
+
137
+ /** @returns true if `this` and `other` are approximately equal. */
138
+ public eq(other: Color4|null|undefined): boolean {
139
+ if (other == null) {
140
+ return false;
141
+ }
142
+
143
+ // If both completely transparent,
144
+ if (this.a === 0 && other.a === 0) {
145
+ return true;
146
+ }
147
+
148
+ return this.toHexString() === other.toHexString();
149
+ }
150
+
151
+ /**
152
+ * If `fractionTo` is not in the range $[0, 1]$, it will be clamped to the nearest number
153
+ * in that range. For example, `a.mix(b, -1)` is equivalent to `a.mix(b, 0)`.
154
+ *
155
+ * @returns a color `fractionTo` of the way from this color to `other`.
156
+ *
157
+ * @example
158
+ * ```ts
159
+ * Color4.ofRGB(1, 0, 0).mix(Color4.ofRGB(0, 1, 0), 0.1) // -> Color4(0.9, 0.1, 0)
160
+ * ```
161
+ */
162
+ public mix(other: Color4, fractionTo: number): Color4 {
163
+ fractionTo = Math.min(Math.max(fractionTo, 0), 1);
164
+ const fractionOfThis = 1 - fractionTo;
165
+ return new Color4(
166
+ this.r * fractionOfThis + other.r * fractionTo,
167
+ this.g * fractionOfThis + other.g * fractionTo,
168
+ this.b * fractionOfThis + other.b * fractionTo,
169
+ this.a * fractionOfThis + other.a * fractionTo,
170
+ );
171
+ }
172
+
173
+ /**
174
+ * @returns the component-wise average of `colors`, or `Color4.transparent` if `colors` is empty.
175
+ */
176
+ public static average(colors: Color4[]) {
177
+ let averageA = 0;
178
+ let averageR = 0;
179
+ let averageG = 0;
180
+ let averageB = 0;
181
+
182
+ for (const color of colors) {
183
+ averageA += color.a;
184
+ averageR += color.r;
185
+ averageG += color.g;
186
+ averageB += color.b;
187
+ }
188
+
189
+ if (colors.length > 0) {
190
+ averageA /= colors.length;
191
+ averageR /= colors.length;
192
+ averageG /= colors.length;
193
+ averageB /= colors.length;
194
+ }
195
+
196
+ return new Color4(averageR, averageG, averageB, averageA);
197
+ }
198
+
199
+ /**
200
+ * Converts to (hue, saturation, value).
201
+ * See also https://en.wikipedia.org/wiki/HSL_and_HSV#General_approach
202
+ *
203
+ * The resultant hue is represented in radians and is thus in $[0, 2\pi]$.
204
+ */
205
+ public asHSV(): Vec3 {
206
+ // Ref: https://en.wikipedia.org/wiki/HSL_and_HSV#General_approach
207
+ //
208
+ // HUE:
209
+ // First, consider the unit cube. Rotate it such that one vertex is at the origin
210
+ // of a plane and its three neighboring vertices are equidistant from that plane:
211
+ //
212
+ // /\
213
+ // / | \
214
+ // 2 / 3 \ 1
215
+ // \ | /
216
+ // \ | /
217
+ // . \/ .
218
+ //
219
+ // .
220
+ //
221
+ // Let z be up and (x, y, 0) be in the plane.
222
+ //
223
+ // Label vectors 1,2,3 with R, G, and B, respectively. Let R's projection into the plane
224
+ // lie along the x axis.
225
+ //
226
+ // Because R is a unit vector and R, G, B are equidistant from the plane, they must
227
+ // form 30-60-90 triangles, which have side lengths proportional to (1, √3, 2)
228
+ //
229
+ // /|
230
+ // 1/ | (√3)/2
231
+ // / |
232
+ // 1/2
233
+ //
234
+ const minComponent = Math.min(this.r, this.g, this.b);
235
+ const maxComponent = Math.max(this.r, this.g, this.b);
236
+ const chroma = maxComponent - minComponent;
237
+
238
+ let hue;
239
+
240
+ // See https://en.wikipedia.org/wiki/HSL_and_HSV#General_approach
241
+ if (chroma === 0) {
242
+ hue = 0;
243
+ } else if (this.r >= this.g && this.r >= this.b) {
244
+ hue = ((this.g - this.b) / chroma) % 6;
245
+ } else if (this.g >= this.r && this.g >= this.b) {
246
+ hue = (this.b - this.r) / chroma + 2;
247
+ } else {
248
+ hue = (this.r - this.g) / chroma + 4;
249
+ }
250
+
251
+ // Convert to degree representation, then to radians.
252
+ hue *= 60;
253
+ hue *= Math.PI / 180;
254
+
255
+ // Ensure positivity.
256
+ if (hue < 0) {
257
+ hue += Math.PI * 2;
258
+ }
259
+
260
+ const value = maxComponent;
261
+ const saturation = value > 0 ? chroma / value : 0;
262
+
263
+ return Vec3.of(hue, saturation, value);
264
+ }
265
+
266
+ private hexString: string|null = null;
267
+
268
+ /**
269
+ * @returns a hexadecimal color string representation of `this`, in the form `#rrggbbaa`.
270
+ *
271
+ * @example
272
+ * ```
273
+ * Color4.red.toHexString(); // -> #ff0000ff
274
+ * ```
275
+ */
276
+ public toHexString(): string {
277
+ if (this.hexString) {
278
+ return this.hexString;
279
+ }
280
+
281
+ const componentToHex = (component: number): string => {
282
+ const res = Math.round(255 * component).toString(16);
283
+
284
+ if (res.length === 1) {
285
+ return `0${res}`;
286
+ }
287
+ return res;
288
+ };
289
+
290
+ const alpha = componentToHex(this.a);
291
+ const red = componentToHex(this.r);
292
+ const green = componentToHex(this.g);
293
+ const blue = componentToHex(this.b);
294
+ if (alpha === 'ff') {
295
+ return `#${red}${green}${blue}`;
296
+ }
297
+ this.hexString = `#${red}${green}${blue}${alpha}`;
298
+ return this.hexString;
299
+ }
300
+
301
+ public toString() {
302
+ return this.toHexString();
303
+ }
304
+
305
+ public static transparent = Color4.ofRGBA(0, 0, 0, 0);
306
+ public static red = Color4.ofRGB(1.0, 0.0, 0.0);
307
+ public static orange = Color4.ofRGB(1.0, 0.65, 0.0);
308
+ public static green = Color4.ofRGB(0.0, 1.0, 0.0);
309
+ public static blue = Color4.ofRGB(0.0, 0.0, 1.0);
310
+ public static purple = Color4.ofRGB(0.5, 0.2, 0.5);
311
+ public static yellow = Color4.ofRGB(1, 1, 0.1);
312
+ public static clay = Color4.ofRGB(0.8, 0.4, 0.2);
313
+ public static black = Color4.ofRGB(0, 0, 0);
314
+ public static gray = Color4.ofRGB(0.5, 0.5, 0.5);
315
+ public static white = Color4.ofRGB(1, 1, 1);
316
+ }
317
+
318
+ export { Color4 };