@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 +1 -1
- package/src/core/cache/LoadingCache.d.ts +19 -0
- package/src/core/cache/LoadingCache.js +144 -0
- package/src/core/cache/LoadingCache.spec.js +47 -0
- package/src/core/geom/3d/compute_triangle_normal.js +1 -51
- package/src/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.js +2 -2
- package/src/core/geom/3d/sphere/harmonics/sh3_dering_optimize_positive.spec.js +27 -0
- package/src/core/geom/3d/triangle/computeTriangleRayIntersection.spec.js +25 -0
- package/src/core/geom/3d/v3_compute_triangle_normal.js +51 -0
- package/src/core/geom/3d/v3_compute_triangle_normal.spec.js +27 -0
- package/src/core/geom/packing/miniball/Miniball.spec.js +11 -0
- package/src/core/graph/graph_k_means_cluster.spec.js +23 -0
- package/src/core/graph/GraphElement.js +0 -6
- package/src/core/graph/Node.js +0 -12
package/package.json
CHANGED
|
@@ -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
|
+
});
|