@woosh/meep-engine 2.33.2 → 2.33.6

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 (98) hide show
  1. package/core/bvh2/aabb3/AABB3.js +18 -0
  2. package/core/bvh2/aabb3/aabb3_intersects_frustum.js +38 -0
  3. package/core/collection/RingBuffer.js +40 -10
  4. package/core/collection/array/copyArray.js +2 -2
  5. package/core/collection/array/typed/typed_array_copy.js +2 -2
  6. package/core/collection/table/RowFirstTableSpec.js +8 -0
  7. package/core/geom/3d/aabb/aabb3_compute_projected_area.js +16 -14
  8. package/core/geom/3d/aabb/aabb3_estimate_projected_area2.js +59 -0
  9. package/core/geom/3d/aabb/aabb3_matrix4_project.js +33 -0
  10. package/core/geom/3d/aabb/aabb3_matrix4_project.spec.js +41 -0
  11. package/core/geom/3d/plane/plane3_lerp.js +2 -2
  12. package/core/geom/3d/topology/TopoTriangle.js +2 -2
  13. package/core/geom/AABB2.d.ts +2 -0
  14. package/core/geom/Matrix4.js +4 -4
  15. package/core/geom/Vector3.js +5 -5
  16. package/core/model/reactive/model/terminal/ReactiveReference.js +7 -1
  17. package/core/process/undo/ActionProcessor.d.ts +2 -0
  18. package/core/process/undo/ActionProcessor.js +59 -1
  19. package/core/process/undo/ActionProcessor.spec.js +59 -0
  20. package/core/process/undo/Mark.js +2 -1
  21. package/engine/asset/loaders/GLTFAssetLoader.js +3 -3
  22. package/engine/asset/loaders/material/TextureAttachmensByMaterialType.js +11 -1
  23. package/engine/asset/loaders/material/TextureAttachment.js +12 -1
  24. package/engine/asset/loaders/material/bitmap2sampler_gl.js +19 -0
  25. package/engine/asset/loaders/material/computeImageBitmapEquality.js +7 -19
  26. package/engine/asset/loaders/material/computeTextureEquality.js +1 -1
  27. package/engine/asset/loaders/material/computeTextureHash.js +16 -14
  28. package/engine/asset/loaders/material/traverseMaterialTextures.js +2 -2
  29. package/engine/ecs/animation/OneBoneSurfaceAlignmentSolver.js +2 -2
  30. package/engine/ecs/animation/TwoBoneInverseKinematicsSolver.js +4 -4
  31. package/engine/graphics/GraphicsEngine.d.ts +2 -2
  32. package/engine/graphics/GraphicsEngine.js +20 -62
  33. package/engine/graphics/filter/FlipArrayInPlace.js +25 -0
  34. package/engine/graphics/filter/FlipArrayViaCanvas.js +13 -0
  35. package/engine/graphics/filter/ImageFilter.js +20 -51
  36. package/engine/graphics/geometry/bvh/buffered/BVHGeometryRaycaster.js +2 -2
  37. package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +25 -16
  38. package/engine/graphics/material/SplatMaterial.js +4 -97
  39. package/engine/graphics/material/optimization/MaterialOptimizationContext.js +598 -0
  40. package/engine/graphics/micron/build/PatchRepresentation.js +4 -43
  41. package/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +4 -75
  42. package/engine/graphics/micron/build/compute_buffer_geometry_byte_size.js +16 -0
  43. package/engine/graphics/micron/build/fill_patch_geometry_data.js +2 -0
  44. package/engine/graphics/micron/format/MicronGeometryBufferAttributeSpec.js +1 -1
  45. package/engine/graphics/micron/format/MicronGeometryPatch.js +17 -5
  46. package/engine/graphics/micron/format/MicronVertexDataSpec.js +25 -3
  47. package/engine/graphics/micron/prototypeVirtualGeometry.js +19 -12
  48. package/engine/graphics/micron/render/VirtualGeometryBuilder.js +34 -27
  49. package/engine/graphics/micron/render/instanced/AttributeDataTexture.js +2 -4
  50. package/engine/graphics/micron/render/instanced/PatchDataTextures.js +1 -0
  51. package/engine/graphics/micron/render/instanced/ThreeInstancedAdapter.js +1 -29
  52. package/engine/graphics/micron/render/refinement/ActivePatchList.js +43 -159
  53. package/engine/graphics/micron/render/refinement/ActivePatchRecord.js +4 -2
  54. package/engine/graphics/micron/render/refinement/RefinementSpec.js +98 -0
  55. package/engine/graphics/micron/render/refinement/array_normalize_plane.js +25 -0
  56. package/engine/graphics/micron/render/refinement/array_setFromProjectionMatrix.js +60 -0
  57. package/engine/graphics/micron/render/refinement/is_patch_facing_back.js +43 -0
  58. package/engine/graphics/micron/render/refinement/is_patch_visible.js +37 -0
  59. package/engine/graphics/micron/render/v1/MaterialVertexSpec.js +21 -0
  60. package/engine/graphics/micron/render/v1/VGThreeRenderer.js +12 -9
  61. package/engine/graphics/micron/util/unit_hemioct.spec.js +2 -2
  62. package/engine/graphics/render/buffer/buffers/ColorAndDepthFrameBuffer.js +5 -3
  63. package/engine/graphics/render/buffer/buffers/NormalFrameBuffer.js +12 -2
  64. package/engine/graphics/render/buffer/buffers/prototypeNormalFrameBuffer.js +8 -10
  65. package/engine/graphics/render/forward_plus/LightManager.js +5 -7
  66. package/engine/graphics/render/forward_plus/data/NumericType.js +9 -0
  67. package/engine/graphics/render/forward_plus/{TextureBackedMemoryRegion.js → data/TextureBackedMemoryRegion.js} +11 -190
  68. package/engine/graphics/render/forward_plus/data/channelCountToThreIntegerTextureType.js +16 -0
  69. package/engine/graphics/render/forward_plus/data/computeDataType.js +36 -0
  70. package/engine/graphics/render/forward_plus/data/computeThreeTextureFormat.js +18 -0
  71. package/engine/graphics/render/forward_plus/data/computeThreeTextureInternalFormatFromDataType.js +63 -0
  72. package/engine/graphics/render/forward_plus/data/computeThreeTextureTypeFromDataType.js +41 -0
  73. package/engine/graphics/render/forward_plus/plugin/ptototypeFPPlugin.js +1 -1
  74. package/engine/graphics/render/forward_plus/prototypeLightManager.js +1 -1
  75. package/engine/graphics/render/utils/ThreeBypassRenderer.js +1 -3
  76. package/engine/graphics/render/visibility/hiz/buffer/HierarchicalZBuffer.js +198 -0
  77. package/engine/graphics/render/visibility/hiz/buffer/ReductionShader.js +51 -0
  78. package/engine/graphics/render/visibility/hiz/buildCanvasViewFromTexture.js +179 -0
  79. package/engine/graphics/render/visibility/hiz/prototypeHiZ.js +435 -0
  80. package/engine/graphics/render/visibility/hiz/query/BatchOcclusionQuery.js +174 -0
  81. package/engine/graphics/render/visibility/hiz/query/QueryShader.js +173 -0
  82. package/engine/graphics/shadows/ShadowMapRenderer.js +12 -2
  83. package/engine/graphics/texture/atlas/TextureAtlas.js +1 -1
  84. package/engine/graphics/texture/cubemap/buildCubeURLs.js +13 -0
  85. package/engine/graphics/texture/cubemap/load_environment_map.d.ts +8 -0
  86. package/engine/graphics/texture/cubemap/load_environment_map.js +163 -0
  87. package/engine/graphics/texture/sampler/Sampler2D.js +18 -62
  88. package/engine/graphics/texture/sampler/convertTexture2Sampler2D.js +8 -4
  89. package/engine/graphics/texture/sampler/debug/paintSamplerOnHTML.js +3 -2
  90. package/engine/graphics/texture/sampler/differenceSampler.js +31 -0
  91. package/engine/graphics/texture/sampler/typedArrayConstructorByInstance.js +25 -0
  92. package/engine/graphics/texture/sampler/util/drawSamplerHTML.js +3 -2
  93. package/engine/graphics/texture/virtual/v2/ShaderUsage.js +6 -5
  94. package/engine/graphics/texture/virtual/v2/prototype.js +16 -6
  95. package/package.json +1 -1
  96. package/view/elements/progress/segmented/SegmentedResourceBarView.js +50 -15
  97. package/view/minimap/gl/MinimapMarkersGL.js +2 -2
  98. package/engine/graphics/micron/render/RefinementSpec.js +0 -110
