@js-draw/math 1.0.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.
Files changed (69) hide show
  1. package/README.md +3 -0
  2. package/build-config.json +4 -0
  3. package/dist/cjs/Color4.d.ts +83 -0
  4. package/dist/cjs/Color4.js +277 -0
  5. package/dist/cjs/Mat33.d.ts +131 -0
  6. package/dist/cjs/Mat33.js +345 -0
  7. package/dist/cjs/Vec2.d.ts +42 -0
  8. package/dist/cjs/Vec2.js +48 -0
  9. package/dist/cjs/Vec3.d.ts +126 -0
  10. package/dist/cjs/Vec3.js +203 -0
  11. package/dist/cjs/lib.d.ts +27 -0
  12. package/dist/cjs/lib.js +42 -0
  13. package/dist/cjs/polynomial/solveQuadratic.d.ts +9 -0
  14. package/dist/cjs/polynomial/solveQuadratic.js +39 -0
  15. package/dist/cjs/rounding.d.ts +15 -0
  16. package/dist/cjs/rounding.js +146 -0
  17. package/dist/cjs/shapes/Abstract2DShape.d.ts +49 -0
  18. package/dist/cjs/shapes/Abstract2DShape.js +38 -0
  19. package/dist/cjs/shapes/BezierJSWrapper.d.ts +36 -0
  20. package/dist/cjs/shapes/BezierJSWrapper.js +94 -0
  21. package/dist/cjs/shapes/CubicBezier.d.ts +17 -0
  22. package/dist/cjs/shapes/CubicBezier.js +35 -0
  23. package/dist/cjs/shapes/LineSegment2.d.ts +70 -0
  24. package/dist/cjs/shapes/LineSegment2.js +183 -0
  25. package/dist/cjs/shapes/Path.d.ts +96 -0
  26. package/dist/cjs/shapes/Path.js +766 -0
  27. package/dist/cjs/shapes/PointShape2D.d.ts +18 -0
  28. package/dist/cjs/shapes/PointShape2D.js +31 -0
  29. package/dist/cjs/shapes/QuadraticBezier.d.ts +35 -0
  30. package/dist/cjs/shapes/QuadraticBezier.js +120 -0
  31. package/dist/cjs/shapes/Rect2.d.ts +58 -0
  32. package/dist/cjs/shapes/Rect2.js +259 -0
  33. package/dist/cjs/shapes/Triangle.d.ts +46 -0
  34. package/dist/cjs/shapes/Triangle.js +126 -0
  35. package/dist/mjs/Color4.d.ts +83 -0
  36. package/dist/mjs/Color4.mjs +271 -0
  37. package/dist/mjs/Mat33.d.ts +131 -0
  38. package/dist/mjs/Mat33.mjs +338 -0
  39. package/dist/mjs/Vec2.d.ts +42 -0
  40. package/dist/mjs/Vec2.mjs +42 -0
  41. package/dist/mjs/Vec3.d.ts +126 -0
  42. package/dist/mjs/Vec3.mjs +199 -0
  43. package/dist/mjs/lib.d.ts +27 -0
  44. package/dist/mjs/lib.mjs +29 -0
  45. package/dist/mjs/polynomial/solveQuadratic.d.ts +9 -0
  46. package/dist/mjs/polynomial/solveQuadratic.mjs +37 -0
  47. package/dist/mjs/rounding.d.ts +15 -0
  48. package/dist/mjs/rounding.mjs +139 -0
  49. package/dist/mjs/shapes/Abstract2DShape.d.ts +49 -0
  50. package/dist/mjs/shapes/Abstract2DShape.mjs +36 -0
  51. package/dist/mjs/shapes/BezierJSWrapper.d.ts +36 -0
  52. package/dist/mjs/shapes/BezierJSWrapper.mjs +89 -0
  53. package/dist/mjs/shapes/CubicBezier.d.ts +17 -0
  54. package/dist/mjs/shapes/CubicBezier.mjs +30 -0
  55. package/dist/mjs/shapes/LineSegment2.d.ts +70 -0
  56. package/dist/mjs/shapes/LineSegment2.mjs +176 -0
  57. package/dist/mjs/shapes/Path.d.ts +96 -0
  58. package/dist/mjs/shapes/Path.mjs +759 -0
  59. package/dist/mjs/shapes/PointShape2D.d.ts +18 -0
  60. package/dist/mjs/shapes/PointShape2D.mjs +26 -0
  61. package/dist/mjs/shapes/QuadraticBezier.d.ts +35 -0
  62. package/dist/mjs/shapes/QuadraticBezier.mjs +113 -0
  63. package/dist/mjs/shapes/Rect2.d.ts +58 -0
  64. package/dist/mjs/shapes/Rect2.mjs +252 -0
  65. package/dist/mjs/shapes/Triangle.d.ts +46 -0
  66. package/dist/mjs/shapes/Triangle.mjs +121 -0
  67. package/package.json +48 -0
  68. package/tsconfig.json +7 -0
  69. package/typedoc.json +5 -0
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
+ };
7
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
+ if (kind === "m") throw new TypeError("Private method is not writable");
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ var _Triangle_sides;
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ const Abstract2DShape_1 = __importDefault(require("./Abstract2DShape"));
19
+ const LineSegment2_1 = __importDefault(require("./LineSegment2"));
20
+ const Rect2_1 = __importDefault(require("./Rect2"));
21
+ class Triangle extends Abstract2DShape_1.default {
22
+ /**
23
+ * @see {@link fromVertices}
24
+ */
25
+ constructor(vertex1, vertex2, vertex3) {
26
+ super();
27
+ this.vertex1 = vertex1;
28
+ this.vertex2 = vertex2;
29
+ this.vertex3 = vertex3;
30
+ _Triangle_sides.set(this, undefined);
31
+ }
32
+ /**
33
+ * Creates a triangle from its three corners. Corners may be stored in a different
34
+ * order than given.
35
+ */
36
+ static fromVertices(vertex1, vertex2, vertex3) {
37
+ return new Triangle(vertex1, vertex2, vertex3);
38
+ }
39
+ get vertices() {
40
+ return [this.vertex1, this.vertex2, this.vertex3];
41
+ }
42
+ map(mapping) {
43
+ return new Triangle(mapping(this.vertex1), mapping(this.vertex2), mapping(this.vertex3));
44
+ }
45
+ // Transform, treating this as composed of 2D points.
46
+ transformed2DBy(affineTransform) {
47
+ return this.map(affineTransform.transformVec2);
48
+ }
49
+ // Transforms this by a linear transform --- verticies are treated as
50
+ // 3D points.
51
+ transformedBy(linearTransform) {
52
+ return this.map(linearTransform.transformVec3);
53
+ }
54
+ /**
55
+ * Returns the sides of this triangle, as an array of `LineSegment2`s.
56
+ *
57
+ * The first side is from `vertex1` to `vertex2`, the next from `vertex2` to `vertex3`,
58
+ * and the last from `vertex3` to `vertex1`.
59
+ */
60
+ getEdges() {
61
+ if (__classPrivateFieldGet(this, _Triangle_sides, "f")) {
62
+ return __classPrivateFieldGet(this, _Triangle_sides, "f");
63
+ }
64
+ const side1 = new LineSegment2_1.default(this.vertex1, this.vertex2);
65
+ const side2 = new LineSegment2_1.default(this.vertex2, this.vertex3);
66
+ const side3 = new LineSegment2_1.default(this.vertex3, this.vertex1);
67
+ const sides = [side1, side2, side3];
68
+ __classPrivateFieldSet(this, _Triangle_sides, sides, "f");
69
+ return sides;
70
+ }
71
+ intersectsLineSegment(lineSegment) {
72
+ const result = [];
73
+ for (const edge of this.getEdges()) {
74
+ edge.intersectsLineSegment(lineSegment)
75
+ .forEach(point => result.push(point));
76
+ }
77
+ return result;
78
+ }
79
+ /** @inheritdoc */
80
+ containsPoint(point, epsilon = Abstract2DShape_1.default.smallValue) {
81
+ // Project `point` onto normals to each of this' sides.
82
+ // Uses the Separating Axis Theorem (https://en.wikipedia.org/wiki/Hyperplane_separation_theorem#Use_in_collision_detection)
83
+ const sides = this.getEdges();
84
+ for (const side of sides) {
85
+ const orthog = side.direction.orthog();
86
+ // Project all three vertices
87
+ // TODO: Performance can be improved here (two vertices will always have the same projection)
88
+ const projv1 = orthog.dot(this.vertex1);
89
+ const projv2 = orthog.dot(this.vertex2);
90
+ const projv3 = orthog.dot(this.vertex3);
91
+ const minProjVertex = Math.min(projv1, projv2, projv3);
92
+ const maxProjVertex = Math.max(projv1, projv2, projv3);
93
+ const projPoint = orthog.dot(point);
94
+ const inProjection = projPoint >= minProjVertex - epsilon && projPoint <= maxProjVertex + epsilon;
95
+ if (!inProjection) {
96
+ return false;
97
+ }
98
+ }
99
+ return true;
100
+ }
101
+ /**
102
+ * @returns the signed distance from `point` to the closest edge of this triangle.
103
+ *
104
+ * If `point` is inside `this`, the result is negative, otherwise, the result is
105
+ * positive.
106
+ */
107
+ signedDistance(point) {
108
+ const sides = this.getEdges();
109
+ const distances = sides.map(side => side.distance(point));
110
+ const distance = Math.min(...distances);
111
+ // If the point is in this' interior, signedDistance must return a negative
112
+ // number.
113
+ if (this.containsPoint(point, 0)) {
114
+ return -distance;
115
+ }
116
+ else {
117
+ return distance;
118
+ }
119
+ }
120
+ /** @inheritdoc */
121
+ getTightBoundingBox() {
122
+ return Rect2_1.default.bboxOf(this.vertices);
123
+ }
124
+ }
125
+ _Triangle_sides = new WeakMap();
126
+ exports.default = Triangle;
@@ -0,0 +1,83 @@
1
+ import Vec3 from './Vec3';
2
+ /**
3
+ * Represents a color.
4
+ *
5
+ * @example
6
+ * ```ts,runnable,console
7
+ * import { Color4 } from '@js-draw/math';
8
+ *
9
+ * console.log('Red:', Color4.fromString('#f00'));
10
+ * console.log('Also red:', Color4.ofRGB(1, 0, 0), Color4.red);
11
+ * console.log('Mixing red and blue:', Color4.red.mix(Color4.blue, 0.5));
12
+ * console.log('To string:', Color4.orange.toHexString());
13
+ * ```
14
+ */
15
+ export default class Color4 {
16
+ /** Red component. Should be in the range [0, 1]. */
17
+ readonly r: number;
18
+ /** Green component. ${\tt g} \in [0, 1]$ */
19
+ readonly g: number;
20
+ /** Blue component. ${\tt b} \in [0, 1]$ */
21
+ readonly b: number;
22
+ /** Alpha/transparent component. ${\tt a} \in [0, 1]$. 0 = transparent */
23
+ readonly a: number;
24
+ private constructor();
25
+ /**
26
+ * Create a color from red, green, blue components. The color is fully opaque (`a = 1.0`).
27
+ *
28
+ * Each component should be in the range [0, 1].
29
+ */
30
+ static ofRGB(red: number, green: number, blue: number): Color4;
31
+ static ofRGBA(red: number, green: number, blue: number, alpha: number): Color4;
32
+ static fromHex(hexString: string): Color4;
33
+ /** Like fromHex, but can handle additional colors if an `HTMLCanvasElement` is available. */
34
+ static fromString(text: string): Color4;
35
+ /** @returns true if `this` and `other` are approximately equal. */
36
+ eq(other: Color4 | null | undefined): boolean;
37
+ /**
38
+ * If `fractionTo` is not in the range $[0, 1]$, it will be clamped to the nearest number
39
+ * in that range. For example, `a.mix(b, -1)` is equivalent to `a.mix(b, 0)`.
40
+ *
41
+ * @returns a color `fractionTo` of the way from this color to `other`.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * Color4.ofRGB(1, 0, 0).mix(Color4.ofRGB(0, 1, 0), 0.1) // -> Color4(0.9, 0.1, 0)
46
+ * ```
47
+ */
48
+ mix(other: Color4, fractionTo: number): Color4;
49
+ /**
50
+ * @returns the component-wise average of `colors`, or `Color4.transparent` if `colors` is empty.
51
+ */
52
+ static average(colors: Color4[]): Color4;
53
+ /**
54
+ * Converts to (hue, saturation, value).
55
+ * See also https://en.wikipedia.org/wiki/HSL_and_HSV#General_approach
56
+ *
57
+ * The resultant hue is represented in radians and is thus in $[0, 2\pi]$.
58
+ */
59
+ asHSV(): Vec3;
60
+ private hexString;
61
+ /**
62
+ * @returns a hexadecimal color string representation of `this`, in the form `#rrggbbaa`.
63
+ *
64
+ * @example
65
+ * ```
66
+ * Color4.red.toHexString(); // -> #ff0000ff
67
+ * ```
68
+ */
69
+ toHexString(): string;
70
+ toString(): string;
71
+ static transparent: Color4;
72
+ static red: Color4;
73
+ static orange: Color4;
74
+ static green: Color4;
75
+ static blue: Color4;
76
+ static purple: Color4;
77
+ static yellow: Color4;
78
+ static clay: Color4;
79
+ static black: Color4;
80
+ static gray: Color4;
81
+ static white: Color4;
82
+ }
83
+ export { Color4 };
@@ -0,0 +1,271 @@
1
+ import Vec3 from './Vec3.mjs';
2
+ /**
3
+ * Represents a color.
4
+ *
5
+ * @example
6
+ * ```ts,runnable,console
7
+ * import { Color4 } from '@js-draw/math';
8
+ *
9
+ * console.log('Red:', Color4.fromString('#f00'));
10
+ * console.log('Also red:', Color4.ofRGB(1, 0, 0), Color4.red);
11
+ * console.log('Mixing red and blue:', Color4.red.mix(Color4.blue, 0.5));
12
+ * console.log('To string:', Color4.orange.toHexString());
13
+ * ```
14
+ */
15
+ class Color4 {
16
+ constructor(
17
+ /** Red component. Should be in the range [0, 1]. */
18
+ r,
19
+ /** Green component. ${\tt g} \in [0, 1]$ */
20
+ g,
21
+ /** Blue component. ${\tt b} \in [0, 1]$ */
22
+ b,
23
+ /** Alpha/transparent component. ${\tt a} \in [0, 1]$. 0 = transparent */
24
+ a) {
25
+ this.r = r;
26
+ this.g = g;
27
+ this.b = b;
28
+ this.a = a;
29
+ this.hexString = null;
30
+ }
31
+ /**
32
+ * Create a color from red, green, blue components. The color is fully opaque (`a = 1.0`).
33
+ *
34
+ * Each component should be in the range [0, 1].
35
+ */
36
+ static ofRGB(red, green, blue) {
37
+ return Color4.ofRGBA(red, green, blue, 1.0);
38
+ }
39
+ static ofRGBA(red, green, blue, alpha) {
40
+ red = Math.max(0, Math.min(red, 1));
41
+ green = Math.max(0, Math.min(green, 1));
42
+ blue = Math.max(0, Math.min(blue, 1));
43
+ alpha = Math.max(0, Math.min(alpha, 1));
44
+ return new Color4(red, green, blue, alpha);
45
+ }
46
+ static fromHex(hexString) {
47
+ // Remove starting '#' (if present)
48
+ hexString = (hexString.match(/^[#]?(.*)$/) ?? [])[1];
49
+ hexString = hexString.toUpperCase();
50
+ if (!hexString.match(/^[0-9A-F]+$/)) {
51
+ throw new Error(`${hexString} is not in a valid format.`);
52
+ }
53
+ // RGBA or RGB
54
+ if (hexString.length === 3 || hexString.length === 4) {
55
+ // Each character is a component
56
+ const components = hexString.split('');
57
+ // Convert to RRGGBBAA or RRGGBB format
58
+ hexString = components.map(component => `${component}0`).join('');
59
+ }
60
+ if (hexString.length === 6) {
61
+ // Alpha component
62
+ hexString += 'FF';
63
+ }
64
+ const components = [];
65
+ for (let i = 2; i <= hexString.length; i += 2) {
66
+ const chunk = hexString.substring(i - 2, i);
67
+ components.push(parseInt(chunk, 16) / 255);
68
+ }
69
+ if (components.length !== 4) {
70
+ throw new Error(`Unable to parse ${hexString}: Wrong number of components.`);
71
+ }
72
+ return Color4.ofRGBA(components[0], components[1], components[2], components[3]);
73
+ }
74
+ /** Like fromHex, but can handle additional colors if an `HTMLCanvasElement` is available. */
75
+ static fromString(text) {
76
+ if (text.startsWith('#')) {
77
+ return Color4.fromHex(text);
78
+ }
79
+ if (text === 'none' || text === 'transparent') {
80
+ return Color4.transparent;
81
+ }
82
+ // rgba?: Match both rgb and rgba strings.
83
+ // ([,0-9.]+): Match any string of only numeric, '.' and ',' characters.
84
+ const rgbRegex = /^rgba?\(([,0-9.]+)\)$/i;
85
+ const rgbMatch = text.replace(/\s*/g, '').match(rgbRegex);
86
+ if (rgbMatch) {
87
+ const componentsListStr = rgbMatch[1];
88
+ const componentsList = JSON.parse(`[ ${componentsListStr} ]`);
89
+ if (componentsList.length === 3) {
90
+ return Color4.ofRGB(componentsList[0] / 255, componentsList[1] / 255, componentsList[2] / 255);
91
+ }
92
+ else if (componentsList.length === 4) {
93
+ return Color4.ofRGBA(componentsList[0] / 255, componentsList[1] / 255, componentsList[2] / 255, componentsList[3]);
94
+ }
95
+ else {
96
+ throw new Error(`RGB string, ${text}, has wrong number of components: ${componentsList.length}`);
97
+ }
98
+ }
99
+ // Otherwise, try to use an HTMLCanvasElement to determine the color.
100
+ // Note: We may be unable to create an HTMLCanvasElement if running as a unit test.
101
+ const canvas = document.createElement('canvas');
102
+ canvas.width = 1;
103
+ canvas.height = 1;
104
+ const ctx = canvas.getContext('2d');
105
+ ctx.fillStyle = text;
106
+ ctx.fillRect(0, 0, 1, 1);
107
+ const data = ctx.getImageData(0, 0, 1, 1);
108
+ const red = data.data[0] / 255;
109
+ const green = data.data[1] / 255;
110
+ const blue = data.data[2] / 255;
111
+ const alpha = data.data[3] / 255;
112
+ return Color4.ofRGBA(red, green, blue, alpha);
113
+ }
114
+ /** @returns true if `this` and `other` are approximately equal. */
115
+ eq(other) {
116
+ if (other == null) {
117
+ return false;
118
+ }
119
+ // If both completely transparent,
120
+ if (this.a === 0 && other.a === 0) {
121
+ return true;
122
+ }
123
+ return this.toHexString() === other.toHexString();
124
+ }
125
+ /**
126
+ * If `fractionTo` is not in the range $[0, 1]$, it will be clamped to the nearest number
127
+ * in that range. For example, `a.mix(b, -1)` is equivalent to `a.mix(b, 0)`.
128
+ *
129
+ * @returns a color `fractionTo` of the way from this color to `other`.
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * Color4.ofRGB(1, 0, 0).mix(Color4.ofRGB(0, 1, 0), 0.1) // -> Color4(0.9, 0.1, 0)
134
+ * ```
135
+ */
136
+ mix(other, fractionTo) {
137
+ fractionTo = Math.min(Math.max(fractionTo, 0), 1);
138
+ const fractionOfThis = 1 - fractionTo;
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
+ }
141
+ /**
142
+ * @returns the component-wise average of `colors`, or `Color4.transparent` if `colors` is empty.
143
+ */
144
+ static average(colors) {
145
+ let averageA = 0;
146
+ let averageR = 0;
147
+ let averageG = 0;
148
+ let averageB = 0;
149
+ for (const color of colors) {
150
+ averageA += color.a;
151
+ averageR += color.r;
152
+ averageG += color.g;
153
+ averageB += color.b;
154
+ }
155
+ if (colors.length > 0) {
156
+ averageA /= colors.length;
157
+ averageR /= colors.length;
158
+ averageG /= colors.length;
159
+ averageB /= colors.length;
160
+ }
161
+ return new Color4(averageR, averageG, averageB, averageA);
162
+ }
163
+ /**
164
+ * Converts to (hue, saturation, value).
165
+ * See also https://en.wikipedia.org/wiki/HSL_and_HSV#General_approach
166
+ *
167
+ * The resultant hue is represented in radians and is thus in $[0, 2\pi]$.
168
+ */
169
+ asHSV() {
170
+ // Ref: https://en.wikipedia.org/wiki/HSL_and_HSV#General_approach
171
+ //
172
+ // HUE:
173
+ // First, consider the unit cube. Rotate it such that one vertex is at the origin
174
+ // of a plane and its three neighboring vertices are equidistant from that plane:
175
+ //
176
+ // /\
177
+ // / | \
178
+ // 2 / 3 \ 1
179
+ // \ | /
180
+ // \ | /
181
+ // . \/ .
182
+ //
183
+ // .
184
+ //
185
+ // Let z be up and (x, y, 0) be in the plane.
186
+ //
187
+ // Label vectors 1,2,3 with R, G, and B, respectively. Let R's projection into the plane
188
+ // lie along the x axis.
189
+ //
190
+ // Because R is a unit vector and R, G, B are equidistant from the plane, they must
191
+ // form 30-60-90 triangles, which have side lengths proportional to (1, √3, 2)
192
+ //
193
+ // /|
194
+ // 1/ | (√3)/2
195
+ // / |
196
+ // 1/2
197
+ //
198
+ const minComponent = Math.min(this.r, this.g, this.b);
199
+ const maxComponent = Math.max(this.r, this.g, this.b);
200
+ const chroma = maxComponent - minComponent;
201
+ let hue;
202
+ // See https://en.wikipedia.org/wiki/HSL_and_HSV#General_approach
203
+ if (chroma === 0) {
204
+ hue = 0;
205
+ }
206
+ else if (this.r >= this.g && this.r >= this.b) {
207
+ hue = ((this.g - this.b) / chroma) % 6;
208
+ }
209
+ else if (this.g >= this.r && this.g >= this.b) {
210
+ hue = (this.b - this.r) / chroma + 2;
211
+ }
212
+ else {
213
+ hue = (this.r - this.g) / chroma + 4;
214
+ }
215
+ // Convert to degree representation, then to radians.
216
+ hue *= 60;
217
+ hue *= Math.PI / 180;
218
+ // Ensure positivity.
219
+ if (hue < 0) {
220
+ hue += Math.PI * 2;
221
+ }
222
+ const value = maxComponent;
223
+ const saturation = value > 0 ? chroma / value : 0;
224
+ return Vec3.of(hue, saturation, value);
225
+ }
226
+ /**
227
+ * @returns a hexadecimal color string representation of `this`, in the form `#rrggbbaa`.
228
+ *
229
+ * @example
230
+ * ```
231
+ * Color4.red.toHexString(); // -> #ff0000ff
232
+ * ```
233
+ */
234
+ toHexString() {
235
+ if (this.hexString) {
236
+ return this.hexString;
237
+ }
238
+ const componentToHex = (component) => {
239
+ const res = Math.round(255 * component).toString(16);
240
+ if (res.length === 1) {
241
+ return `0${res}`;
242
+ }
243
+ return res;
244
+ };
245
+ const alpha = componentToHex(this.a);
246
+ const red = componentToHex(this.r);
247
+ const green = componentToHex(this.g);
248
+ const blue = componentToHex(this.b);
249
+ if (alpha === 'ff') {
250
+ return `#${red}${green}${blue}`;
251
+ }
252
+ this.hexString = `#${red}${green}${blue}${alpha}`;
253
+ return this.hexString;
254
+ }
255
+ toString() {
256
+ return this.toHexString();
257
+ }
258
+ }
259
+ Color4.transparent = Color4.ofRGBA(0, 0, 0, 0);
260
+ Color4.red = Color4.ofRGB(1.0, 0.0, 0.0);
261
+ Color4.orange = Color4.ofRGB(1.0, 0.65, 0.0);
262
+ Color4.green = Color4.ofRGB(0.0, 1.0, 0.0);
263
+ Color4.blue = Color4.ofRGB(0.0, 0.0, 1.0);
264
+ Color4.purple = Color4.ofRGB(0.5, 0.2, 0.5);
265
+ Color4.yellow = Color4.ofRGB(1, 1, 0.1);
266
+ Color4.clay = Color4.ofRGB(0.8, 0.4, 0.2);
267
+ Color4.black = Color4.ofRGB(0, 0, 0);
268
+ Color4.gray = Color4.ofRGB(0.5, 0.5, 0.5);
269
+ Color4.white = Color4.ofRGB(1, 1, 1);
270
+ export default Color4;
271
+ export { Color4 };
@@ -0,0 +1,131 @@
1
+ import { Point2, Vec2 } from './Vec2';
2
+ import Vec3 from './Vec3';
3
+ export type Mat33Array = [
4
+ number,
5
+ number,
6
+ number,
7
+ number,
8
+ number,
9
+ number,
10
+ number,
11
+ number,
12
+ number
13
+ ];
14
+ /**
15
+ * Represents a three dimensional linear transformation or
16
+ * a two-dimensional affine transformation. (An affine transformation scales/rotates/shears
17
+ * **and** translates while a linear transformation just scales/rotates/shears).
18
+ */
19
+ export declare class Mat33 {
20
+ readonly a1: number;
21
+ readonly a2: number;
22
+ readonly a3: number;
23
+ readonly b1: number;
24
+ readonly b2: number;
25
+ readonly b3: number;
26
+ readonly c1: number;
27
+ readonly c2: number;
28
+ readonly c3: number;
29
+ private readonly rows;
30
+ /**
31
+ * Creates a matrix from inputs in the form,
32
+ * $$
33
+ * \begin{bmatrix}
34
+ * a1 & a2 & a3 \\
35
+ * b1 & b2 & b3 \\
36
+ * c1 & c2 & c3
37
+ * \end{bmatrix}
38
+ * $$
39
+ */
40
+ constructor(a1: number, a2: number, a3: number, b1: number, b2: number, b3: number, c1: number, c2: number, c3: number);
41
+ /**
42
+ * Creates a matrix from the given rows:
43
+ * $$
44
+ * \begin{bmatrix}
45
+ * \texttt{r1.x} & \texttt{r1.y} & \texttt{r1.z}\\
46
+ * \texttt{r2.x} & \texttt{r2.y} & \texttt{r2.z}\\
47
+ * \texttt{r3.x} & \texttt{r3.y} & \texttt{r3.z}\\
48
+ * \end{bmatrix}
49
+ * $$
50
+ */
51
+ static ofRows(r1: Vec3, r2: Vec3, r3: Vec3): Mat33;
52
+ static identity: Mat33;
53
+ /**
54
+ * Either returns the inverse of this, or, if this matrix is singular/uninvertable,
55
+ * returns Mat33.identity.
56
+ *
57
+ * This may cache the computed inverse and return the cached version instead of recomputing
58
+ * it.
59
+ */
60
+ inverse(): Mat33;
61
+ invertable(): boolean;
62
+ private cachedInverse;
63
+ private computeInverse;
64
+ transposed(): Mat33;
65
+ rightMul(other: Mat33): Mat33;
66
+ /**
67
+ * Applies this as an **affine** transformation to the given vector.
68
+ * Returns a transformed version of `other`.
69
+ *
70
+ * Unlike {@link transformVec3}, this **does** translate the given vector.
71
+ */
72
+ transformVec2(other: Vec2): Vec2;
73
+ /**
74
+ * Applies this as a linear transformation to the given vector (doesn't translate).
75
+ * This is the standard way of transforming vectors in ℝ³.
76
+ */
77
+ transformVec3(other: Vec3): Vec3;
78
+ /** @returns true iff this is the identity matrix. */
79
+ isIdentity(): boolean;
80
+ /** Returns true iff this = other ± fuzz */
81
+ eq(other: Mat33, fuzz?: number): boolean;
82
+ toString(): string;
83
+ /**
84
+ * ```
85
+ * result[0] = top left element
86
+ * result[1] = element at row zero, column 1
87
+ * ...
88
+ * ```
89
+ */
90
+ toArray(): Mat33Array;
91
+ /**
92
+ * Returns a new `Mat33` where each entry is the output of the function
93
+ * `mapping`.
94
+ *
95
+ * @example
96
+ * ```
97
+ * new Mat33(
98
+ * 1, 2, 3,
99
+ * 4, 5, 6,
100
+ * 7, 8, 9,
101
+ * ).mapEntries(component => component - 1);
102
+ * // → ⎡ 0, 1, 2 ⎤
103
+ * // ⎢ 3, 4, 5 ⎥
104
+ * // ⎣ 6, 7, 8 ⎦
105
+ * ```
106
+ */
107
+ mapEntries(mapping: (component: number, rowcol: [number, number]) => number): Mat33;
108
+ /** Estimate the scale factor of this matrix (based on the first row). */
109
+ getScaleFactor(): number;
110
+ /**
111
+ * Constructs a 3x3 translation matrix (for translating `Vec2`s) using
112
+ * **transformVec2**.
113
+ */
114
+ static translation(amount: Vec2): Mat33;
115
+ static zRotation(radians: number, center?: Point2): Mat33;
116
+ static scaling2D(amount: number | Vec2, center?: Point2): Mat33;
117
+ /** @see {@link fromCSSMatrix} */
118
+ toCSSMatrix(): string;
119
+ /**
120
+ * Converts a CSS-form `matrix(a, b, c, d, e, f)` to a Mat33.
121
+ *
122
+ * Note that such a matrix has the form,
123
+ * ```
124
+ * ⎡ a c e ⎤
125
+ * ⎢ b d f ⎥
126
+ * ⎣ 0 0 1 ⎦
127
+ * ```
128
+ */
129
+ static fromCSSMatrix(cssString: string): Mat33;
130
+ }
131
+ export default Mat33;