@woosh/meep-engine 2.49.3 → 2.49.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.
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "description": "Fully featured ECS game engine written in JavaScript",
6
6
  "type": "module",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.49.3",
8
+ "version": "2.49.6",
9
9
  "main": "build/meep.module.js",
10
10
  "module": "build/meep.module.js",
11
11
  "exports": {
@@ -0,0 +1,19 @@
1
+ export declare class LoadingCache<K, V> {
2
+ constructor(options: {
3
+ maxWeight?: number,
4
+ keyWeigher?: (key: K) => number,
5
+ valueWeigher?: (value: V) => number,
6
+ keyHashFunction?: (key: K) => number,
7
+ keyEqualityFunction?: (a: K, b: K) => boolean,
8
+ capacity?: number,
9
+ timeToLive?: number,
10
+ load: (key: K) => Promise<V>,
11
+ retryFailed?: boolean
12
+ })
13
+
14
+ invalidate(key: K): void
15
+
16
+ clear(): void
17
+
18
+ get(key: K): Promise<V>
19
+ }
@@ -0,0 +1,144 @@
1
+ //
2
+
3
+ import { Cache } from "./Cache.js";
4
+
5
+ /**
6
+ * @template R
7
+ */
8
+ class Record {
9
+ /**
10
+ * @template R
11
+ * @param {R} value
12
+ * @param {number} time
13
+ */
14
+ constructor(value, time) {
15
+ this.value = value;
16
+ this.time = time;
17
+ this.failed = false;
18
+ }
19
+ }
20
+
21
+ const DEFAULT_TIME_TO_LIVE = 10;
22
+
23
+ /**
24
+ * @template K,V
25
+ */
26
+ export class LoadingCache {
27
+ /**
28
+ * @type {Cache<K,Record<Promise<V>>>}
29
+ */
30
+ #internal
31
+ /**
32
+ * In seconds
33
+ * @type {number}
34
+ */
35
+ #timeToLive = DEFAULT_TIME_TO_LIVE;
36
+
37
+ /**
38
+ * @type {function(key:K):Promise<V>}
39
+ */
40
+ #load
41
+
42
+ /**
43
+ *
44
+ * @type {boolean}
45
+ */
46
+ #policyRetryFailed = true;
47
+
48
+ /**
49
+ * @see {@link Cache} for more details on what each parameter means
50
+ * @param maxWeight
51
+ * @param keyWeigher
52
+ * @param valueWeigher
53
+ * @param keyHashFunction
54
+ * @param keyEqualityFunction
55
+ * @param capacity
56
+ * @param {number} [timeToLive] in seconds
57
+ * @param load
58
+ * @param {boolean} [retryFailed]
59
+ */
60
+ constructor({
61
+ maxWeight,
62
+ keyWeigher,
63
+ valueWeigher,
64
+ keyHashFunction,
65
+ keyEqualityFunction,
66
+ capacity,
67
+ timeToLive = DEFAULT_TIME_TO_LIVE,
68
+ load,
69
+ retryFailed = true
70
+ }) {
71
+
72
+ this.#internal = new Cache({
73
+ maxWeight,
74
+ keyWeigher,
75
+ valueWeigher,
76
+ keyHashFunction,
77
+ keyEqualityFunction,
78
+ capacity,
79
+ });
80
+
81
+ this.#timeToLive = timeToLive;
82
+ this.#load = load;
83
+ this.#policyRetryFailed = retryFailed;
84
+ }
85
+
86
+ /**
87
+ *
88
+ * @param {K} key
89
+ */
90
+ invalidate(key) {
91
+ this.#internal.remove(key);
92
+ }
93
+
94
+ /**
95
+ *
96
+ */
97
+ clear() {
98
+ this.#internal.clear();
99
+ }
100
+
101
+ /**
102
+ *
103
+ * @param {K} key
104
+ * @return {Promise<V>}
105
+ */
106
+ async get(key) {
107
+ const currentTime = performance.now() * 1e-3;
108
+
109
+ /**
110
+ *
111
+ * @type {Record<Promise<V>>}
112
+ */
113
+ let record = this.#internal.get(key);
114
+
115
+ if (record === null
116
+ || (record.time + this.#timeToLive) < currentTime // timeout
117
+ || (record.failed && this.#policyRetryFailed) // load failed and we're configured to retry
118
+ ) {
119
+
120
+ // record needs to be loaded
121
+
122
+ let promise;
123
+
124
+ try {
125
+ promise = this.#load(key);
126
+ }catch (e){
127
+ promise = Promise.reject(e);
128
+ }
129
+
130
+ record = new Record(promise, currentTime);
131
+
132
+ this.#internal.put(key, record);
133
+
134
+ promise.catch(() => {
135
+ // mark as failure
136
+ record.failed = true;
137
+ });
138
+
139
+ }
140
+
141
+
142
+ return record.value;
143
+ }
144
+ }
@@ -0,0 +1,47 @@
1
+ import { LoadingCache } from "./LoadingCache.js";
2
+ import { delay } from "../process/delay.js";
3
+
4
+ test("successful load", async () => {
5
+ const cache = new LoadingCache({
6
+ async load(key) {
7
+ return 17;
8
+ }
9
+ });
10
+
11
+ expect(await cache.get(1)).toEqual(17);
12
+ });
13
+
14
+ test("record reuse", async () => {
15
+ const load = jest.fn(async () => 17);
16
+
17
+ const cache = new LoadingCache({
18
+ load
19
+ });
20
+
21
+ expect(await cache.get(1)).toEqual(17);
22
+ expect(await cache.get(1)).toEqual(17);
23
+
24
+ expect(load).toHaveBeenCalledTimes(1);
25
+ });
26
+
27
+ test("timeout reload reuse", async () => {
28
+
29
+ const values = [3, 5, 11];
30
+
31
+ const load = jest.fn(async () => values.pop());
32
+
33
+ const cache = new LoadingCache({
34
+ load,
35
+ timeToLive: 0.00001
36
+ });
37
+
38
+ expect(await cache.get(1)).toEqual(11);
39
+
40
+ await delay(1);
41
+
42
+ expect(await cache.get(1)).toEqual(5);
43
+
44
+ await delay(1);
45
+
46
+ expect(await cache.get(1)).toEqual(3);
47
+ });
@@ -1,55 +1,5 @@
1
- import { v3_length_sqr } from "../v3_length_sqr.js";
2
1
  import { vec3 } from "gl-matrix";
3
-
4
- /**
5
- *
6
- * @param {number[]} result
7
- * @param {number} result_offset
8
- * @param {number} vAx
9
- * @param {number} vAy
10
- * @param {number} vAz
11
- * @param {number} vBx
12
- * @param {number} vBy
13
- * @param {number} vBz
14
- * @param {number} vCx
15
- * @param {number} vCy
16
- * @param {number} vCz
17
- */
18
- export function v3_compute_triangle_normal(result, result_offset, vAx, vAy, vAz, vBx, vBy, vBz, vCx, vCy, vCz) {
19
- //compute CB and AB vectors
20
- const vCBx = vCx - vBx;
21
- const vCBy = vCy - vBy;
22
- const vCBz = vCz - vBz;
23
-
24
- const vABx = vAx - vBx;
25
- const vABy = vAy - vBy;
26
- const vABz = vAz - vBz;
27
-
28
- //compute triangle normal
29
- const crossX = vCBy * vABz - vCBz * vABy;
30
- const crossY = vCBz * vABx - vCBx * vABz;
31
- const crossZ = vCBx * vABy - vCBy * vABx;
32
-
33
- const l_sqr = v3_length_sqr(crossX, crossY, crossZ);
34
-
35
- if (l_sqr === 0) {
36
- // degenerate triangle, provide arbitrary invalid result
37
- result[result_offset] = 0;
38
- result[result_offset + 1] = 1;
39
- result[result_offset + 2] = 0;
40
- return;
41
- }
42
-
43
- const l_inv = 1 / Math.sqrt(l_sqr);
44
-
45
- const x = crossX * l_inv;
46
- const y = crossY * l_inv;
47
- const z = crossZ * l_inv;
48
-
49
- result[result_offset] = x;
50
- result[result_offset + 1] = y;
51
- result[result_offset + 2] = z;
52
- }
2
+ import { v3_compute_triangle_normal } from "./v3_compute_triangle_normal.js";
53
3
 
54
4
  /**
55
5
  *
@@ -613,9 +613,9 @@ function windowSH(output, output_offset, input, input_offset, numBands, channel_
613
613
 
614
614
  /**
615
615
  *
616
- * @param {number[]} result
616
+ * @param {number[]|Float32Array} result
617
617
  * @param {number} result_offset
618
- * @param {number[]} harmonics
618
+ * @param {number[]|Float32Array} harmonics
619
619
  * @param {number} harmonics_offset
620
620
  * @param {number} dimension_count
621
621
  */
@@ -0,0 +1,27 @@
1
+ import { sh3_dering_optimize_positive } from "./sh3_dering_optimize_positive.js";
2
+
3
+ test("de-ringing produces plausible numeric values", () => {
4
+
5
+ const result = [];
6
+
7
+ const harmonics = new Float32Array(9);
8
+
9
+ harmonics.fill(1);
10
+
11
+ sh3_dering_optimize_positive(
12
+ result, 0,
13
+ harmonics,
14
+ 0,
15
+ 1
16
+ );
17
+
18
+ for (let i = 0; i < 9; i++) {
19
+
20
+ const element = result[i];
21
+
22
+ expect(element).not.toBeNaN();
23
+ expect(Number.isFinite(element)).toBe(true);
24
+
25
+ }
26
+
27
+ });
@@ -0,0 +1,25 @@
1
+ import { computeTriangleRayIntersection } from "./computeTriangleRayIntersection.js";
2
+ import { SurfacePoint3 } from "../SurfacePoint3.js";
3
+
4
+ test("simple axis aligned intersection", () => {
5
+ const sp = new SurfacePoint3();
6
+
7
+ expect(computeTriangleRayIntersection(
8
+ sp,
9
+ 0, 0, -1,
10
+ 0, 0, 1,
11
+ -1, -1, 0,
12
+ 1, -1, 0,
13
+ 0, 1, 0
14
+ )).toBe(true);
15
+
16
+ expect(computeTriangleRayIntersection(
17
+ sp,
18
+ 0, 0, -1,
19
+ 0, 0, -1,
20
+ -1, -1, 0,
21
+ 1, -1, 0,
22
+ 0, 1, 0
23
+ )).toBe(false);
24
+
25
+ });
@@ -0,0 +1,51 @@
1
+ import { v3_length_sqr } from "../v3_length_sqr.js";
2
+
3
+ /**
4
+ *
5
+ * @param {number[]} result
6
+ * @param {number} result_offset
7
+ * @param {number} vAx
8
+ * @param {number} vAy
9
+ * @param {number} vAz
10
+ * @param {number} vBx
11
+ * @param {number} vBy
12
+ * @param {number} vBz
13
+ * @param {number} vCx
14
+ * @param {number} vCy
15
+ * @param {number} vCz
16
+ */
17
+ export function v3_compute_triangle_normal(result, result_offset, vAx, vAy, vAz, vBx, vBy, vBz, vCx, vCy, vCz) {
18
+ //compute CB and AB vectors
19
+ const vCBx = vCx - vBx;
20
+ const vCBy = vCy - vBy;
21
+ const vCBz = vCz - vBz;
22
+
23
+ const vABx = vAx - vBx;
24
+ const vABy = vAy - vBy;
25
+ const vABz = vAz - vBz;
26
+
27
+ //compute triangle normal
28
+ const crossX = vCBy * vABz - vCBz * vABy;
29
+ const crossY = vCBz * vABx - vCBx * vABz;
30
+ const crossZ = vCBx * vABy - vCBy * vABx;
31
+
32
+ const l_sqr = v3_length_sqr(crossX, crossY, crossZ);
33
+
34
+ if (l_sqr === 0) {
35
+ // degenerate triangle, provide arbitrary invalid result
36
+ result[result_offset] = 0;
37
+ result[result_offset + 1] = 1;
38
+ result[result_offset + 2] = 0;
39
+ return;
40
+ }
41
+
42
+ const l_inv = 1 / Math.sqrt(l_sqr);
43
+
44
+ const x = crossX * l_inv;
45
+ const y = crossY * l_inv;
46
+ const z = crossZ * l_inv;
47
+
48
+ result[result_offset] = x;
49
+ result[result_offset + 1] = y;
50
+ result[result_offset + 2] = z;
51
+ }
@@ -0,0 +1,27 @@
1
+ import { v3_compute_triangle_normal } from "./v3_compute_triangle_normal.js";
2
+
3
+ test("axis aligned triangles on +X plane", () => {
4
+ const result = [];
5
+
6
+ v3_compute_triangle_normal(
7
+ result, 0,
8
+ 0, -1, -1,
9
+ 0, 1, -1,
10
+ 0, 0, 1,
11
+ );
12
+
13
+ expect(result).toEqual([+1, 0, 0]);
14
+ });
15
+
16
+ test("axis aligned triangles on -X plane", () => {
17
+ const result = [];
18
+
19
+ v3_compute_triangle_normal(
20
+ result, 0,
21
+ 0, 0, 1,
22
+ 0, 1, -1,
23
+ 0, -1, -1,
24
+ );
25
+
26
+ expect(result).toEqual([-1, 0, 0]);
27
+ });
@@ -0,0 +1,11 @@
1
+ import { Miniball } from "./Miniball.js";
2
+ import { PointSet } from "./PointSet.js";
3
+
4
+ test("single point in 1d", () => {
5
+
6
+ const miniball = new Miniball(new PointSet(1, 1, [7]));
7
+
8
+ expect(miniball.center()).toEqual([7]);
9
+ expect(miniball.radius()).toEqual(0);
10
+
11
+ });
@@ -0,0 +1,23 @@
1
+ import { graph_k_means_cluster } from "./graph_k_means_cluster.js";
2
+ import { Graph } from "./v2/Graph.js";
3
+
4
+ test("empty graph", () => {
5
+ const graph = new Graph();
6
+
7
+ const clusters = graph_k_means_cluster(graph, 0, 0);
8
+
9
+ expect(Array.isArray(clusters)).toBe(true);
10
+ expect(clusters.length).toBe(0);
11
+ });
12
+
13
+ test("2-node graph with k=1", () => {
14
+ const graph = new Graph();
15
+ graph.addNode(3);
16
+ graph.addNode(7);
17
+
18
+ const clusters = graph_k_means_cluster(graph, 1, 0);
19
+
20
+ expect(Array.isArray(clusters)).toBe(true);
21
+ expect(clusters.length).toBe(1);
22
+ expect(clusters.map(cluster => cluster.sort())).toEqual([[0, 1]]);
23
+ });
@@ -1,6 +0,0 @@
1
- class GraphElement {
2
- properties = [];
3
- labels = [];
4
- }
5
-
6
- export default GraphElement;
@@ -1,12 +0,0 @@
1
- /**
2
- * Created by Alex on 29/01/14.
3
- */
4
-
5
-
6
- import GraphElement from "./GraphElement.js";
7
-
8
- class GraphNode extends GraphElement {
9
- edges = [];
10
- }
11
-
12
- export default GrapNode;