@woosh/meep-engine 2.50.0 → 2.50.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +39 -0
  2. package/editor/Editor.js +5 -1
  3. package/editor/SelectionVisualizer.js +9 -3
  4. package/editor/tools/paint/TerrainTexturePaintTool.js +6 -1
  5. package/editor/view/EditorView.js +5 -1
  6. package/editor/view/ecs/EntityList.js +48 -37
  7. package/editor/view/makeEntityDecorators.js +125 -0
  8. package/package.json +7 -3
  9. package/samples/generation/README.md +6 -0
  10. package/{src/generation/example → samples/generation}/SampleGenerator0.js +60 -46
  11. package/{src/generation/example → samples/generation}/filters/SampleGroundMoistureFilter.js +14 -12
  12. package/samples/generation/filters/SampleNoise20_0.js +3 -0
  13. package/{src/generation/example → samples/generation}/generators/interactive/mir_generator_place_buff_objects.js +29 -23
  14. package/{src/generation/example → samples/generation}/generators/mir_generator_place_bases.js +33 -25
  15. package/{src/generation/example → samples/generation}/generators/mir_generator_place_road_decorators.js +16 -14
  16. package/samples/generation/generators/mir_generator_place_starting_point.js +60 -0
  17. package/{src/generation/example → samples/generation}/grid/configureMirGrid.js +2 -2
  18. package/{src/generation/example → samples/generation}/main.js +29 -28
  19. package/{src/generation/example → samples/generation}/rules/matcher_not_play_area.js +1 -1
  20. package/samples/generation/rules/matcher_play_area.js +5 -0
  21. package/{src/generation/example → samples/generation}/rules/matcher_tag_not_traversable.js +1 -1
  22. package/samples/generation/rules/matcher_tag_occupied.js +5 -0
  23. package/samples/generation/rules/matcher_tag_traversable.js +5 -0
  24. package/{src/generation/example → samples/generation}/rules/matcher_tag_traversable_unoccupied.js +1 -1
  25. package/{src/generation/example → samples/generation}/rules/matcher_tag_unoccupied.js +1 -1
  26. package/{src/generation/example → samples/generation}/rules/mir_matcher_attack_corridor.js +3 -3
  27. package/{src/generation/example → samples/generation}/themes/SampleTheme0.js +57 -47
  28. package/{src/generation/example → samples/generation}/themes/SampleTheme1.js +4 -4
  29. package/{src/generation/example → samples/generation}/themes/SampleTheme2.js +4 -4
  30. package/src/core/geom/3d/aabb/aabb3_detailed_volume_intersection.js +4 -4
  31. package/src/core/geom/3d/frustum/frustum3_computeNearestPointToPoint.js +5 -5
  32. package/src/core/geom/3d/plane/is_point_within_planes.js +1 -1
  33. package/src/core/geom/3d/plane/lerp_planes_to_array.js +2 -0
  34. package/src/core/geom/3d/plane/{plane_computeConvex3PlaneIntersection.js → plane3_compute_convex_3_plane_intersection.js} +1 -1
  35. package/src/core/geom/3d/plane/{plane3_computeLineSegmentIntersection.js → plane3_compute_line_segment_intersection.js} +1 -1
  36. package/src/core/geom/3d/plane/{computePlanePlaneIntersection.js → plane3_compute_plane_intersection.js} +15 -11
  37. package/src/core/geom/3d/plane/{computePlaneLineIntersection.js → plane3_compute_ray_intersection.js} +5 -1
  38. package/src/core/geom/3d/plane/plane3_intersect_plane.js +14 -0
  39. package/src/core/geom/3d/plane/{plane_three_compute_convex3_plane_intersection.js → plane3_three_compute_convex_3_plane_intersection.js} +7 -4
  40. package/src/core/geom/3d/plane/planeRayIntersection.js +2 -2
  41. package/src/core/geom/3d/tetrahedra/compute_bounding_simplex_3d.js +5 -5
  42. package/src/core/geom/Vector3.d.ts +4 -2
  43. package/src/engine/ecs/EntityBlueprint.d.ts +14 -0
  44. package/src/engine/ecs/EntityBlueprint.js +2 -2
  45. package/src/engine/ecs/EntityBlueprint.spec.js +52 -0
  46. package/src/engine/ecs/EntityBuilder.js +8 -0
  47. package/src/engine/ecs/EntityManager.d.ts +1 -0
  48. package/src/engine/ecs/EntityManager.js +16 -8
  49. package/src/engine/ecs/EntityManager.spec.js +62 -1
  50. package/src/engine/ecs/System.js +8 -2
  51. package/src/engine/ecs/{Blueprint.js → storage/json/Blueprint.js} +1 -1
  52. package/src/engine/ecs/{EntityFactory.js → storage/json/EntityFactory.js} +1 -1
  53. package/src/engine/ecs/storage/{JSONDeSerializer.js → json/JSONDeSerializer.js} +4 -4
  54. package/src/engine/ecs/storage/json/README.md +5 -0
  55. package/src/engine/graphics/ecs/camera/Camera.js +2 -2
  56. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.spec.js +5 -0
  57. package/src/engine/graphics/ecs/path/entity/EntityPath.js +1 -1
  58. package/src/engine/graphics/render/forward_plus/computeFrustumCorners.js +10 -10
  59. package/src/engine/graphics/render/layers/RenderLayerUtils.js +3 -3
  60. package/src/engine/intelligence/behavior/Behavior.d.ts +5 -0
  61. package/src/engine/intelligence/behavior/Behavior.js +19 -0
  62. package/src/engine/intelligence/behavior/SelectorBehavior.js +4 -2
  63. package/src/engine/intelligence/behavior/composite/ParallelBehavior.js +2 -0
  64. package/src/engine/intelligence/behavior/composite/SequenceBehavior.js +3 -1
  65. package/src/engine/intelligence/behavior/decorator/AbstractDecoratorBehavior.js +2 -3
  66. package/src/engine/intelligence/behavior/decorator/RepeatBehavior.js +2 -2
  67. package/src/engine/intelligence/behavior/primitive/SucceedingBehavior.js +1 -1
  68. package/src/engine/scene/SceneManager.spec.js +24 -0
  69. package/src/generation/grid/GridData.js +8 -2
  70. package/src/generation/grid/GridData.spec.js +5 -0
  71. package/src/generation/grid/generation/discrete/GridTaskConnectRooms.js +1 -1
  72. package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +2 -2
  73. package/src/generation/markers/actions/MarkerNodeActionEntityPlacement.js +1 -1
  74. package/src/view/common/VirtualListView.js +2 -0
  75. package/src/view/minimap/dom/MinimapCameraView.js +3 -3
  76. package/src/core/geom/3d/plane/computePlaneRayIntersection.js +0 -55
  77. package/src/generation/example/filters/SampleNoise20_0.js +0 -3
  78. package/src/generation/example/generators/mir_generator_place_starting_point.js +0 -52
  79. package/src/generation/example/rules/matcher_play_area.js +0 -5
  80. package/src/generation/example/rules/matcher_tag_occupied.js +0 -5
  81. package/src/generation/example/rules/matcher_tag_traversable.js +0 -5
  82. package/src/generation/grid/MarkerMatchCounter.js +0 -25
  83. /package/{src/generation/example → samples/generation}/grid/MirGridLayers.js +0 -0
  84. /package/src/engine/ecs/storage/{JSONSerializer.js → json/JSONSerializer.js} +0 -0
