@js-draw/math 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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;