@woosh/meep-engine 2.38.2 → 2.39.1

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 (36) hide show
  1. package/core/geom/Quaternion.js +70 -0
  2. package/core/geom/Vector2.js +17 -0
  3. package/core/geom/v3_angle_between.js +17 -3
  4. package/engine/ecs/EntityBuilder.d.ts +3 -1
  5. package/engine/ecs/EntityBuilder.js +10 -1
  6. package/engine/ecs/EntityComponentDataset.js +1 -1
  7. package/engine/ecs/parent/ChildEntities.d.ts +3 -0
  8. package/engine/ecs/parent/ChildEntities.js +41 -0
  9. package/engine/ecs/system/AbstractContextSystem.js +4 -2
  10. package/engine/graphics/ecs/mesh-v2/sg_hierarchy_compute_bounding_box_via_parent_entity.js +1 -1
  11. package/engine/graphics/ecs/path/PathDisplayEvents.js +3 -2
  12. package/engine/graphics/ecs/path/PathDisplaySystem.js +5 -0
  13. package/engine/graphics/ecs/path/highlight/PathDisplayHighlightSystem.d.ts +7 -0
  14. package/engine/graphics/ecs/path/highlight/PathDisplayHighlightSystem.js +141 -0
  15. package/engine/graphics/ecs/path/testPathDisplaySystem.js +86 -25
  16. package/engine/graphics/ecs/path/tube/BasicMaterialDefinition.d.ts +3 -0
  17. package/engine/graphics/ecs/path/tube/BasicMaterialDefinition.js +8 -0
  18. package/engine/graphics/ecs/path/tube/TubeMaterialType.d.ts +1 -0
  19. package/engine/graphics/ecs/path/tube/TubeMaterialType.js +1 -0
  20. package/engine/graphics/ecs/path/tube/TubePathStyle.d.ts +12 -0
  21. package/engine/graphics/ecs/path/tube/TubePathStyle.js +54 -9
  22. package/engine/graphics/ecs/path/tube/build/TubePathBuilder.js +67 -25
  23. package/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.js +12 -2
  24. package/engine/graphics/ecs/path/tube/build/build_geometry_linear.js +13 -2
  25. package/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +33 -28
  26. package/engine/graphics/ecs/path/tube/build/compute_smooth_profile_normals.js +41 -0
  27. package/engine/graphics/ecs/path/tube/build/fix_shape_normal_order.js +45 -0
  28. package/engine/graphics/ecs/path/tube/build/makeTubeGeometry.js +123 -347
  29. package/engine/graphics/ecs/path/tube/build/make_cap.js +274 -0
  30. package/engine/graphics/ecs/path/tube/build/make_ring_faces.js +40 -0
  31. package/engine/graphics/ecs/path/tube/build/make_ring_vertices.js +152 -0
  32. package/package.json +1 -1
  33. package/view/View.js +2 -2
  34. package/view/{compose3x3transform.js → m3_cm_compose_transform.js} +11 -3
  35. package/view/m3_rm_compose_transform.js +45 -0
  36. package/view/multiplyMatrices3.js +4 -4