@@ -8,6 +8,7 @@ import { computeAABB3PlaneSide } from "../../geom/3d/aabb/computeAABB3PlaneSide.
8
8
  import computeMortonCode from "../../geom/3d/morton/Morton.js";
9
9
  import { _boxSurfaceArea2, intersectRay, intersectSegment } from "./AABB3Math.js";
10
10
  import { aabb3_build_corners } from "../../geom/3d/aabb/aabb3_build_corners.js";
11
+ import { assert } from "../../assert.js";
11
12
 
12
13
 
13
14
  /**
@@ -718,6 +719,23 @@ AABB3.prototype.getCorners = function (result) {
718
719
  );
719
720
  };
720
721
 
722
+ /**
723
+ *
724
+ * @param {number[]|Float32Array|Float64Array} result
725
+ * @param {number} offset
726
+ */
727
+ AABB3.prototype.writeToArray = function (result, offset) {
728
+ assert.isNonNegativeInteger(offset, 'offset');
729
+
730
+ result[offset] = this.x0;
731
+ result[offset + 1] = this.y0;
732
+ result[offset + 2] = this.z0;
733
+
734
+ result[offset + 3] = this.x1;
735
+ result[offset + 4] = this.y1;
736
+ result[offset + 5] = this.z1;
737
+ };
738
+
721
739
  /**
722
740
  * @param {THREE.Plane} plane
723
741
  * @returns {int} 2,0,or -2; 2: above, -2 : below, 0 : intersects plane
@@ -0,0 +1,38 @@
1
+ import { aabb3_computeDistanceAbovePlane } from "../../geom/3d/aabb/aabb3_computeDistanceAbovePlane.js";
2
+
3
+ /**
4
+ *
5
+ * @param {Float32Array|number[]} aabb
6
+ * @param {Float32Array|number[]} frustum
7
+ * @return {boolean}
8
+ */
9
+ export function aabb3_intersects_frustum(aabb, frustum) {
10
+
11
+ const x0 = aabb[0];
12
+ const y0 = aabb[1];
13
+ const z0 = aabb[2];
14
+ const x1 = aabb[3];
15
+ const y1 = aabb[4];
16
+ const z1 = aabb[5];
17
+
18
+ for (let i = 0; i < 24; i += 4) {
19
+
20
+ const plane_n_x = frustum[i];
21
+ const plane_n_y = frustum[i + 1];
22
+ const plane_n_z = frustum[i + 2];
23
+ const plane_constant = frustum[i + 3];
24
+
25
+ const plane_distance = aabb3_computeDistanceAbovePlane(
26
+ plane_n_x, plane_n_y, plane_n_z, plane_constant,
27
+ x0, y0, z0,
28
+ x1, y1, z1
29
+ );
30
+
31
+ if (plane_distance < 0) {
32
+ return false;
33
+ }
34
+
35
+ }
36
+
37
+ return true;
38
+ }
@@ -7,14 +7,32 @@ export class RingBuffer {
7
7
  * @constructor
8
8
  */
9
9
  constructor(size) {
10
- assert.equal(typeof size, "number", `Type of 'size' must be 'number', was '${typeof size}' instead`);
10
+ assert.isNumber(size, 'size');
11
11
  assert.greaterThan(size, 0, `size`);
12
+ assert.isNonNegativeInteger(size, 'size');
12
13
 
14
+ /**
15
+ *
16
+ * @type {number}
17
+ */
13
18
  this.size = size;
14
19
 
20
+ /**
21
+ *
22
+ * @type {number}
23
+ */
15
24
  this.head = 0;
25
+
26
+ /**
27
+ *
28
+ * @type {number}
29
+ */
16
30
  this.tail = 0;
17
31
 
32
+ /**
33
+ *
34
+ * @type {number}
35
+ */
18
36
  this.count = 0;
19
37
 
20
38
  /**
@@ -62,13 +80,15 @@ export class RingBuffer {
62
80
 
63
81
  this.data[head] = element;
64
82
 
65
- const newHead = (head + 1) % this.size;
83
+ const size = this.size;
84
+
85
+ const newHead = (head + 1) % size;
66
86
 
67
87
  this.head = newHead;
68
88
 
69
- if (this.count === this.size) {
89
+ if (this.count === size) {
70
90
  //update tail
71
- this.tail = (this.tail + 1) % this.size;
91
+ this.tail = (this.tail + 1) % size;
72
92
  } else {
73
93
  this.count++;
74
94
  }
@@ -130,11 +150,15 @@ export class RingBuffer {
130
150
  * @returns {V|undefined}
131
151
  */
132
152
  removeIf(condition, thisArg) {
153
+ const count = this.count;
154
+ const size = this.size;
133
155
 
134
- for (let i = 0; i < this.count; i++) {
135
- const index = (this.tail + i) % this.size;
156
+ const tail = this.tail;
157
+ const data = this.data;
136
158
 
137
- const el = this.data[index];
159
+ for (let i = 0; i < count; i++) {
160
+ const index = (tail + i) % size;
161
+ const el = data[index];
138
162
 
139
163
  if (condition.call(thisArg, el)) {
140
164
 
@@ -151,10 +175,16 @@ export class RingBuffer {
151
175
  * @param {*} [thisArg]
152
176
  */
153
177
  forEach(visitor, thisArg) {
154
- for (let i = 0; i < this.count; i++) {
155
- const index = (this.tail + i) % this.size;
178
+ const count = this.count;
179
+ const size = this.size;
180
+
181
+ const tail = this.tail;
182
+ const data = this.data;
183
+
184
+ for (let i = 0; i < count; i++) {
156
185
 
157
- const el = this.data[index];
186
+ const index = (tail + i) % size;
187
+ const el = data[index];
158
188
 
159
189
  visitor.call(thisArg, el);
160
190
  }
@@ -25,9 +25,9 @@ export function copyArray(source, target) {
25
25
  * Follows Java System.arraycopy interface
26
26
  * Copy data from one array to another
27
27
  * @template T
28
- * @param {T[]|ArrayLike<T>|TypedArray} source The source array
28
+ * @param {T[]|ArrayLike<T>|TypedArray|Uint8ClampedArray|Uint8Array} source The source array
29
29
  * @param {number} source_position Starting position where to copy from inside the source array
30
- * @param {T[]|ArrayLike<T>|TypedArray} destination The destination array
30
+ * @param {T[]|ArrayLike<T>|TypedArray|Uint8ClampedArray|Uint8Array} destination The destination array
31
31
  * @param {number} destination_position Starting position where to copy to inside the destination array
32
32
  * @param {number} length How many elements should be copied
33
33
  */
@@ -3,8 +3,8 @@ import { array_copy } from "../copyArray.js";
3
3
 
4
4
  /**
5
5
  *
6
- * @param {TypedArray} source
7
- * @param {TypedArray} destination
6
+ * @param {TypedArray|Float32Array} source
7
+ * @param {TypedArray|Float32Array} destination
8
8
  */
9
9
  export function typed_array_copy(source, destination) {
10
10
  const destination_size = destination.length;
@@ -234,6 +234,14 @@ export function RowFirstTableSpec(types, endianType = EndianType.BigEndian) {
234
234
  }
235
235
  }
236
236
 
237
+ /**
238
+ *
239
+ * @return {number}
240
+ */
241
+ RowFirstTableSpec.prototype.getColumnCount = function () {
242
+ return this.types.length;
243
+ }
244
+
237
245
  /**
238
246
  *
239
247
  * @param {RowFirstTableSpec} other
@@ -1,5 +1,3 @@
1
- const scratch_corners = new Float32Array(24);
2
-
3
1
  /*
4
2
 
5
3
  one option of Algorithm (problem-orthogonal only, rectangles under perspective projection do not behave this way):
@@ -12,13 +10,13 @@ const scratch_corners = new Float32Array(24);
12
10
 
13
11
 
14
12
  /**
13
+ * NOTE: Consider using an optimized aabb projection method based on Jim Arvo's Graphics Gems algorithm: https://stackoverflow.com/a/58630206/3973586
15
14
  * Almost everything is inlined for speed
16
- * @param {AABB2} result
17
- * @param {AABB3} bounds
15
+ * @param {number[]} aabb_corners
18
16
  * @param {number[]|mat4|Float32Array} projection_matrix
17
+ * @returns {number}
19
18
  */
20
- export function aabb3_project_to_2d(result, bounds, projection_matrix) {
21
- bounds.getCorners(scratch_corners);
19
+ export function aabb3_project_to_2d(aabb_corners, projection_matrix) {
22
20
 
23
21
  // read matrix into local variables for speed
24
22
  const a0 = projection_matrix[0];
@@ -48,9 +46,9 @@ export function aabb3_project_to_2d(result, bounds, projection_matrix) {
48
46
  for (let i = 0; i < 24; i += 3) {
49
47
 
50
48
  // read corner
51
- const _x = scratch_corners[i];
52
- const _y = scratch_corners[i + 1];
53
- const _z = scratch_corners[i + 2];
49
+ const _x = aabb_corners[i];
50
+ const _y = aabb_corners[i + 1];
51
+ const _z = aabb_corners[i + 2];
54
52
 
55
53
  // apply transform
56
54
  const t_x = a0 * _x + b0 * _y + c0 * _z + d0;
@@ -58,7 +56,7 @@ export function aabb3_project_to_2d(result, bounds, projection_matrix) {
58
56
  const t_w = a3 * _x + b3 * _y + c3 * _z + d3;
59
57
 
60
58
  // perspective divide and normalization (-1,1 space projected to -0.5,0.5)
61
- const d = 0.5 / Math.abs(t_w);
59
+ const d = 0.5 / t_w;
62
60
 
63
61
  // finally compute screen-space coordinates of the corner
64
62
  const x = t_x * d;
@@ -67,18 +65,22 @@ export function aabb3_project_to_2d(result, bounds, projection_matrix) {
67
65
  if (x0 > x) {
68
66
  x0 = x;
69
67
  }
70
- if (y0 > y) {
71
- y0 = y;
72
- }
73
68
 
74
69
  if (x1 < x) {
75
70
  x1 = x;
76
71
  }
72
+
73
+ if (y0 > y) {
74
+ y0 = y;
75
+ }
77
76
  if (y1 < y) {
78
77
  y1 = y;
79
78
  }
80
79
 
81
80
  }
82
81
 
83
- result.set(x0, y0, x1, y1);
82
+ const dx = x1 - x0;
83
+ const dy = y1 - y0;
84
+
85
+ return dx * dy;
84
86
  }
@@ -0,0 +1,59 @@
1
+ const scratch_aabb = new Float32Array(4);
2
+
3
+
4
+ /**
5
+ * NOTE: tends to overestimate the area
6
+ * @param {number[]} aabb
7
+ * @param {number[]} matrix
8
+ * @return {number}
9
+ */
10
+ export function aabb3_estimate_projected_area2(aabb, matrix) {
11
+ // extract translation from matrix
12
+ scratch_aabb[0] = scratch_aabb[2] = matrix[12];
13
+ scratch_aabb[1] = scratch_aabb[3] = matrix[13];
14
+
15
+ // apply 3x3 matrix transform
16
+ for (let i = 0; i < 2; i++) {
17
+ for (let j = 0; j < 3; j++) {
18
+
19
+ const m_a = i * 4 + j;
20
+ const m_v = matrix[m_a];
21
+
22
+ const a = m_v * aabb[j];
23
+ const b = m_v * aabb[j + 3];
24
+
25
+ if (a < b) {
26
+ scratch_aabb[i] += a;
27
+ scratch_aabb[i + 2] += b;
28
+ } else {
29
+ scratch_aabb[i] += b;
30
+ scratch_aabb[i + 2] += a;
31
+ }
32
+ }
33
+ }
34
+
35
+
36
+ const x1 = scratch_aabb[2];
37
+ const x0 = scratch_aabb[0];
38
+
39
+ const dx = x1 - x0;
40
+
41
+ const y1 = scratch_aabb[3];
42
+ const y0 = scratch_aabb[1];
43
+
44
+ const dy = y1 - y0;
45
+ const a = dx * dy;
46
+
47
+ const cx = (aabb[0] + aabb[3]) * 0.5;
48
+ const cy = (aabb[1] + aabb[4]) * 0.5;
49
+ const cz = (aabb[2] + aabb[5]) * 0.5;
50
+
51
+ /* compute W projection value, using conservative estimate
52
+ @see https://twitter.com/SebAaltonen/status/1332332808979832832
53
+ @see https://gist.github.com/slembcke/b2b2af4dede1d5702a635dc2d0e45d54
54
+ */
55
+ const w = matrix[3] * cx + matrix[7] * cy + matrix[11] * cz + matrix[15];
56
+ const d = 0.5 / Math.abs(w);
57
+
58
+ return a * d * d;
59
+ }
@@ -0,0 +1,33 @@
1
+
2
+ /**
3
+ * NOTE: Based on Transforming Axis-Aligned Bounding Boxes by Jim Arvo from "Graphics Gems", Academic Press, 1990
4
+ * @param {number[]} result
5
+ * @param {number[]} aabb
6
+ * @param {number[]} matrix 4x4 matrix
7
+ */
8
+ export function aabb3_matrix4_project(result, aabb, matrix) {
9
+ // extract translation from matrix
10
+ result[0] = result[3] = matrix[12];
11
+ result[1] = result[4] = matrix[13];
12
+ result[2] = result[5] = matrix[14];
13
+
14
+ // apply 3x3 matrix transform
15
+ for (let i = 0; i < 3; i++) {
16
+ for (let j = 0; j < 3; j++) {
17
+
18
+ const m_a = i * 4 + j;
19
+ const m_v = matrix[m_a];
20
+
21
+ const a = m_v * aabb[j];
22
+ const b = m_v * aabb[j + 3];
23
+
24
+ if (a < b) {
25
+ result[i] += a;
26
+ result[i + 3] += b;
27
+ } else {
28
+ result[i] += b;
29
+ result[i + 3] += a;
30
+ }
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,41 @@
1
+ import { aabb3_matrix4_project } from "./aabb3_matrix4_project.js";
2
+
3
+ test('identity', () => {
4
+ const r = [];
5
+
6
+ aabb3_matrix4_project(r, [1, 2, 3, 5, 7, 9], [
7
+ 1, 0, 0, 0,
8
+ 0, 1, 0, 0,
9
+ 0, 0, 1, 0,
10
+ 0, 0, 0, 1
11
+ ]);
12
+
13
+ expect(r).toEqual([1, 2, 3, 5, 7, 9]);
14
+ });
15
+
16
+ test('translation case', () => {
17
+ const r = [];
18
+
19
+ aabb3_matrix4_project(r, [1, 2, 3, 5, 7, 9], [
20
+ 1, 0, 0, 0,
21
+ 0, 1, 0, 0,
22
+ 0, 0, 1, 0,
23
+ 13, -17, 23, 1
24
+ ]);
25
+
26
+ expect(r).toEqual([14, -15, 26, 18, -10, 32]);
27
+ });
28
+
29
+
30
+ test('scale case', () => {
31
+ const r = [];
32
+
33
+ aabb3_matrix4_project(r, [1, 2, 3, 5, 7, 9], [
34
+ -1, 0, 0, 0,
35
+ 0, 2, 0, 0,
36
+ 0, 0, -3, 0,
37
+ 0, 0, 0, 1
38
+ ]);
39
+
40
+ expect(r).toEqual([-5, 4, -27, -1, 14, -9]);
41
+ });
@@ -1,5 +1,5 @@
1
1
  import { lerp } from "../../../math/lerp.js";
2
- import Vector3, { v3_dot, v3Length_i } from "../../Vector3.js";
2
+ import Vector3, { v3_dot, v3_length } from "../../Vector3.js";
3
3
  import { plane3_intersect_plane } from "./plane3_intersect_plane.js";
4
4
 
5
5
  const v0 = new Vector3();
@@ -32,7 +32,7 @@ export function plane3_lerp(
32
32
  let normal_z = lerp(a_normal_z, b_normal_z, t);
33
33
 
34
34
  // normalize the normal vector
35
- const normal_length = v3Length_i(normal_x, normal_y, normal_z);
35
+ const normal_length = v3_length(normal_x, normal_y, normal_z);
36
36
  const normal_length_inv = 1 / normal_length;
37
37
 
38
38
  normal_x *= normal_length_inv;
@@ -1,4 +1,4 @@
1
- import { v3Length_i } from "../../Vector3.js";
1
+ import { v3_length } from "../../Vector3.js";
2
2
  import { array_replace_all } from "../../../collection/array/array_replace_all.js";
3
3
  import { array_push_if_unique } from "../../../collection/array/array_push_if_unique.js";
4
4
  import { assert } from "../../../assert.js";
@@ -40,7 +40,7 @@ export function compute_triangle_normal(result, vA, vB, vC) {
40
40
  const crossY = vCBz * vABx - vCBx * vABz;
41
41
  const crossZ = vCBx * vABy - vCBy * vABx;
42
42
 
43
- const l = v3Length_i(crossX, crossY, crossZ);
43
+ const l = v3_length(crossX, crossY, crossZ);
44
44
 
45
45
  if (l === 0) {
46
46
  // degenerate triangle, provide arbitrary invalid result
@@ -20,5 +20,7 @@ export default class AABB2 {
20
20
 
21
21
  grow(size: number): void
22
22
 
23
+ shrink(size: number): void
24
+
23
25
  setNegativelyInfiniteBounds(): void
24
26
  }
@@ -1,4 +1,4 @@
1
- import Vector3, { v3Length_i } from "./Vector3.js";
1
+ import Vector3, { v3_length } from "./Vector3.js";
2
2
 
3
3
  const _z = new Vector3();
4
4
  const _x = new Vector3();
@@ -192,9 +192,9 @@ export class Matrix4 {
192
192
  decompose(position, rotation, scale) {
193
193
 
194
194
 
195
- var sx = v3Length_i(this.a0, this.a1, this.a2);
196
- var sy = v3Length_i(this.b0, this.b1, this.b2);
197
- var sz = v3Length_i(this.c0, this.c1, this.c2);
195
+ var sx = v3_length(this.a0, this.a1, this.a2);
196
+ var sy = v3_length(this.b0, this.b1, this.b2);
197
+ var sz = v3_length(this.c0, this.c1, this.c2);
198
198
 
199
199
  // if determine is negative, we need to invert one scale
200
200
  var det = this.determinant();
@@ -883,7 +883,7 @@ class Vector3 {
883
883
  * @returns {number}
884
884
  */
885
885
  static distance(a, b) {
886
- return v3Length_i(a.x - b.x, a.y - b.y, a.z - b.z);
886
+ return v3_length(a.x - b.x, a.y - b.y, a.z - b.z);
887
887
  }
888
888
  }
889
889
 
@@ -1008,10 +1008,10 @@ export function v3_distance_sqr(x0, y0, z0, x1, y1, z1) {
1008
1008
 
1009
1009
 
1010
1010
  function v3Length(v) {
1011
- return v3Length_i(v.x, v.y, v.z);
1011
+ return v3_length(v.x, v.y, v.z);
1012
1012
  }
1013
1013
 
1014
- export function v3Length_i(x, y, z) {
1014
+ export function v3_length(x, y, z) {
1015
1015
  return Math.sqrt(v3_length_sqr(x, y, z));
1016
1016
  }
1017
1017
 
@@ -1040,8 +1040,8 @@ export function v3_length_sqr(x, y, z) {
1040
1040
  export function v3_angleBetween(x0, y0, z0, x1, y1, z1) {
1041
1041
  const d = v3_dot(x0, y0, z0, x1, y1, z1);
1042
1042
 
1043
- const magnitude_0 = v3Length_i(x0, y0, z0);
1044
- const magnitude_1 = v3Length_i(x1, y1, z1);
1043
+ const magnitude_0 = v3_length(x0, y0, z0);
1044
+ const magnitude_1 = v3_length(x1, y1, z1);
1045
1045
 
1046
1046
  const l = magnitude_0 * magnitude_1;
1047
1047
 
@@ -64,9 +64,15 @@ export class ReactiveReference extends ReactiveExpression {
64
64
  assert.notEqual(source, null, 'source is null');
65
65
  assert.notEqual(source, undefined, 'source is undefined');
66
66
  assert.typeOf(source.onChanged, 'object', 'source.onChanged');
67
- assert.typeOf(source.onChanged.add, 'function', 'source.onChanged.add');
67
+ assert.isFunction(source.onChanged.add, 'source.onChanged.add');
68
68
 
69
69
  if (this.source !== null) {
70
+
71
+ if (this.source === source) {
72
+ // already connected to this source, do nothing
73
+ return;
74
+ }
75
+
70
76
  throw new Error('source is already set. Already connected');
71
77
  }
72
78
 
@@ -17,6 +17,8 @@ export class ActionProcessor<CTX, MD> {
17
17
 
18
18
  redo(): void
19
19
 
20
+ navigateTo(mark: MD): boolean
21
+
20
22
  doMany(actions: Action<CTX>[]): void
21
23
 
22
24
  do(action: Action<CTX>): this
@@ -202,6 +202,64 @@ export class ActionProcessor {
202
202
 
203
203
  }
204
204
 
205
+ /**
206
+ * Navigate to a specific mark back or forward
207
+ * @param {M} mark_descriptor specific mark description
208
+ * @returns {boolean} true on success, false otherwise (e.g. mark not found)
209
+ * @throws {Error} when mark is non-unique
210
+ */
211
+ navigateTo(mark_descriptor) {
212
+ // find all matching marks
213
+ const matching_marks = this.marks.filter(m => m.description === mark_descriptor);
214
+
215
+ if (matching_marks.length === 0) {
216
+ // not found
217
+ return false;
218
+ }
219
+
220
+ if (matching_marks.length > 1) {
221
+ throw new Error(`Attempting to navigate to a non-unique mark descriptor '${mark_descriptor}'. Mark descriptor must be unique, instead ${matching_marks.length} matches were found`);
222
+ }
223
+
224
+ const match = matching_marks[0];
225
+
226
+ const target = match.index;
227
+
228
+ let temp_cursor = this.cursor;
229
+
230
+ if (target > this.cursor) {
231
+
232
+ while (target > this.cursor) {
233
+ // navigate forward
234
+ this.redo();
235
+
236
+ if (temp_cursor === this.cursor) {
237
+ // no cursor change
238
+ return false;
239
+ }
240
+
241
+ temp_cursor = this.cursor;
242
+ }
243
+
244
+ } else if (target < this.cursor) {
245
+
246
+ while (target < this.cursor) {
247
+ // navigate backwards
248
+ this.undo();
249
+
250
+ if (temp_cursor === this.cursor) {
251
+ // no cursor change
252
+ return false;
253
+ }
254
+
255
+ temp_cursor = this.cursor;
256
+ }
257
+
258
+ }
259
+
260
+ return true;
261
+ }
262
+
205
263
  undo() {
206
264
  let markAddress = 0;
207
265
  /**
@@ -226,7 +284,7 @@ export class ActionProcessor {
226
284
  markAddress = 0;
227
285
  }
228
286
 
229
- //keep rewinding until we hit mark
287
+ //keep rewinding until we hit a mark
230
288
  while (this.cursor > markAddress && this.cursor > 0) {
231
289
  this.cursor--;
232
290
  const action = this.history.get(this.cursor);
@@ -0,0 +1,59 @@
1
+ import { ActionProcessor } from "./ActionProcessor.js";
2
+ import { Action } from "./Action.js";
3
+
4
+ class DummyContext {
5
+ constructor() {
6
+ this.value = 0;
7
+ }
8
+ }
9
+
10
+ class DummyAction extends Action {
11
+ apply(context) {
12
+ // do nothing
13
+ context.value++;
14
+ }
15
+
16
+ revert(context) {
17
+ context.value--;
18
+ }
19
+ }
20
+
21
+ test('navigateTo backwards', () => {
22
+ const ctx = new DummyContext();
23
+ const ap = new ActionProcessor(ctx);
24
+
25
+ ap.mark('a');
26
+ ap.do(new DummyAction());
27
+ ap.mark('b');
28
+ ap.do(new DummyAction());
29
+
30
+ expect(ap.navigateTo('a')).toBe(true);
31
+
32
+ expect(ctx.value).toBe(0);
33
+ });
34
+
35
+ test('navigateTo forward', () => {
36
+ const ctx = new DummyContext();
37
+ const ap = new ActionProcessor(ctx);
38
+
39
+ ap.mark('a');
40
+ ap.do(new DummyAction());
41
+ ap.mark('b');
42
+ ap.do(new DummyAction());
43
+ ap.mark('c');
44
+
45
+ ap.undo();
46
+ ap.undo();
47
+ ap.undo();
48
+
49
+ expect(ap.navigateTo('c')).toBe(true);
50
+
51
+ expect(ctx.value).toBe(2);
52
+ });
53
+
54
+ test('navigateTo non-existent on empty', () => {
55
+ const ctx = new DummyContext();
56
+ const ap = new ActionProcessor(ctx);
57
+
58
+ expect(ap.navigateTo('a')).toBe(false);
59
+ });
@@ -8,13 +8,14 @@ export class Mark {
8
8
  * @constructor
9
9
  */
10
10
  constructor(index, description) {
11
- assert.typeOf(index, 'number', 'index');
11
+ assert.isNumber(index, 'index');
12
12
 
13
13
  /**
14
14
  *
15
15
  * @type {number}
16
16
  */
17
17
  this.index = index;
18
+
18
19
  /**
19
20
  *
20
21
  * @type {D}