@js-draw/math 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 };