@@ -0,0 +1,274 @@
1
+ import { Vector3 } from "three";
2
+ import { max2 } from "../../../../../../core/math/max2.js";
3
+ import { m3_rm_compose_transform } from "../../../../../../view/m3_rm_compose_transform.js";
4
+ import { m3_multiply } from "../../../../../../view/multiplyMatrices3.js";
5
+ import { make_ring_vertices } from "./make_ring_vertices.js";
6
+ import { make_ring_faces } from "./make_ring_faces.js";
7
+ import { v2_distance } from "../../../../../../core/geom/Vector2.js";
8
+ import { CapType } from "../CapType.js";
9
+
10
+ /**
11
+ *
12
+ * @param {number} radial_segments
13
+ * @returns {number}
14
+ */
15
+ function compute_cap_round_segment_count(radial_segments) {
16
+ return max2(3, Math.ceil(radial_segments / 4));
17
+ }
18
+
19
+ /**
20
+ *
21
+ * @param {number[]} shape
22
+ * @param {number} shape_length
23
+ * @param {number} cx
24
+ * @param {number} cy
25
+ * @returns {number}
26
+ */
27
+ function compute_shape_radius(shape, shape_length, cx = 0, cy = 0) {
28
+ let max_distance = 0;
29
+ for (let i = 0; i < shape_length; i++) {
30
+ const i2 = i * 2;
31
+
32
+ const x = shape[i2];
33
+ const y = shape[i2 + 1];
34
+
35
+ // NOTE: can be optimized by delaying SQRT
36
+ const distance = v2_distance(x, y, cx, cy);
37
+
38
+ if (distance > max_distance) {
39
+ max_distance = distance;
40
+ }
41
+ }
42
+
43
+ return max_distance;
44
+ }
45
+
46
+ /**
47
+ *
48
+ * @param {number} index
49
+ * @param {Float32Array|number[]} in_positions
50
+ * @param {Vector3[]} in_normals
51
+ * @param {Vector3[]} in_binormals
52
+ * @param {Vector3[]} in_tangents
53
+ * @param {GeometryOutput} out
54
+ * @param {number[]} shape
55
+ * @param {number[]|Float32Array} shape_normal
56
+ * @param {number} shape_length
57
+ * @param {number[]} shape_transform
58
+ * @param {number} direction
59
+ */
60
+ function make_cap_round(
61
+ out,
62
+ index,
63
+ in_positions, in_normals, in_binormals, in_tangents,
64
+ shape, shape_normal, shape_length, shape_transform, direction
65
+ ) {
66
+ // how many radial segments will be placed laterally to make up the cap
67
+ const cap_segment_count = compute_cap_round_segment_count(shape_length);
68
+
69
+ const i3 = index * 3;
70
+
71
+ const Px = in_positions[i3];
72
+ const Py = in_positions[i3 + 1];
73
+ const Pz = in_positions[i3 + 2];
74
+
75
+ // retrieve corresponding normal and binormal
76
+
77
+ const N = in_normals[index];
78
+ const B = in_binormals[index];
79
+ const T = in_tangents[index];
80
+
81
+ const tangent = new Vector3();
82
+ tangent.crossVectors(N, B);
83
+ tangent.normalize();
84
+ tangent.multiplyScalar(-direction);
85
+
86
+ const normal = direction > 0 ? N : N.clone().negate();
87
+ const binormal = direction > 0 ? B : B.clone().negate();
88
+
89
+ const angular_step_i = (Math.PI / 2) / cap_segment_count;
90
+
91
+ const uv_u = index / (in_positions.length / 3 - 1);
92
+
93
+ let i;
94
+
95
+ const index_offset = out.cursor_vertices;
96
+
97
+ const m3 = new Float32Array(9);
98
+
99
+ const radius_raw = compute_shape_radius(shape, shape_length);
100
+
101
+ // extract scale from shape matrix
102
+ const scale_x = Math.hypot(shape_transform[0], shape_transform[3]);
103
+ const scale_y = Math.hypot(shape_transform[1], shape_transform[4]);
104
+
105
+ const radius = radius_raw * max2(scale_x, scale_y);
106
+
107
+ for (i = 0; i <= cap_segment_count; i++) {
108
+
109
+ const j = direction>0? i:cap_segment_count-i;
110
+
111
+ const angle_b = j * angular_step_i ;
112
+
113
+
114
+ const cos_b = Math.cos(angle_b);
115
+ const sin_b = Math.sin(angle_b);
116
+
117
+ // build shape transform matrix that scales original shape down
118
+ m3_rm_compose_transform(m3, 0, 0, sin_b, sin_b, 0, 0, 0);
119
+ m3_multiply(m3, shape_transform, m3);
120
+
121
+ const _d = radius;
122
+
123
+ // compute offset for ring center
124
+ const _px = Px + tangent.x * cos_b * _d;
125
+ const _py = Py + tangent.y * cos_b * _d;
126
+ const _pz = Pz + tangent.z * cos_b * _d;
127
+
128
+ // build ring
129
+ // TODO normals on produced geometry are incorrect as they need to be bent along the cap
130
+ make_ring_vertices(
131
+ out,
132
+ _px, _py, _pz,
133
+ normal, binormal, tangent,
134
+ uv_u, v4_no_bend,
135
+ shape, shape_normal, shape_length, m3
136
+ );
137
+ }
138
+
139
+ make_ring_faces(out, index_offset, cap_segment_count, shape_length);
140
+ }
141
+
142
+ const m3_zero = new Float32Array(9);
143
+ const v4_array = new Float32Array(4);
144
+
145
+ const v4_no_bend = new Float32Array([0, 1, 0, 0]);
146
+
147
+ /**
148
+ *
149
+ * @param {number} index
150
+ * @param {Float32Array|number[]} in_positions
151
+ * @param {Vector3[]} in_normals
152
+ * @param {Vector3[]} in_binormals
153
+ * @param {Vector3[]} in_tangents
154
+ * @param {GeometryOutput} out
155
+ * @param {number[]} shape
156
+ * @param {number[]|Float32Array} shape_normals
157
+ * @param {number} shape_length
158
+ * @param {number[]} shape_transform
159
+ * @param {number} direction
160
+ */
161
+ function make_cap_flat(
162
+ out,
163
+ index,
164
+ in_positions, in_normals, in_binormals, in_tangents,
165
+ shape, shape_normal, shape_length, shape_transform, direction
166
+ ) {
167
+ // how many radial segments will be placed laterally to make up the cap
168
+ const i3 = index * 3;
169
+
170
+ const Px = in_positions[i3];
171
+ const Py = in_positions[i3 + 1];
172
+ const Pz = in_positions[i3 + 2];
173
+
174
+ // retrieve corresponding normal and binormal
175
+
176
+ const N = in_normals[index];
177
+ const B = in_binormals[index];
178
+
179
+ const tangent = new Vector3();
180
+ tangent.crossVectors(N, B);
181
+ tangent.normalize();
182
+
183
+ const normal = direction > 0 ? N.clone().negate() : N;
184
+
185
+ const uv_u = index / (in_positions.length / 3 - 1);
186
+
187
+ const index_offset = out.cursor_vertices;
188
+
189
+ make_ring_vertices(
190
+ out,
191
+ Px, Py, Pz,
192
+ normal, B, in_tangents[index],
193
+ uv_u, v4_no_bend,
194
+ shape, shape_normal, shape_length, shape_transform
195
+ );
196
+
197
+ make_ring_vertices(
198
+ out,
199
+ Px, Py, Pz,
200
+ normal, B, in_tangents[index],
201
+ uv_u, v4_no_bend,
202
+ shape, shape_normal, shape_length, m3_zero
203
+ );
204
+
205
+ make_ring_faces(out, index_offset, 1, shape_length);
206
+ }
207
+
208
+ /**
209
+ *
210
+ * @param {number} index
211
+ * @param {Float32Array|number[]} in_positions
212
+ * @param {Vector3[]} in_normals
213
+ * @param {Vector3[]} in_binormals
214
+ * @param {GeometryOutput} out
215
+ * @param {Vector3[]} in_tangents
216
+ * @param {number[]} shape
217
+ * @param {number[]|Float32Array} shape_normal
218
+ * @param {number} shape_length
219
+ * @param {number[]|Float32Array} shape_transform
220
+ * @param {number} direction
221
+ * @param {CapType} type
222
+ */
223
+ export function make_cap(
224
+ out,
225
+ index,
226
+ in_positions, in_normals, in_binormals, in_tangents,
227
+ shape, shape_normal, shape_length, shape_transform, direction, type
228
+ ) {
229
+ if (type === CapType.None) {
230
+ // do nothing
231
+ } else if (type === CapType.Round) {
232
+ make_cap_round(
233
+ out,
234
+ index,
235
+ in_positions, in_normals, in_binormals, in_tangents,
236
+ shape, shape_normal, shape_length, shape_transform,
237
+ direction
238
+ );
239
+ } else if (type === CapType.Flat) {
240
+ make_cap_flat(
241
+ out,
242
+ index,
243
+ in_positions, in_normals, in_binormals, in_tangents,
244
+ shape, shape_normal, shape_length, shape_transform,
245
+ direction
246
+ );
247
+ } else {
248
+ throw new Error(`Unsupported cap type '${type}'`);
249
+ }
250
+ }
251
+
252
+ /**
253
+ *
254
+ * @param {number} count how many caps
255
+ * @param {{polygon_count:number, vertex_count:number}} out
256
+ * @param {number} radial_segments
257
+ * @param {CapType} type
258
+ */
259
+ export function compute_cap_geometry_size(count, out, radial_segments, type) {
260
+
261
+ if (type === CapType.None) {
262
+ // do nothing
263
+ } else if (type === CapType.Round) {
264
+ const cap_segment_count = compute_cap_round_segment_count(radial_segments);
265
+
266
+ out.vertex_count += count * (cap_segment_count + 1) * (radial_segments + 1);
267
+ out.polygon_count += count * cap_segment_count * (radial_segments) * 2;
268
+ } else if (type === CapType.Flat) {
269
+ out.vertex_count += count * (2) * (radial_segments + 1);
270
+ out.polygon_count += count * (radial_segments) * 2;
271
+ } else {
272
+ throw new Error(`Unsupported cap type '${type}'`);
273
+ }
274
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ *
3
+ * @param {GeometryOutput} out
4
+ * @param {number} index_offset
5
+ * @param {number} path_segments
6
+ * @param {number} profile_segments
7
+ */
8
+ export function make_ring_faces(out, index_offset, path_segments, profile_segments) {
9
+ const indices = out.indices;
10
+
11
+ let i = 0, j = 0;
12
+
13
+ for (j = 1; j <= path_segments; j++) {
14
+
15
+ for (i = 1; i <= profile_segments; i++) {
16
+
17
+ const s_1 = profile_segments + 1;
18
+
19
+ const a = index_offset + s_1 * (j - 1) + (i - 1);
20
+ const b = index_offset + s_1 * j + (i - 1);
21
+ const c = index_offset + s_1 * j + i;
22
+ const d = index_offset + s_1 * (j - 1) + i;
23
+
24
+ // faces
25
+
26
+ const ci = out.cursor_indices;
27
+
28
+ indices[ci] = a;
29
+ indices[ci + 1] = b;
30
+ indices[ci + 2] = d;
31
+
32
+ indices[ci + 3] = b;
33
+ indices[ci + 4] = c;
34
+ indices[ci + 5] = d;
35
+
36
+ out.cursor_indices += 6;
37
+ }
38
+
39
+ }
40
+ }
@@ -0,0 +1,152 @@
1
+ //
2
+
3
+ import { v3_dot } from "../../../../../../core/geom/v3_dot.js";
4
+
5
+ /**
6
+ *
7
+ * @param {GeometryOutput} out
8
+ * @param {number} Px
9
+ * @param {number} Py
10
+ * @param {number} Pz
11
+ * @param {Vector3} N normal
12
+ * @param {Vector3} B binormal
13
+ * @param {Vector3} T tangent
14
+ * @param {number} u X component of UV
15
+ * @param {number[]|Float32Array} bend_angle
16
+ * @param {number[]|Float32Array} shape 2d shape, format [x0,y1, x1,y1, ... , xN,yN]
17
+ * @param {number[]|Float32Array} shape_normals
18
+ * @param {number} shape_length number of points in the shape
19
+ * @param {number[]|Float32Array} shape_transform 3x3 2d transform matrix for shape Note that matrix uses row-major order
20
+ */
21
+ export function make_ring_vertices(
22
+ out,
23
+ Px, Py, Pz,
24
+ N, B, T,
25
+ u, bend_angle,
26
+ shape, shape_normals, shape_length,
27
+ shape_transform
28
+ ) {
29
+ const out_positions = out.positions;
30
+ const out_normals = out.normals;
31
+ const out_uvs = out.uvs;
32
+
33
+ for (let i = 0; i <= shape_length; i++) {
34
+ const j = i % shape_length;
35
+ const i2 = j * 2;
36
+
37
+ const shape_x = shape[i2];
38
+ const shape_y = shape[i2 + 1];
39
+
40
+ // apply transform to the shape
41
+ const tx = shape_transform[0] * shape_x + shape_transform[3] * shape_y + shape_transform[6];
42
+ const ty = shape_transform[1] * shape_x + shape_transform[4] * shape_y + shape_transform[7];
43
+
44
+ const b_sin_x = ty * N.x;
45
+ const b_sin_y = ty * N.y;
46
+ const b_sin_z = ty * N.z;
47
+
48
+ const n_cos_x = tx * B.x;
49
+ const n_cos_y = tx * B.y;
50
+ const n_cos_z = tx * B.z;
51
+
52
+ // compute radial normal
53
+ const radial_normal_x_raw = (n_cos_x - b_sin_x);
54
+ const radial_normal_y_raw = (n_cos_y - b_sin_y);
55
+ const radial_normal_z_raw = (n_cos_z - b_sin_z);
56
+
57
+
58
+ const radial_normal_magnitude = Math.hypot(radial_normal_x_raw, radial_normal_y_raw, radial_normal_z_raw);
59
+
60
+ let normal_x;
61
+ let normal_y;
62
+ let normal_z;
63
+
64
+ if (radial_normal_magnitude !== 0) {
65
+
66
+ const normalization_factor = 1 / radial_normal_magnitude;
67
+
68
+ // re-normalize and compensate by bending angle
69
+ normal_x = radial_normal_x_raw * normalization_factor;
70
+ normal_y = radial_normal_y_raw * normalization_factor;
71
+ normal_z = radial_normal_z_raw * normalization_factor;
72
+
73
+ } else {
74
+ // normal would be 0 and produce NaNs, apply arbitrary offset
75
+ normal_x = T.x;
76
+ normal_y = T.y;
77
+ normal_z = T.z;
78
+ }
79
+
80
+ const shape_normal_x = shape_normals[i2];
81
+ const shape_normal_y = shape_normals[i2 + 1];
82
+
83
+ const t_nx = shape_transform[0] * shape_normal_x + shape_transform[3] * shape_normal_y + shape_transform[6];
84
+ const t_ny = shape_transform[1] * shape_normal_x + shape_transform[4] * shape_normal_y + shape_transform[7];
85
+
86
+
87
+ const nb_sin_x = t_ny * N.x;
88
+ const nb_sin_y = t_ny * N.y;
89
+ const nb_sin_z = t_ny * N.z;
90
+
91
+ const nn_cos_x = t_nx * B.x;
92
+ const nn_cos_y = t_nx * B.y;
93
+ const nn_cos_z = t_nx * B.z;
94
+
95
+ const n_normal_x_raw = (-nn_cos_x - nb_sin_x);
96
+ const n_normal_y_raw = (-nn_cos_y - nb_sin_y);
97
+ const n_normal_z_raw = (-nn_cos_z - nb_sin_z);
98
+
99
+ const n_normal_magnitude = Math.hypot(n_normal_x_raw, n_normal_y_raw, n_normal_z_raw);
100
+
101
+ const cv = out.cursor_vertices;
102
+ const cv3 = cv * 3;
103
+
104
+ if(n_normal_magnitude !== 0){
105
+ out_normals[cv3] = n_normal_x_raw / n_normal_magnitude;
106
+ out_normals[cv3 + 1] = n_normal_y_raw / n_normal_magnitude;
107
+ out_normals[cv3 + 2] = n_normal_z_raw / n_normal_magnitude;
108
+ }else {
109
+
110
+ out_normals[cv3] = T.x;
111
+ out_normals[cv3 + 1] = T.y;
112
+ out_normals[cv3 + 2] = T.z;
113
+ }
114
+
115
+ let bend_magnitude = 1;
116
+
117
+ // rotate bend normal by 90deg
118
+
119
+ const cross_x = bend_angle[1] * T.z - bend_angle[2] * T.y;
120
+ const cross_y = bend_angle[2] * T.x - bend_angle[0] * T.z;
121
+ const cross_z = bend_angle[0] * T.y - bend_angle[1] * T.x;
122
+
123
+ const rotated_bend_normal_length = Math.hypot(cross_x, cross_y, cross_z);
124
+ const bend_normal_length_inv = 1 / rotated_bend_normal_length;
125
+
126
+ // apply bend angle
127
+ const dot = v3_dot(
128
+ cross_x * bend_normal_length_inv, cross_y * bend_normal_length_inv, cross_z * bend_normal_length_inv,
129
+ normal_x, normal_y, normal_z
130
+ );
131
+
132
+ bend_magnitude += Math.abs(dot) * bend_angle[3];
133
+
134
+ // compute vertex position
135
+ const vx = Px + radial_normal_x_raw * bend_magnitude;
136
+ const vy = Py + radial_normal_y_raw * bend_magnitude;
137
+ const vz = Pz + radial_normal_z_raw * bend_magnitude;
138
+
139
+ out_positions[cv3] = vx;
140
+ out_positions[cv3 + 1] = vy;
141
+ out_positions[cv3 + 2] = vz;
142
+
143
+ // UV
144
+ const cv2 = cv * 2;
145
+
146
+ out_uvs[cv2] = u;
147
+ out_uvs[cv2 + 1] = i / shape_length;
148
+
149
+ out.cursor_vertices++;
150
+ }
151
+
152
+ }
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "productName": "Meep",
6
6
  "description": "production-ready JavaScript game engine based on Entity Component System Architecture",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.38.2",
8
+ "version": "2.39.1",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",
package/view/View.js CHANGED
@@ -10,7 +10,7 @@ import Signal from "../core/events/signal/Signal.js";
10
10
  import { SignalBinding } from "../core/events/signal/SignalBinding.js";
11
11
  import { assert } from "../core/assert.js";
12
12
  import Vector1 from "../core/geom/Vector1.js";
13
- import { compose3x3transform } from "./compose3x3transform.js";
13
+ import { m3_cm_compose_transform } from "./m3_cm_compose_transform.js";
14
14
 
15
15
 
16
16
  const scratch_m3_0 = new Float32Array(9);
@@ -26,7 +26,7 @@ function setElementTransform(domElement, position, scale, rotation) {
26
26
 
27
27
  const m3 = scratch_m3_0;
28
28
 
29
- compose3x3transform(m3, position.x, position.y, scale.x, scale.y,0,0, rotation);
29
+ m3_cm_compose_transform(m3, position.x, position.y, scale.x, scale.y,0,0, rotation);
30
30
 
31
31
 
32
32
  /*
@@ -1,4 +1,6 @@
1
1
  /**
2
+ * Composes column-major transform matrix
3
+ * Column-major matrices are used in CSS
2
4
  *
3
5
  * @param {number[]|Float32Array} result
4
6
  * @param {number} tX translation X offset
@@ -9,7 +11,13 @@
9
11
  * @param {number} cy center of rotation Y
10
12
  * @param {number} angle rotation angle
11
13
  */
12
- export function compose3x3transform(result, tX, tY, sX, sY, cx, cy, angle) {
14
+ export function m3_cm_compose_transform(
15
+ result,
16
+ tX, tY,
17
+ sX, sY,
18
+ cx, cy,
19
+ angle
20
+ ) {
13
21
  const neg_angle = -angle;
14
22
 
15
23
  const sin = Math.sin(neg_angle);
@@ -34,7 +42,7 @@ export function compose3x3transform(result, tX, tY, sX, sY, cx, cy, angle) {
34
42
  * @param {number} tX
35
43
  * @param {number} tY
36
44
  */
37
- export function m3_from_translation(result, tX, tY) {
45
+ export function m3_cm_from_translation(result, tX, tY) {
38
46
  result[0] = 1;
39
47
  result[1] = 0;
40
48
  result[2] = tX;
@@ -53,6 +61,6 @@ export function m3_from_translation(result, tX, tY) {
53
61
  * @param {mat3|number[]|Float32Array} m3
54
62
  * @returns {number}
55
63
  */
56
- export function m3_extract_rotation(m3) {
64
+ export function m3_cm_extract_rotation(m3) {
57
65
  return Math.atan2(m3[3], m3[0]);
58
66
  }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Composes row-major transform matrix, row-major format is used in OpenGL
3
+ *
4
+ * @param {number[]|Float32Array} result
5
+ * @param {number} tX translation X offset
6
+ * @param {number} tY translation Y offset
7
+ * @param {number} sX scale in X axis
8
+ * @param {number} sY scale in Y axis
9
+ * @param {number} cx center of rotation X
10
+ * @param {number} cy center of rotation Y
11
+ * @param {number} angle rotation angle
12
+ */
13
+ export function m3_rm_compose_transform(
14
+ result,
15
+ tX, tY,
16
+ sX, sY,
17
+ cx, cy,
18
+ angle
19
+ ) {
20
+ const neg_angle = -angle;
21
+
22
+ const sin = Math.sin(neg_angle);
23
+ const cos = Math.cos(neg_angle);
24
+
25
+ result[0] = sX * cos;
26
+ result[3] = sX * sin;
27
+ result[6] = -sX * (cos * cx + sin * cy) + cx + tX;
28
+
29
+ result[1] = -sY * sin;
30
+ result[4] = sY * cos;
31
+ result[7] = -sY * (-sin * cx + cos * cy) + cy + tY;
32
+
33
+ result[2] = 0;
34
+ result[5] = 0;
35
+ result[8] = 1;
36
+ }
37
+
38
+
39
+ export function m3_rm_extract_scale(result, m3) {
40
+ const scale_x = Math.hypot(m3[0], m3[3]);
41
+ const scale_y = Math.hypot(m3[1], m3[4]);
42
+
43
+ result[0] = scale_x;
44
+ result[1] = scale_y;
45
+ }
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * Performs matrix multiplication of 3x3 matrices
3
3
  * r = a * b
4
- * @param {number[]} a first matrix
5
- * @param {number[]} b second matrix
6
- * @param {number[]} r result matrix
4
+ * @param {number[]|Float32Array} a first matrix
5
+ * @param {number[]|Float32Array} b second matrix
6
+ * @param {number[]|Float32Array} r result matrix
7
7
  */
8
- export function multiplyMatrices3(a, b, r) {
8
+ export function m3_multiply(a, b, r) {
9
9
  //read out values of input matrices to support the case where result is written back into one of the inputs
10
10
  const a0 = a[0];
11
11
  const a1 = a[1];