@@ -2,7 +2,7 @@ import { v3_distance_above_plane } from "../../v3_distance_above_plane.js";
2
2
  import { EPSILON } from "../../../math/EPSILON.js";
3
3
 
4
4
  /**
5
- *
5
+ * Checks if point lies in a volume bound by a number of planes, such a camera frustum
6
6
  * @param {number} x
7
7
  * @param {number} y
8
8
  * @param {number} z
@@ -48,4 +48,6 @@ function lerp_planes_to_array(
48
48
  const n_z = a_normal_z * scale_0 + b_normal_z * scale_1;
49
49
 
50
50
  //compute R
51
+
52
+ throw new Error("Not Implemented");
51
53
  }
@@ -23,7 +23,7 @@ import { v3_dot } from "../../v3_dot.js";
23
23
  * @param {number} c_constant
24
24
  * @returns {boolean} true if planes intersect, false otherwise
25
25
  */
26
- export function plane_computeConvex3PlaneIntersection(
26
+ export function plane3_compute_convex_3_plane_intersection(
27
27
  result, result_offset,
28
28
  a_normal_x, a_normal_y, a_normal_z, a_constant,
29
29
  b_normal_x, b_normal_y, b_normal_z, b_constant,
@@ -16,7 +16,7 @@ import { v3_dot } from "../../v3_dot.js";
16
16
  * @param {number} dist
17
17
  * @returns {boolean}
18
18
  */
19
- export function plane3_computeLineSegmentIntersection(
19
+ export function plane3_compute_line_segment_intersection(
20
20
  result, result_offset,
21
21
  x0, y0, z0,
22
22
  x1, y1, z1,
@@ -3,21 +3,25 @@
3
3
  Algorithm taken from http://geomalgorithms.com/a05-_intersect-1.html. See the
4
4
  section 'Intersection of 2 Planes' and specifically the subsection
5
5
  (A) Direct Linear Equation
6
+ TODO consolidate with {@link plane3_intersect_plane} as they seem to do the same thing
6
7
  @param {Plane} p1
7
8
  @param {Plane} p2
8
- @param {Vector3} point
9
- @param {Vector3} direction
9
+ @param {Vector3} out_point
10
+ @param {Vector3} out_direction
10
11
  @returns {boolean}
11
12
  */
12
- export function computePlanePlaneIntersection(p1, p2, point, direction) {
13
+ export function plane3_compute_plane_intersection(
14
+ p1, p2,
15
+ out_point, out_direction
16
+ ) {
13
17
 
14
18
  // the cross product gives us the direction of the line at the intersection
15
19
  // of the two planes, and gives us an easy way to check if the two planes
16
20
  // are parallel - the cross product will have zero magnitude
17
21
 
18
- direction.crossVectors(p1.normal, p2.normal);
22
+ out_direction.crossVectors(p1.normal, p2.normal);
19
23
 
20
- const magnitude = direction.lengthSqr();
24
+ const magnitude = out_direction.lengthSqr();
21
25
 
22
26
  if (magnitude === 0) {
23
27
  return false;
@@ -28,16 +32,16 @@ export function computePlanePlaneIntersection(p1, p2, point, direction) {
28
32
  // to set as zero by seeing which has the largest absolute value in the
29
33
  // directional vector
30
34
 
31
- const X = Math.abs(direction.x);
32
- const Y = Math.abs(direction.y);
33
- const Z = Math.abs(direction.z);
35
+ const X = Math.abs(out_direction.x);
36
+ const Y = Math.abs(out_direction.y);
37
+ const Z = Math.abs(out_direction.z);
34
38
 
35
39
  if (Z >= X && Z >= Y) {
36
- solveIntersectingPoint('z', 'x', 'y', p1, p2, point);
40
+ solveIntersectingPoint('z', 'x', 'y', p1, p2, out_point);
37
41
  } else if (Y >= Z && Y >= X) {
38
- solveIntersectingPoint('y', 'z', 'x', p1, p2, point);
42
+ solveIntersectingPoint('y', 'z', 'x', p1, p2, out_point);
39
43
  } else {
40
- solveIntersectingPoint('x', 'y', 'z', p1, p2, point);
44
+ solveIntersectingPoint('x', 'y', 'z', p1, p2, out_point);
41
45
  }
42
46
 
43
47
  return true;
@@ -15,7 +15,7 @@ import { v3_dot } from "../../v3_dot.js";
15
15
  * @param {number} dist Plane distance
16
16
  * @returns {boolean} true if intersection is found, false otherwise
17
17
  */
18
- export function computePlaneLineIntersection(
18
+ export function plane3_compute_ray_intersection(
19
19
  out,
20
20
  originX, originY, originZ,
21
21
  directionX, directionY, directionZ,
@@ -29,6 +29,10 @@ export function computePlaneLineIntersection(
29
29
 
30
30
  const t = -p / denom;
31
31
 
32
+ if (t < 0) {
33
+ return false;
34
+ }
35
+
32
36
  out.set(
33
37
  directionX * t + originX,
34
38
  directionY * t + originY,
@@ -3,6 +3,20 @@ import Vector3 from "../../Vector3.js";
3
3
  const v0 = new Vector3();
4
4
  const v1 = new Vector3();
5
5
 
6
+ /**
7
+ *
8
+ * @param {Vector3} result_point
9
+ * @param {Vector3} result_direction
10
+ * @param {number} a_normal_x
11
+ * @param {number} a_normal_y
12
+ * @param {number} a_normal_z
13
+ * @param {number} a_constant
14
+ * @param {number} b_normal_x
15
+ * @param {number} b_normal_y
16
+ * @param {number} b_normal_z
17
+ * @param {number} b_constant
18
+ * @return {boolean}
19
+ */
6
20
  export function plane3_intersect_plane(
7
21
  result_point,
8
22
  result_direction,
@@ -1,7 +1,7 @@
1
- import { plane_computeConvex3PlaneIntersection } from "./plane_computeConvex3PlaneIntersection.js";
1
+ import { plane3_compute_convex_3_plane_intersection } from "./plane3_compute_convex_3_plane_intersection.js";
2
2
 
3
3
  /**
4
- *
4
+ * Works with THREE.js Plane representations as inputs
5
5
  * @param {number[]} result
6
6
  * @param {number} result_offset
7
7
  * @param {Plane} a
@@ -9,12 +9,15 @@ import { plane_computeConvex3PlaneIntersection } from "./plane_computeConvex3Pla
9
9
  * @param {Plane} c
10
10
  * @returns {boolean}
11
11
  */
12
- export function plane_three_computeConvex3PlaneIntersection(result, result_offset, a, b, c) {
12
+ export function plane3_three_compute_convex_3_plane_intersection(
13
+ result, result_offset,
14
+ a, b, c
15
+ ) {
13
16
  const a_normal = a.normal;
14
17
  const b_normal = b.normal;
15
18
  const c_normal = c.normal;
16
19
 
17
- return plane_computeConvex3PlaneIntersection(
20
+ return plane3_compute_convex_3_plane_intersection(
18
21
  result,
19
22
  result_offset,
20
23
  a_normal.x, a_normal.y, a_normal.z, a.constant,
@@ -1,4 +1,4 @@
1
- import { computePlaneRayIntersection } from "./computePlaneRayIntersection.js";
1
+ import { plane3_compute_ray_intersection } from "./plane3_compute_ray_intersection.js";
2
2
 
3
3
  /**
4
4
  *
@@ -10,5 +10,5 @@ import { computePlaneRayIntersection } from "./computePlaneRayIntersection.js";
10
10
  * @returns {boolean} true if intersection is found, false otherwise
11
11
  */
12
12
  export function planeRayIntersection(out, origin, direction, normal, dist) {
13
- return computePlaneRayIntersection(out, origin.x, origin.y, origin.z, direction.x, direction.y, direction.z, normal.x, normal.y, normal.z, dist)
13
+ return plane3_compute_ray_intersection(out, origin.x, origin.y, origin.z, direction.x, direction.y, direction.z, normal.x, normal.y, normal.z, dist)
14
14
  }
@@ -1,7 +1,7 @@
1
1
  import { v3_dot } from "../../v3_dot.js";
2
2
  import { min2 } from "../../../math/min2.js";
3
3
  import { max2 } from "../../../math/max2.js";
4
- import { plane_computeConvex3PlaneIntersection } from "../plane/plane_computeConvex3PlaneIntersection.js";
4
+ import { plane3_compute_convex_3_plane_intersection } from "../plane/plane3_compute_convex_3_plane_intersection.js";
5
5
 
6
6
  /**
7
7
  * Planes used to bound the simplex
@@ -152,25 +152,25 @@ export function compute_bounding_simplex_3d(
152
152
  min_d -= padding;
153
153
 
154
154
  // let's compute intersection points for each tri-plane combination to find simplex corners
155
- plane_computeConvex3PlaneIntersection(
155
+ plane3_compute_convex_3_plane_intersection(
156
156
  result, result_offset,
157
157
  SIMPLEX_PROJECTION_AXES[0], SIMPLEX_PROJECTION_AXES[1], SIMPLEX_PROJECTION_AXES[2], -min_a,
158
158
  SIMPLEX_PROJECTION_AXES[3], SIMPLEX_PROJECTION_AXES[4], SIMPLEX_PROJECTION_AXES[5], -min_b,
159
159
  SIMPLEX_PROJECTION_AXES[9], SIMPLEX_PROJECTION_AXES[10], SIMPLEX_PROJECTION_AXES[11], -min_d
160
160
  );
161
- plane_computeConvex3PlaneIntersection(
161
+ plane3_compute_convex_3_plane_intersection(
162
162
  result, result_offset + 3,
163
163
  SIMPLEX_PROJECTION_AXES[3], SIMPLEX_PROJECTION_AXES[4], SIMPLEX_PROJECTION_AXES[5], -min_b,
164
164
  SIMPLEX_PROJECTION_AXES[6], SIMPLEX_PROJECTION_AXES[7], SIMPLEX_PROJECTION_AXES[8], -min_c,
165
165
  SIMPLEX_PROJECTION_AXES[9], SIMPLEX_PROJECTION_AXES[10], SIMPLEX_PROJECTION_AXES[11], -min_d
166
166
  );
167
- plane_computeConvex3PlaneIntersection(
167
+ plane3_compute_convex_3_plane_intersection(
168
168
  result, result_offset + 6,
169
169
  SIMPLEX_PROJECTION_AXES[0], SIMPLEX_PROJECTION_AXES[1], SIMPLEX_PROJECTION_AXES[2], -min_a,
170
170
  SIMPLEX_PROJECTION_AXES[3], SIMPLEX_PROJECTION_AXES[4], SIMPLEX_PROJECTION_AXES[5], -min_b,
171
171
  SIMPLEX_PROJECTION_AXES[6], SIMPLEX_PROJECTION_AXES[7], SIMPLEX_PROJECTION_AXES[8], -min_c
172
172
  );
173
- plane_computeConvex3PlaneIntersection(
173
+ plane3_compute_convex_3_plane_intersection(
174
174
  result, result_offset + 9,
175
175
  SIMPLEX_PROJECTION_AXES[6], SIMPLEX_PROJECTION_AXES[7], SIMPLEX_PROJECTION_AXES[8], -min_c,
176
176
  SIMPLEX_PROJECTION_AXES[0], SIMPLEX_PROJECTION_AXES[1], SIMPLEX_PROJECTION_AXES[2], -min_a,
@@ -69,14 +69,16 @@ export default class Vector3 implements Vector3Like {
69
69
 
70
70
  clone(): Vector3
71
71
 
72
- equals(other:Vector3Like):boolean
72
+ equals(other: Vector3Like): boolean
73
73
 
74
- roughlyEquals(other:Vector3Like,tolerance?:number):boolean
74
+ roughlyEquals(other: Vector3Like, tolerance?: number): boolean
75
75
 
76
76
  normalize(): void
77
77
 
78
78
  length(): number
79
79
 
80
+ lengthSqr(): number
81
+
80
82
  lerpVectors(a: Vector3Like, b: Vector3Like, fraction: number): void
81
83
 
82
84
  distanceTo(other: Vector3Like): number
@@ -1,3 +1,17 @@
1
+ import EntityBuilder from "./EntityBuilder";
2
+
3
+ interface Type<T> extends Function {
4
+ new(...args: any[]): T;
5
+ }
6
+
1
7
  export class EntityBlueprint {
2
8
  static from(components: any[]): EntityBlueprint
9
+
10
+ add<T>(component: T): void
11
+
12
+ addJSON<T>(klass: Type<T>, json: any): void
13
+
14
+ clear(): void
15
+
16
+ build(seed?: object): EntityBuilder
3
17
  }
@@ -140,10 +140,10 @@ export class EntityBlueprint {
140
140
 
141
141
 
142
142
  /**
143
- *
143
+ * @param {object} [templateSeed]
144
144
  * @return {EntityBuilder}
145
145
  */
146
- buildEntityBuilder(templateSeed) {
146
+ build(templateSeed) {
147
147
  const eb = new EntityBuilder();
148
148
 
149
149
  this.componentnts.forEach((template, ComponentClass) => {
@@ -0,0 +1,52 @@
1
+ import { EntityBlueprint } from "./EntityBlueprint.js";
2
+ import EntityBuilder from "./EntityBuilder.js";
3
+
4
+ test("constructor does not throw", () => {
5
+
6
+ expect(() => new EntityBlueprint()).not.toThrow();
7
+
8
+ });
9
+
10
+ test("build empty", () => {
11
+ const blueprint = new EntityBlueprint();
12
+
13
+ const entity = blueprint.build();
14
+
15
+ expect(entity).toBeDefined();
16
+ expect(entity).toBeInstanceOf(EntityBuilder);
17
+ expect(entity.count).toEqual(0);
18
+ });
19
+
20
+ class DummyComponent {
21
+ a = 0
22
+
23
+ fromJSON({ a }) {
24
+ this.a = a;
25
+ }
26
+ }
27
+
28
+ test("a copy of the added component ends up on built entity", () => {
29
+
30
+ const blueprint = new EntityBlueprint();
31
+
32
+ blueprint.addJSON(DummyComponent, { a: 7 });
33
+
34
+ const entity = blueprint.build();
35
+
36
+ const retrieved = entity.getComponent(DummyComponent);
37
+
38
+ expect(retrieved).toBeInstanceOf(DummyComponent);
39
+ expect(retrieved.a).toEqual(7);
40
+ });
41
+
42
+ test("seed template", () => {
43
+ const blueprint = new EntityBlueprint();
44
+
45
+ blueprint.addJSON(DummyComponent, { a: '$x' });
46
+
47
+ const entity = blueprint.build({ x: 13 });
48
+
49
+ const component = entity.getComponent(DummyComponent);
50
+
51
+ expect(component.a).toEqual(13);
52
+ });
@@ -125,6 +125,14 @@ class EntityBuilder {
125
125
  }
126
126
  }
127
127
 
128
+ /**
129
+ * Number of attached components
130
+ * @return {number}
131
+ */
132
+ get count() {
133
+ return this.element.length;
134
+ }
135
+
128
136
  /**
129
137
  * @template T
130
138
  * @param {T} componentInstance
@@ -8,6 +8,7 @@ interface Type<T> extends Function {
8
8
 
9
9
  export class EntityManager {
10
10
  dataset: EntityComponentDataset
11
+ fixedUpdateStepSize: number
11
12
 
12
13
  getComponentTypeMap(): any[]
13
14
 
@@ -329,6 +329,9 @@ EntityManager.prototype.simulate = function (timeDelta) {
329
329
  const fixed_step = this.fixedUpdateStepSize;
330
330
  const accumulatedTime = this.systemAccumulatedFixedStepTime;
331
331
 
332
+ assert.notNaN(fixed_step, 'fixed_step');
333
+ assert.greaterThan(fixed_step, 0, 'fixed_step must be greater than 0');
334
+
332
335
  for (let i = 0; i < system_count; i++) {
333
336
 
334
337
  const system = systems[i];
@@ -337,7 +340,7 @@ EntityManager.prototype.simulate = function (timeDelta) {
337
340
  let accumulated_time = accumulatedTime.get(system) + timeDelta;
338
341
 
339
342
 
340
- while (accumulated_time > fixed_step) {
343
+ while (accumulated_time >= fixed_step) {
341
344
 
342
345
  try {
343
346
  system.fixedUpdate(fixed_step)
@@ -458,9 +461,10 @@ function validateSystem(system) {
458
461
  /**
459
462
  * If the {@link EntityManager} is already started, the system will be started automatically before being added
460
463
  * @param {System} system
464
+ * @returns {Promise}
461
465
  * @throws {IllegalStateException}
462
466
  */
463
- EntityManager.prototype.addSystem = async function (system) {
467
+ EntityManager.prototype.addSystem = function (system) {
464
468
  assert.defined(system, "system");
465
469
  assert.isInstanceOf(system, System, 'system', 'System');
466
470
 
@@ -469,7 +473,7 @@ EntityManager.prototype.addSystem = async function (system) {
469
473
  if (existing !== null) {
470
474
  if (existing === system) {
471
475
  //system already added, do nothing
472
- return;
476
+ return Promise.resolve();
473
477
  } else {
474
478
  throw new IllegalStateException(`Another instance of system '${computeSystemName(system)}' is already registered`);
475
479
  }
@@ -508,7 +512,7 @@ EntityManager.prototype.addSystem = async function (system) {
508
512
  if (this.state === EntityManagerState.Running) {
509
513
  //initialize the system
510
514
  startup_promise = new Promise((resolve, reject) => {
511
- system.startup(this, resolve, reject);
515
+ this.startSystem(system, resolve, reject);
512
516
  });
513
517
  } else {
514
518
  startup_promise = Promise.resolve();
@@ -526,13 +530,13 @@ EntityManager.prototype.addSystem = async function (system) {
526
530
 
527
531
  this.on.systemAdded.send1(system);
528
532
 
529
- await startup_promise;
533
+ return startup_promise;
530
534
  };
531
535
 
532
536
  /**
533
537
  *
534
538
  * @param {System} system
535
- * @returns {boolean}
539
+ * @returns {Promise<boolean>}
536
540
  */
537
541
  EntityManager.prototype.removeSystem = async function (system) {
538
542
  assert.defined(system, "system");
@@ -573,11 +577,13 @@ EntityManager.prototype.removeSystem = async function (system) {
573
577
  this.systemAccumulatedFixedStepTime.delete(system);
574
578
 
575
579
  this.on.systemRemoved.send1(system);
580
+
581
+ return true;
576
582
  };
577
583
 
578
584
 
579
585
  /**
580
- *
586
+ * @private
581
587
  * @param {System} system
582
588
  * @param {function(system: System)} successCallback
583
589
  * @param {function(reason:*)} errorCallback
@@ -614,13 +620,15 @@ EntityManager.prototype.stopSystem = function (system, successCallback, errorCal
614
620
  };
615
621
 
616
622
  /**
617
- *
623
+ * @private
618
624
  * @param {System} system
619
625
  * @param {function(system: System)} successCallback
620
626
  * @param {function(reason:*)} errorCallback
621
627
  */
622
628
  EntityManager.prototype.startSystem = function (system, successCallback, errorCallback) {
623
629
  assert.defined(system, "system");
630
+ assert.isFunction(successCallback, "successCallback");
631
+ assert.isFunction(errorCallback, "errorCallback");
624
632
 
625
633
  if (system.state.getValue() === System.State.RUNNING) {
626
634
  //system is already running
@@ -1,6 +1,7 @@
1
1
  import { EntityManager } from "./EntityManager.js";
2
- import { System } from "./System.js";
2
+ import { System, SystemState } from "./System.js";
3
3
  import { jest } from '@jest/globals';
4
+ import { IllegalStateException } from "../../core/fsm/exceptions/IllegalStateException.js";
4
5
 
5
6
  /**
6
7
  *
@@ -97,3 +98,63 @@ test("call to 'simulate' propagate to registered system", () => {
97
98
  expect(update).toHaveBeenLastCalledWith(7);
98
99
  });
99
100
  });
101
+
102
+
103
+ test("call to 'simulate' ticks fixed update correctly", async () => {
104
+
105
+ const dummySystem = new DummySystem();
106
+ const fixedUpdate = jest.spyOn(dummySystem, 'fixedUpdate');
107
+
108
+ const em = await makeEm([dummySystem]);
109
+
110
+ em.fixedUpdateStepSize = 1.1;
111
+
112
+ em.simulate(3.3001);
113
+
114
+ expect(fixedUpdate).toHaveBeenCalledTimes(3);
115
+ expect(fixedUpdate).toHaveBeenLastCalledWith(1.1);
116
+ });
117
+
118
+ test("hasSystem", async () => {
119
+
120
+ const dummySystem = new DummySystem();
121
+ const em = await makeEm([]);
122
+
123
+ expect(em.hasSystem(DummySystem)).toBe(false);
124
+
125
+ await em.addSystem(dummySystem);
126
+
127
+ expect(em.hasSystem(DummySystem)).toBe(true);
128
+ });
129
+
130
+ test("addSystem", async () => {
131
+ const em = await makeEm([]);
132
+
133
+ const s1 = new DummySystem();
134
+
135
+ await em.addSystem(s1);
136
+
137
+ expect(s1.state.get()).toEqual(SystemState.RUNNING)
138
+
139
+ // system is already added, expect silent return
140
+ expect(() => em.addSystem(s1)).not.toThrow();
141
+
142
+ // 2 systems of the same class are not allowed
143
+ expect(() => em.addSystem(new DummySystem())).toThrow(IllegalStateException);
144
+ });
145
+
146
+ test("removeSystem", async () => {
147
+
148
+ const system = new DummySystem();
149
+
150
+ const em = await makeEm([system]);
151
+
152
+ expect(await em.removeSystem(system)).toBe(true);
153
+
154
+ expect(em.hasSystem(DummySystem)).toBe(false);
155
+
156
+ expect(system.state.get()).toEqual(SystemState.STOPPED);
157
+
158
+ // Try again, this time we expect nothing to happen
159
+ expect(await em.removeSystem(system)).toEqual(false);
160
+ });
@@ -155,13 +155,19 @@ Object.defineProperties(System.prototype, {
155
155
  * @readonly
156
156
  * @enum {number}
157
157
  */
158
- System.State = {
158
+ export const SystemState = {
159
159
  INITIAL: 0,
160
160
  STARTING: 1,
161
161
  RUNNING: 2,
162
162
  STOPPING: 3,
163
163
  STOPPED: 4
164
- };
164
+ }
165
+
166
+ /**
167
+ * @readonly
168
+ * @deprecated use {@link SystemState} directly instead
169
+ */
170
+ System.State = SystemState;
165
171
 
166
172
  /**
167
173
  *
@@ -4,7 +4,7 @@
4
4
 
5
5
 
6
6
  import EntityFactory from './EntityFactory.js';
7
- import { FunctionCompiler } from "../../core/function/FunctionCompiler.js";
7
+ import { FunctionCompiler } from "../../../../core/function/FunctionCompiler.js";
8
8
 
9
9
  function Blueprint() {
10
10
  this.element = [];
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Created by Alex on 25/08/2015.
3
3
  */
4
- import { FunctionCompiler } from "../../core/function/FunctionCompiler.js";
4
+ import { FunctionCompiler } from "../../../../core/function/FunctionCompiler.js";
5
5
 
6
6
 
7
7
  function ComponentSpec(options) {
@@ -3,10 +3,10 @@
3
3
  */
4
4
 
5
5
 
6
- import Blueprint from '../Blueprint.js';
7
- import { assert } from "../../../core/assert.js";
8
- import { countTask } from "../../../core/process/task/util/countTask.js";
9
- import { emptyTask } from "../../../core/process/task/util/emptyTask.js";
6
+ import Blueprint from './Blueprint.js';
7
+ import { assert } from "../../../../core/assert.js";
8
+ import { countTask } from "../../../../core/process/task/util/countTask.js";
9
+ import { emptyTask } from "../../../../core/process/task/util/emptyTask.js";
10
10
 
11
11
  /**
12
12
  *
@@ -0,0 +1,5 @@
1
+ This is a framework for json serialization/deserialization of `EntityComponentDataset`s
2
+
3
+ Meep has been using binary format for datasets since ~2017
4
+
5
+ This is largely obsolete.
@@ -14,7 +14,7 @@ import { frustum_from_camera } from "./frustum_from_camera.js";
14
14
  import { invertQuaternionOrientation } from "./InvertQuaternionOrientation.js";
15
15
  import { v3_distance_above_plane } from "../../../../core/geom/v3_distance_above_plane.js";
16
16
  import { ProjectionType } from "./ProjectionType.js";
17
- import { computePlaneRayIntersection } from "../../../../core/geom/3d/plane/computePlaneRayIntersection.js";
17
+ import { plane3_compute_ray_intersection } from "../../../../core/geom/3d/plane/plane3_compute_ray_intersection.js";
18
18
 
19
19
  /**
20
20
  * @class
@@ -250,7 +250,7 @@ export class Camera {
250
250
 
251
251
  assert.defined(plane, 'plane');
252
252
 
253
- computePlaneRayIntersection(
253
+ plane3_compute_ray_intersection(
254
254
  out,
255
255
  origin_x, origin_y, origin_z,
256
256
  direction_x, direction_y, direction_z,
@@ -0,0 +1,5 @@
1
+ import { ShadedGeometry } from "./ShadedGeometry.js";
2
+
3
+ test("constructor does not throw", () => {
4
+ expect(() => new ShadedGeometry()).not.toThrow()
5
+ });
@@ -86,7 +86,7 @@ export class EntityPath {
86
86
  const blueprint = def.blueprint;
87
87
 
88
88
  marker.offset = offset;
89
- marker.entity = blueprint.buildEntityBuilder({});
89
+ marker.entity = blueprint.build({});
90
90
  marker.definition = def;
91
91
 
92
92
  this.__position_marker(marker);