@woosh/meep-engine 2.44.6 → 2.44.8

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.
@@ -395,7 +395,7 @@ export class Cache {
395
395
  }
396
396
 
397
397
  /**
398
- *
398
+ * Please note that the key will be stored inside the cache, so it must be treated as immutable
399
399
  * @param {Key} key
400
400
  * @param {function(Key):Value} compute
401
401
  * @param {*} [compute_context]
@@ -0,0 +1,28 @@
1
+ import { assert } from "../assert.js";
2
+
3
+ /**
4
+ *
5
+ */
6
+ export class LanguageMetadata {
7
+ /**
8
+ * Measured in characters per second
9
+ * @type {number}
10
+ */
11
+ reading_speed = 10
12
+
13
+ fromJSON({ reading_speed = 10 }) {
14
+
15
+ assert.isNumber(reading_speed, 'reading_speed');
16
+ assert.greaterThan(reading_speed, 0, 'reading_speed');
17
+
18
+ this.reading_speed = reading_speed;
19
+ }
20
+
21
+ static fromJSON(j) {
22
+ const r = new LanguageMetadata();
23
+
24
+ r.fromJSON(j);
25
+
26
+ return r;
27
+ }
28
+ }
@@ -0,0 +1,244 @@
1
+ import levenshtein from "fast-levenshtein";
2
+ import { parseTooltipString } from "../../view/tooltip/gml/parser/parseTooltipString.js";
3
+ import { assert } from "../assert.js";
4
+ import ObservedString from "../model/ObservedString.js";
5
+ import { seedVariablesIntoTemplateString } from "../parser/seedVariablesIntoTemplateString.js";
6
+ import { STRING_TEMPLATE_VARIABLE_REGEX } from "../parser/STRING_TEMPLATE_VARIABLE_REGEX.js";
7
+ import { Cache } from "../cache/Cache.js";
8
+ import { computeStringHash } from "../primitives/strings/computeStringHash.js";
9
+ import { computeHashArray } from "../collection/array/computeHashArray.js";
10
+ import { LanguageMetadata } from "./LanguageMetadata.js";
11
+
12
+ /**
13
+ * Validation utility method
14
+ * @param {string} template
15
+ * @return {string}
16
+ */
17
+ function validationMockSeed(template) {
18
+
19
+ const result = template.replace(STRING_TEMPLATE_VARIABLE_REGEX, function (match, varName) {
20
+ return "0";
21
+ });
22
+
23
+ return result;
24
+ }
25
+
26
+ const EMPTY_SEED = Object.freeze({});
27
+
28
+ const DEFAULT_LANGUAGE_METADATA = new LanguageMetadata();
29
+
30
+ export class Localization {
31
+ constructor() {
32
+ /**
33
+ *
34
+ * @type {AssetManager|null}
35
+ */
36
+ this.assetManager = null;
37
+
38
+ this.json = {};
39
+
40
+ /**
41
+ * @protected
42
+ * @type {LanguageMetadata}
43
+ */
44
+ this.language_metadata = DEFAULT_LANGUAGE_METADATA;
45
+
46
+ /**
47
+ *
48
+ * @type {ObservedString}
49
+ */
50
+ this.locale = new ObservedString('');
51
+
52
+ /**
53
+ * In debug mode error messages are a lot more verbose and helpful
54
+ * @type {boolean}
55
+ */
56
+ this.debug = true;
57
+
58
+ /**
59
+ * @type {Cache<string[], string>}
60
+ * @private
61
+ */
62
+ this.__failure_cache = new Cache({
63
+ maxWeight: 1024,
64
+ keyHashFunction: (key) => computeHashArray(key, computeStringHash)
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Measured in characters per second
70
+ * @deprecated use 'language_metadata' directly instead
71
+ * @return {number}
72
+ */
73
+ get reading_speed() {
74
+ return this.language_metadata.reading_speed;
75
+ }
76
+
77
+ /**
78
+ * Time required to read a piece of text, in seconds
79
+ * @param {string} text
80
+ * @returns {number} time in seconds
81
+ */
82
+ estimateReadingTime(text) {
83
+ assert.isString(text, 'text');
84
+
85
+ return text.length / this.reading_speed;
86
+ }
87
+
88
+ /**
89
+ *
90
+ * @param {AssetManager} am
91
+ */
92
+ setAssetManager(am) {
93
+ this.assetManager = am;
94
+ }
95
+
96
+ /**
97
+ * @returns {boolean}
98
+ * @param {function(key:string, error:*, original: string)} errorConsumer
99
+ */
100
+ validate(errorConsumer) {
101
+ let result = true;
102
+
103
+ for (let key in this.json) {
104
+ const value = this.json[key];
105
+
106
+ const seededValue = validationMockSeed(value);
107
+
108
+ try {
109
+ parseTooltipString(seededValue);
110
+ } catch (e) {
111
+ result = false;
112
+ errorConsumer(key, e, value);
113
+ }
114
+ }
115
+
116
+ return result;
117
+ }
118
+
119
+ /**
120
+ *
121
+ * @param {string} locale
122
+ * @param {string} path where to look for localization data
123
+ * @returns {Promise}
124
+ */
125
+ loadLocale(locale, path = 'data/database/text') {
126
+ assert.isString(locale, 'locale');
127
+ assert.isString(path, 'path');
128
+
129
+ const am = this.assetManager;
130
+
131
+ if (am === null) {
132
+ throw new Error('AssetManager is not set');
133
+ }
134
+
135
+ const pLoadData = am.promise(`${path}/${locale}.json`, "json")
136
+ .then(asset => {
137
+ const json = asset.create();
138
+
139
+ this.json = json;
140
+
141
+ this.locale.set(locale);
142
+ }, reason => {
143
+
144
+ console.error(`Failed to load locale data for locale '${locale}' : ${reason}`);
145
+
146
+ // reset data
147
+ this.json = {};
148
+
149
+ });
150
+
151
+ const pLoadMetadata = am.promise(`${path}/languages.json`, "json")
152
+ .then(asset => {
153
+ const languages_metadata = asset.create();
154
+
155
+ this.language_metadata = LanguageMetadata.fromJSON(languages_metadata[locale]);
156
+ }, reason => {
157
+ console.error(`Failed to load language metadata: ${reason}`);
158
+
159
+ this.language_metadata = DEFAULT_LANGUAGE_METADATA;
160
+ });
161
+
162
+ return Promise.all([pLoadData, pLoadMetadata]);
163
+ }
164
+
165
+ /**
166
+ *
167
+ * @param {String} id
168
+ * @return {boolean}
169
+ */
170
+ hasString(id) {
171
+ return this.json[id] !== undefined;
172
+ }
173
+
174
+ /**
175
+ *
176
+ * @param {number} value
177
+ */
178
+ formatIntegerByThousands(value) {
179
+ const formatter = new Intl.NumberFormat(this.locale.getValue(), { useGrouping: true });
180
+
181
+ return formatter.format(value);
182
+ }
183
+
184
+ /**
185
+ *
186
+ * @param {string} id
187
+ * @param {Object} seed
188
+ * @private
189
+ */
190
+ __debugMissingKey(id, seed) {
191
+ const locale = this.locale.getValue();
192
+
193
+ const seed_string = JSON.stringify(seed);
194
+
195
+ const message = this.__failure_cache.getOrCompute([locale, id, seed_string], () => {
196
+
197
+ //try to find similar keys
198
+ const similarities = Object.keys(this.json).map(function (key) {
199
+ const distance = levenshtein.get(key, id);
200
+ return {
201
+ key,
202
+ distance
203
+ };
204
+ });
205
+
206
+ similarities.sort(function (a, b) {
207
+ return a.distance - b.distance;
208
+ });
209
+
210
+ const suggestions = similarities.slice(0, 3).map(p => p.key);
211
+
212
+ return `No localization value for id='${id}', seed=${seed_string}, approximate matches: ${suggestions.join(', ')}`;
213
+ });
214
+
215
+ console.warn(message);
216
+ }
217
+
218
+ /**
219
+ *
220
+ * @param {string} id
221
+ * @param {object} [seed]
222
+ *
223
+ * @returns {string}
224
+ */
225
+ getString(id, seed = EMPTY_SEED) {
226
+ assert.isString(id, 'id');
227
+
228
+ const value = this.json[id];
229
+
230
+ if (value === undefined) {
231
+
232
+ if (this.debug) {
233
+ this.__debugMissingKey(id, seed)
234
+ }
235
+
236
+ //no value, provide substitute
237
+ return `@${id}`;
238
+
239
+ }
240
+
241
+ //value needs to be seeded
242
+ return seedVariablesIntoTemplateString(value, seed);
243
+ }
244
+ }
@@ -127,6 +127,10 @@ export class Connection {
127
127
  ^ (target_hash)
128
128
  ;
129
129
  }
130
+
131
+ toString() {
132
+ return `Connection{ id = ${this.id}, source=${this.source !== null ? this.source.id : 'null'}, target=${this.target !== null ? this.target.id : 'null'} }`
133
+ }
130
134
  }
131
135
 
132
136
 
@@ -16,7 +16,7 @@ export class DataType {
16
16
  }
17
17
 
18
18
  toString() {
19
- return `{DataType: id=${this.id}, name='${this.name}' }`;
19
+ return `DataType{ id=${this.id}, name='${this.name}' }`;
20
20
  }
21
21
 
22
22
  /**
@@ -5,6 +5,7 @@ import { Connection } from "./Connection.js";
5
5
  import { NodeInstance } from "./node/NodeInstance.js";
6
6
  import { array_push_if_unique } from "../../collection/array/array_push_if_unique.js";
7
7
  import { array_remove_first } from "../../collection/array/array_remove_first.js";
8
+ import { PortDirection } from "./node/PortDirection.js";
8
9
 
9
10
  export class NodeGraph {
10
11
  constructor() {
@@ -227,6 +228,7 @@ export class NodeGraph {
227
228
  }
228
229
 
229
230
  /**
231
+ * Utility method to help in creation of connections
230
232
  * Same as {@link #createConnection}, but ports are identified by their named instead
231
233
  * @param {number} sourceNode
232
234
  * @param {string} sourcePort
@@ -236,12 +238,26 @@ export class NodeGraph {
236
238
  */
237
239
  createConnectionByPortName(
238
240
  sourceNode, sourcePort,
239
- targetNode, targetPort) {
241
+ targetNode, targetPort
242
+ ) {
240
243
  const source_node_instance = this.getNodeSafe(sourceNode);
241
244
  const target_node_instance = this.getNodeSafe(targetNode);
242
245
 
243
- const source_port_object = source_node_instance.description.getPortByName(sourcePort);
244
- const target_port_object = target_node_instance.description.getPortByName(targetPort);
246
+ const source_ports = source_node_instance.description.getPortsByName(sourcePort).filter(p => p.direction === PortDirection.Out);
247
+
248
+ if (source_ports.length > 1) {
249
+ throw new Error(`Multiple source ports match name '${sourcePort}'`);
250
+ }
251
+
252
+ const source_port_object = source_ports[0];
253
+
254
+ const target_ports = target_node_instance.description.getPortsByName(targetPort).filter(p => p.direction === PortDirection.In);
255
+
256
+ if (target_ports.length > 1) {
257
+ throw new Error(`Multiple target ports match name '${targetPort}'`);
258
+ }
259
+
260
+ const target_port_object = target_ports[0];
245
261
 
246
262
  if (source_port_object === undefined) {
247
263
  throw new Error(`Source port '${sourcePort}' not found`);
@@ -177,20 +177,31 @@ export class NodeDescription {
177
177
 
178
178
  /**
179
179
  *
180
- * @param name
181
- * @return {Port|undefined}
180
+ * @param {string} name
181
+ * @return {Port[]}
182
182
  */
183
- getPortByName(name) {
183
+ getPortsByName(name) {
184
184
  assert.isString(name, 'name');
185
185
 
186
- for (const port of this.ports) {
186
+ /**
187
+ *
188
+ * @type {Port[]}
189
+ */
190
+ const result = [];
191
+
192
+ const ports = this.ports;
193
+ const port_count = ports.length;
194
+
195
+ for (let i = 0; i < port_count; i++) {
196
+ const port = ports[i];
197
+
187
198
  if (port.name === name) {
188
- return port;
199
+ result.push(port);
189
200
  }
201
+
190
202
  }
191
203
 
192
- //not found
193
- return undefined;
204
+ return result;
194
205
  }
195
206
 
196
207
  /**
@@ -205,6 +205,10 @@ export class NodeInstance {
205
205
  && isArrayEqual(this.parameters, other.parameters)
206
206
  ;
207
207
  }
208
+
209
+ toString() {
210
+ return `NodeInstance{ id = ${this.id}, description = ${this.description.id} }`
211
+ }
208
212
  }
209
213
 
210
214
 
@@ -57,6 +57,10 @@ export class Port {
57
57
  ;
58
58
 
59
59
  }
60
+
61
+ toString() {
62
+ return `Port{ id=${this.id}, direction=${this.direction}, name=${this.name}, type=${this.dataType} }`;
63
+ }
60
64
  }
61
65
 
62
66
  /**
@@ -1,5 +1,5 @@
1
1
  import { storiesOf } from "@storybook/html/dist/client/preview";
2
- import { Localization } from "../../../../../core/Localization.js";
2
+ import { Localization } from "../../../../../core/localization/Localization.js";
3
3
  import { ItemContainerController } from "./ItemContainerController.js";
4
4
  import ItemContainer from "../../../../../../model/game/ecs/component/item_container/ItemContainer.js";
5
5
  import Item from "../../../../../../model/game/ecs/component/Item.js";
package/engine/Engine.js CHANGED
@@ -23,7 +23,7 @@ import EmptyView from "../view/elements/EmptyView.js";
23
23
  import { assert } from "../core/assert.js";
24
24
  import { StaticKnowledgeDatabase } from "./knowledge/database/StaticKnowledgeDatabase.js";
25
25
  import Ticker from "./simulation/Ticker.js";
26
- import { Localization } from "../core/Localization.js";
26
+ import { Localization } from "../core/localization/Localization.js";
27
27
  import { ModuleRegistry } from "../core/model/ModuleRegistry.js";
28
28
  import { BinarySerializationRegistry } from "./ecs/storage/binary/BinarySerializationRegistry.js";
29
29
  import { EnginePluginManager } from "./plugin/EnginePluginManager.js";
@@ -326,6 +326,10 @@ export class AssetManager {
326
326
  throw new Error("Path must be string. Path = " + JSON.stringify(path));
327
327
  }
328
328
 
329
+ if(typeof type !== "string"){
330
+ throw new TypeError(`type must be a string, instead was '${typeof type}'`);
331
+ }
332
+
329
333
  const assetDescription = new AssetDescription(path, type);
330
334
 
331
335
  const asset = this.assets.get(assetDescription);
@@ -373,7 +373,7 @@ export class VoiceSystem extends AbstractContextSystem {
373
373
  // localized line may contain reference tags, the user will not see/read those, so we also compile line as pure text for estimating reading time
374
374
  const line_pure_text = gml.compileAsText(localized_line);
375
375
 
376
- const display_time_raw = localiation.computeReadingTime(line_pure_text);
376
+ const display_time_raw = localiation.estimateReadingTime(line_pure_text);
377
377
 
378
378
  const display_time = max2(TIMING_MINIMUM_READ_TIME, display_time_raw * line.displayDuration) + TIMING_NOTICE_DELAY;
379
379
 
@@ -15,7 +15,7 @@ import { GameAssetType } from "../../../../asset/GameAssetType.js";
15
15
  import { GLTFAssetLoader } from "../../../../asset/loaders/GLTFAssetLoader.js";
16
16
  import '../../../../../../../../css/game.scss';
17
17
  import { countTask } from "../../../../../core/process/task/util/countTask.js";
18
- import Vector2 from "../../../../../core/geom/Vector2.js";
18
+ import Vector2, { v2_distance } from "../../../../../core/geom/Vector2.js";
19
19
  import { RotationBehavior } from "../../../../intelligence/behavior/util/RotationBehavior.js";
20
20
  import { BehaviorComponent } from "../../../../intelligence/behavior/ecs/BehaviorComponent.js";
21
21
  import Vector3 from "../../../../../core/geom/Vector3.js";
@@ -270,14 +270,17 @@ function grid(ecd, offset_x, offset_y, x, y, size_x, size_y, spacing, textures)
270
270
  function makeNormalTestGridDecal(ecd, root_transform = new Transform()) {
271
271
  const uv = new OctahedralUvEncoder();
272
272
 
273
- const GRID_SIZE = 9;
273
+ const GRID_SIZE = 16;
274
274
 
275
275
  const CELL_SIZE = 1;
276
276
 
277
- const SPACING = CELL_SIZE*1.2;
277
+ const SPACING = CELL_SIZE * 1.2;
278
+ // const SPACING = CELL_SIZE * 0.6;
278
279
 
279
280
  const DEBUG_BOX = makeHelperBoxGeometry();
280
281
 
282
+ const random = seededRandom(7);
283
+
281
284
  for (let i = 0; i < GRID_SIZE; i++) {
282
285
  for (let j = 0; j < GRID_SIZE; j++) {
283
286
 
@@ -290,7 +293,7 @@ function makeNormalTestGridDecal(ecd, root_transform = new Transform()) {
290
293
  const decal = new Decal();
291
294
 
292
295
  // decal.uri = decal_urls[1];
293
- decal.uri ='moicon/ISO 7010 - Safety Signs (3)/ISO 7010 - Safety Signs/Emergency/400px/E001 – Emergency exit (left hand).png';
296
+ decal.uri = 'moicon/ISO 7010 - Safety Signs (3)/ISO 7010 - Safety Signs/Emergency/400px/E001 – Emergency exit (left hand).png';
294
297
 
295
298
  const entity = new EntityBuilder();
296
299
 
@@ -303,7 +306,7 @@ function makeNormalTestGridDecal(ecd, root_transform = new Transform()) {
303
306
  0,
304
307
  );
305
308
  transform.scale.setScalar(CELL_SIZE);
306
- transform.rotation._lookRotation(direction[0],direction[2], direction[1] , 0, 1, 0);
309
+ transform.rotation._lookRotation(direction[0], direction[2], direction[1], 0, 1, 0);
307
310
 
308
311
  transform.multiplyTransforms(root_transform, transform);
309
312
 
@@ -314,9 +317,12 @@ function makeNormalTestGridDecal(ecd, root_transform = new Transform()) {
314
317
  .add(decal)
315
318
  .build(ecd);
316
319
 
317
- setInterval(()=>{
318
- decal.priority = Math.random();
319
- },7000);
320
+ // setInterval(()=>{
321
+ // decal.priority = Math.random();
322
+ // },7000);
323
+
324
+ // decal.priority = randomIntegerBetween(random,0,4096);
325
+ decal.priority = v2_distance(i, j, GRID_SIZE * 0.5, GRID_SIZE * 0.5);
320
326
 
321
327
 
322
328
  // make an arrow helper
@@ -330,7 +336,7 @@ function makeNormalTestGridDecal(ecd, root_transform = new Transform()) {
330
336
  side: DoubleSide
331
337
  }), DrawMode.Triangles))
332
338
  .add(arrow_transform)
333
- .build(ecd);
339
+ .build(ecd);
334
340
  }
335
341
  }
336
342
  }
@@ -448,9 +454,9 @@ async function main(engine) {
448
454
  new EntityBuilder()
449
455
  .add(SGMesh.fromURL('data/models/snaps/cube_white.gltf'))
450
456
  .add(Transform.fromJSON({
451
- position:new Vector3(14,1,4.8),
457
+ position: new Vector3(14, 1, 4.8),
452
458
  rotation: t_forward_grid.rotation,
453
- scale: new Vector3(25,25,0.1)
459
+ scale: new Vector3(25, 25, 0.1)
454
460
  }))
455
461
  .build(ecd);
456
462