@woosh/meep-engine 2.43.11 → 2.43.12

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.
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Returns true if string is a data URL
3
+ * @param {string} url
4
+ * @return {boolean}
5
+ */
6
+ export function is_data_url(url) {
7
+ if (typeof url !== "string") {
8
+ return false;
9
+ }
10
+
11
+ return url.startsWith('data:');
12
+ }
@@ -63,6 +63,15 @@ export class AbstractShape3D {
63
63
  throw new Error('Not Implemented');
64
64
  }
65
65
 
66
+ /**
67
+ *
68
+ * @param {number[]|ArrayLike<number>|Float32Array|Float64Array} result format: x0,y0,z0,x1,y1,z1
69
+ * @returns {void}
70
+ */
71
+ compute_bounding_box(result) {
72
+ throw new Error('Not Implemented');
73
+ }
74
+
66
75
  /**
67
76
  * @template {AbstractShape3D} T
68
77
  * @param {T} other
@@ -3,6 +3,7 @@ import { AbstractShape3D } from "./AbstractShape3D.js";
3
3
  import { sign } from "../../../math/sign.js";
4
4
  import { isArrayEqualStrict } from "../../../collection/array/isArrayEqualStrict.js";
5
5
  import { computeHashFloatArray } from "../../../math/hash/computeHashFloatArray.js";
6
+ import { aabb3_matrix4_project } from "../aabb/aabb3_matrix4_project.js";
6
7
 
7
8
  /**
8
9
  *
@@ -108,6 +109,13 @@ export class TransformedShape3D extends AbstractShape3D {
108
109
  return this.__subject.volume * scale_modifier;
109
110
  }
110
111
 
112
+ compute_bounding_box(result) {
113
+ const tmp = [];
114
+ this.__subject.compute_bounding_box(tmp);
115
+
116
+ aabb3_matrix4_project(result, tmp, this.__matrix);
117
+ }
118
+
111
119
  signed_distance_gradient_at_point(result, point) {
112
120
  // transform point to subject's local space
113
121
  vec3.transformMat4(scratch_v3_0, point, this.__inverse_matrix);
@@ -5,6 +5,7 @@ import { compareNumbers } from "../../../primitives/numbers/compareNumbers.js";
5
5
  import { min2 } from "../../../math/min2.js";
6
6
  import { compute_signed_distance_gradient_by_sampling } from "./util/compute_signed_distance_gradient_by_sampling.js";
7
7
  import { isArrayEqual } from "../../../collection/array/isArrayEqual.js";
8
+ import { max2 } from "../../../math/max2.js";
8
9
 
9
10
  /**
10
11
  * To avoid severe performance overhead, we limit number of possible rejections
@@ -44,6 +45,40 @@ export class UnionShape3D extends AbstractShape3D {
44
45
  this.__child_volumes = [];
45
46
  }
46
47
 
48
+ compute_bounding_box(result) {
49
+ let x0 = Infinity, y0 = Infinity, z0 = Infinity,
50
+ x1 = -Infinity, y1 = -Infinity, z1 = -Infinity;
51
+
52
+ const children = this.children;
53
+ const n = children.length;
54
+
55
+ const tmp_aabb3 = new Float32Array(6);
56
+
57
+ for (let i = 0; i < n; i++) {
58
+ const child = children[i];
59
+
60
+ child.compute_bounding_box(tmp_aabb3);
61
+
62
+ x0 = min2(x0, tmp_aabb3[0]);
63
+ y0 = min2(y0, tmp_aabb3[1]);
64
+ z0 = min2(z0, tmp_aabb3[2]);
65
+
66
+ x1 = max2(x1, tmp_aabb3[3]);
67
+ y1 = max2(y1, tmp_aabb3[4]);
68
+ z1 = max2(z1, tmp_aabb3[5]);
69
+
70
+ }
71
+
72
+
73
+ result[0] = x0;
74
+ result[1] = y0;
75
+ result[2] = z0;
76
+
77
+ result[3] = x1;
78
+ result[4] = y1;
79
+ result[5] = z1;
80
+ }
81
+
47
82
  /**
48
83
  *
49
84
  * @private
@@ -13,6 +13,16 @@ export class UnitCubeShape3D extends AbstractShape3D {
13
13
  return 1;
14
14
  }
15
15
 
16
+ compute_bounding_box(result) {
17
+ result[0] = -0.5;
18
+ result[1] = -0.5;
19
+ result[2] = -0.5;
20
+
21
+ result[3] = 0.5;
22
+ result[4] = 0.5;
23
+ result[5] = 0.5;
24
+ }
25
+
16
26
  nearest_point_on_surface(result, reference) {
17
27
  const r_x = reference[0];
18
28
  const r_y = reference[1];
@@ -15,6 +15,16 @@ export class UnitSphereShape3D extends AbstractShape3D {
15
15
  return 3.14159265;
16
16
  }
17
17
 
18
+ compute_bounding_box(result) {
19
+ result[0] = -0.5;
20
+ result[1] = -0.5;
21
+ result[2] = -0.5;
22
+
23
+ result[3] = 0.5;
24
+ result[4] = 0.5;
25
+ result[5] = 0.5;
26
+ }
27
+
18
28
  nearest_point_on_surface(result, reference) {
19
29
 
20
30
  const r_x = reference[0];
@@ -1,44 +1,18 @@
1
1
  import { TypeEditor } from "../TypeEditor.js";
2
- import { StringEditor } from "./primitive/StringEditor.js";
3
2
  import EmptyView from "../../../../view/elements/EmptyView.js";
4
3
  import ImageView from "../../../../view/elements/image/ImageView.js";
5
- import ObservedString from "../../../../core/model/ObservedString.js";
6
- import { ObservedStringEditor } from "./ObservedStringEditor.js";
7
4
  import ButtonView from "../../../../view/elements/button/ButtonView.js";
8
5
  import { url_to_data_url } from "../../../../core/binary/url_to_data_url.js";
9
-
10
- /**
11
- *
12
- * @param {string} url
13
- * @return {boolean}
14
- */
15
- function is_data_url(url) {
16
- if (typeof url !== "string") {
17
- return false;
18
- }
19
-
20
- return url.startsWith('data:');
21
- }
6
+ import { is_data_url } from "../../../../core/binary/is_data_url.js";
7
+ import { LargeStrongEditor } from "./LargeStrongEditor.js";
22
8
 
23
9
  export class ImagePathEditor extends TypeEditor {
24
10
  inline = true;
25
11
 
26
12
  build(parent, field, registry) {
27
- let url_editor;
28
-
29
- if (field.type === String) {
30
-
31
- url_editor = new StringEditor();
32
-
33
- } else if (field.type === ObservedString) {
13
+ const url_editor = new LargeStrongEditor();
34
14
 
35
- url_editor = new ObservedStringEditor();
36
-
37
- } else {
38
- throw new Error(`Unsupported type`);
39
- }
40
-
41
- const vEditor = url_editor.build(parent, field, registry);
15
+ const editor = url_editor.build(parent, field, registry);
42
16
 
43
17
  const r = new EmptyView({
44
18
  css: {
@@ -59,14 +33,14 @@ export class ImagePathEditor extends TypeEditor {
59
33
  field.adapter.write(parent, field.name, url);
60
34
 
61
35
  // update editor value
62
- vEditor.el.value = url;
36
+ editor.methods.set_value(url);
63
37
 
64
- update();
38
+ input_changed();
65
39
  }
66
40
  });
67
41
 
68
42
  b_toDataURL.attr({
69
- title:'Convert URL into DATA_URL, embedding the data. This allows to bypass CORRS restrictions if the URL points to a different origin from the one where the texture resides'
43
+ title: 'Convert URL into DATA_URL, embedding the data. This allows to bypass CORRS restrictions if the URL points to a different origin from the one where the texture resides'
70
44
  });
71
45
 
72
46
  function update_data_url_status() {
@@ -88,15 +62,18 @@ export class ImagePathEditor extends TypeEditor {
88
62
  }
89
63
  }
90
64
 
91
- function update() {
92
- vPreview.__setSource(vEditor.el.value);
65
+ function input_changed() {
66
+ const url = field.adapter.read(parent, field.name);
67
+
68
+ vPreview.__setSource(url);
69
+
93
70
  update_data_url_status();
94
71
  }
95
72
 
96
73
  r.addChild(vPreview);
97
- r.addChild(vEditor);
74
+ r.addChild(editor.view);
98
75
 
99
- vEditor.el.addEventListener('input', update);
76
+ editor.signals.input_changed.add(input_changed);
100
77
 
101
78
  r.on.linked.add(update_data_url_status);
102
79
 
@@ -0,0 +1,107 @@
1
+ import { TypeEditor } from "../TypeEditor.js";
2
+ import { StringEditor } from "./primitive/StringEditor.js";
3
+ import ObservedString from "../../../../core/model/ObservedString.js";
4
+ import { ObservedStringEditor } from "./ObservedStringEditor.js";
5
+ import EmptyView from "../../../../view/elements/EmptyView.js";
6
+ import LabelView from "../../../../view/common/LabelView.js";
7
+ import { MouseEvents } from "../../../../engine/input/devices/events/MouseEvents.js";
8
+ import Signal from "../../../../core/events/signal/Signal.js";
9
+
10
+ /**
11
+ * If path is larger than this - it will be truncated and displayed as non-input
12
+ * @readonly
13
+ * @type {number}
14
+ */
15
+ const MAX_PATH_LENGTH_EDITABLE = 256;
16
+ /**
17
+ * How long to crop to
18
+ * @readonly
19
+ * @type {number}
20
+ */
21
+ const MAX_CROP_LENGTH = 32;
22
+
23
+ export class LargeStrongEditor extends TypeEditor {
24
+ inline = true;
25
+
26
+ build(parent, field, registry) {
27
+ let url_editor;
28
+
29
+ if (field.type === String) {
30
+
31
+ url_editor = new StringEditor();
32
+
33
+ } else if (field.type === ObservedString) {
34
+
35
+ url_editor = new ObservedStringEditor();
36
+
37
+ } else {
38
+ throw new Error(`Unsupported type`);
39
+ }
40
+
41
+ let in_focus = false;
42
+
43
+ const vEditor = url_editor.build(parent, field, registry);
44
+
45
+ const truncated_url = new ObservedString('');
46
+ const vTruncated = new LabelView(truncated_url);
47
+
48
+ vTruncated.el.addEventListener(MouseEvents.Click, () => {
49
+ in_focus = true;
50
+ update();
51
+ vEditor.el.focus();
52
+ });
53
+ vEditor.el.addEventListener('focusin', () => {
54
+ in_focus = true;
55
+ update();
56
+ });
57
+ vEditor.el.addEventListener('blur', () => {
58
+ in_focus = false;
59
+ update();
60
+ });
61
+
62
+ const r = new EmptyView();
63
+
64
+ function update() {
65
+ const url = field.adapter.read(parent, field.name);
66
+
67
+ if (typeof url === "string" && url.length > MAX_PATH_LENGTH_EDITABLE && !in_focus) {
68
+ r.removeChild(vEditor);
69
+
70
+ truncated_url.set('[...]'+url.slice(0, MAX_CROP_LENGTH));
71
+
72
+ if (!r.hasChild(vTruncated)) {
73
+ r.addChild(vTruncated);
74
+ }
75
+
76
+ } else if(!r.hasChild(vEditor)){
77
+ r.removeChild(vTruncated);
78
+ r.addChild(vEditor);
79
+ }
80
+ }
81
+
82
+ const input_changed = new Signal();
83
+
84
+ vEditor.el.addEventListener('input', () => {
85
+
86
+ const value = vEditor.el.value;
87
+
88
+ input_changed.send1(value);
89
+
90
+ update();
91
+ });
92
+ r.on.linked.add(update);
93
+
94
+ return {
95
+ view: r, signals: {
96
+ input_changed
97
+ },
98
+ methods: {
99
+ set_value(v) {
100
+ field.adapter.write(parent, field.name, v);
101
+ update();
102
+ vEditor.el.value = v;
103
+ }
104
+ }
105
+ };
106
+ }
107
+ }
@@ -11,6 +11,7 @@ import { ShadedGeometryFlags } from "../../../mesh-v2/ShadedGeometryFlags.js";
11
11
  import { m3_rm_compose_transform } from "../../../../../../view/m3_rm_compose_transform.js";
12
12
  import { fix_shape_normal_order } from "./fix_shape_normal_order.js";
13
13
  import { compute_smooth_profile_normals } from "./compute_smooth_profile_normals.js";
14
+ import { assert } from "../../../../../../core/assert.js";
14
15
 
15
16
  /**
16
17
  *
@@ -28,6 +29,9 @@ function make_geometry_segment(
28
29
  shape, shape_normal, shape_transform,
29
30
  segment_start, segment_end
30
31
  ) {
32
+ assert.notNaN(segment_start, 'segment_start');
33
+ assert.notNaN(segment_end, 'segment_end');
34
+ assert.greaterThanOrEqual(segment_end, segment_start, 'segment_start must be <= segment_end');
31
35
 
32
36
  const interpolation = path_component.interpolation;
33
37
 
@@ -161,8 +165,17 @@ export class TubePathBuilder {
161
165
  for (let i = 0; i < segment_count; i++) {
162
166
  const i2 = i * 2;
163
167
 
164
- const segment_start = style.path_mask[i2];
165
- const segment_end = style.path_mask[i2 + 1];
168
+ let segment_start = style.path_mask[i2];
169
+ let segment_end = style.path_mask[i2 + 1];
170
+
171
+ // check for wrong ordering
172
+ if (segment_end < segment_start) {
173
+ // violated interval constraint, re-order
174
+ const t = segment_end;
175
+
176
+ segment_end = segment_start;
177
+ segment_start = t;
178
+ }
166
179
 
167
180
  const geometry = make_geometry_segment(
168
181
  path_component, style,
@@ -1,13 +1,15 @@
1
1
  import EntityBuilder from "../../../../../../ecs/EntityBuilder";
2
2
  import {AbstractShape3D} from "../../../../../../../core/geom/3d/shape/AbstractShape3D";
3
3
  import {AttributeValue} from "./AttributeValue";
4
+ import {SamplingFunctionKind} from "./SamplingFunctionKind";
4
5
 
5
6
  interface Args {
6
7
  density?: number
7
8
  particle_size?: number
8
9
  shape?: AbstractShape3D
9
10
  sort?: boolean
10
- sprite?: string
11
+ sprite?: string,
12
+ sampling_function?: SamplingFunctionKind
11
13
  }
12
14
 
13
15
  interface Attributes {
@@ -12,6 +12,10 @@ import { PARTICULAR_PARTICLE_SPECIFICATION } from "../../emitter/PARTICULAR_PART
12
12
  import { SerializationMetadata } from "../../../../../../ecs/components/SerializationMetadata.js";
13
13
  import { UnitCubeShape3D } from "../../../../../../../core/geom/3d/shape/UnitCubeShape3D.js";
14
14
  import { shape_from_json } from "../../../../../../../core/geom/3d/shape/json/shape_from_json.js";
15
+ import { min2 } from "../../../../../../../core/math/min2.js";
16
+ import { max2 } from "../../../../../../../core/math/max2.js";
17
+ import { SamplingFunctionKind } from "./SamplingFunctionKind.js";
18
+ import { assert } from "../../../../../../../core/assert.js";
15
19
 
16
20
  const DEFAULT_ATTRIBUTE_VALUES = {
17
21
  color: [1, 1, 1, 1],
@@ -182,6 +186,13 @@ export class ParticleVolume {
182
186
  */
183
187
  this.__shape = new UnitCubeShape3D();
184
188
 
189
+ /**
190
+ *
191
+ * @type {SamplingFunctionKind|number}
192
+ * @private
193
+ */
194
+ this.__sampling_function = SamplingFunctionKind.Random;
195
+
185
196
  const eb = new EntityBuilder();
186
197
 
187
198
  eb.add(this.__emitter)
@@ -260,6 +271,7 @@ export class ParticleVolume {
260
271
  * @param {boolean} sort
261
272
  * @param {boolean} lighting
262
273
  * @param {string} [sprite]
274
+ * @param {SamplingFunctionKind|number} [sampling_function]
263
275
  */
264
276
  from({
265
277
  density = 1,
@@ -267,12 +279,17 @@ export class ParticleVolume {
267
279
  shape = new UnitCubeShape3D(),
268
280
  sort = false,
269
281
  lighting = true,
270
- sprite = "data/textures/particle/smokeparticle.png"
282
+ sprite = "data/textures/particle/smokeparticle.png",
283
+ sampling_function = SamplingFunctionKind.Random
271
284
  } = {}) {
272
285
 
286
+ assert.enum(sampling_function, SamplingFunctionKind, 'sampling_function');
287
+
273
288
  this.__density = density;
274
289
  this.__shape = shape;
275
290
 
291
+ this.__sampling_function = sampling_function;
292
+
276
293
  const emitter = this.__emitter;
277
294
 
278
295
  emitter.writeFlag(ParticleEmitterFlag.DepthSorting, sort);
@@ -295,6 +312,7 @@ export class ParticleVolume {
295
312
  * @param {boolean} [is_lit]
296
313
  * @param {string} sprite
297
314
  * @param {Object<Object>} [attributes]
315
+ * @param {string} [sampling_function]
298
316
  */
299
317
  fromJSON({
300
318
  density,
@@ -304,20 +322,28 @@ export class ParticleVolume {
304
322
  is_sorted = true,
305
323
  is_lit = true,
306
324
  sprite,
307
- attributes = {}
325
+ attributes = {},
326
+ sampling_function = 'Random'
308
327
  }) {
309
328
 
310
329
  const shape_object = shape_from_json(shape);
311
330
 
312
331
  const actual_density = is_density_size_adjusted ? density / particle_size : density;
313
332
 
333
+ const sf_type = SamplingFunctionKind[sampling_function];
334
+
335
+ if (sf_type === undefined) {
336
+ throw new Error(`Unsupported sampling function type '${sampling_function}', valid values:[${Object.keys(SamplingFunctionKind).join(', ')}]`);
337
+ }
338
+
314
339
  this.from({
315
340
  density: actual_density,
316
341
  shape: shape_object,
317
342
  particle_size,
318
343
  sort: is_sorted,
319
344
  lighting: is_lit,
320
- sprite
345
+ sprite,
346
+ sampling_function: sf_type
321
347
  });
322
348
 
323
349
  // parse attributes
@@ -353,12 +379,118 @@ export class ParticleVolume {
353
379
 
354
380
  }
355
381
 
382
+ __distribute_grid() {
383
+ const emitter = this.__emitter;
384
+ const particles = emitter.particles;
385
+
386
+ const layer = emitter.layers.get(0);
387
+
388
+ // build transform matrix
389
+ const m4_transform = new Matrix4();
390
+
391
+ const t = this.__transform;
392
+
393
+ composeMatrix4(m4_transform, t.position, t.rotation, t.scale);
394
+
395
+ const m4_layer = new Matrix4();
396
+
397
+ composeMatrix4(m4_layer, layer.position, new Quaternion(), layer.scale);
398
+
399
+ const v3_position = [];
400
+
401
+ const m4_final = new Matrix4();
402
+
403
+ m4_final.multiplyMatrices(m4_transform, m4_layer);
404
+
405
+
406
+ // cache variables to save a bit on de-referencing
407
+ const matrix_elements = m4_final.elements;
408
+ const shape = this.__shape;
409
+
410
+ const ts = shape;
411
+
412
+ const bounding_box = [];
413
+
414
+ ts.compute_bounding_box(bounding_box);
415
+
416
+ const density_step = 1 / Math.pow(this.__density, 1 / 3);
417
+
418
+ let particle_index = 0;
419
+
420
+ // apply margin to the space, this will shrink the grid spacing to align particles more evenly within the bounding volume
421
+ const density_margin = density_step * 0.5;
422
+
423
+ const mid_x = (bounding_box[3] + bounding_box[0]) * 0.5;
424
+ const mid_y = (bounding_box[4] + bounding_box[1]) * 0.5;
425
+ const mid_z = (bounding_box[5] + bounding_box[2]) * 0.5;
426
+
427
+ const x0 = min2(mid_x, bounding_box[0] + density_margin);
428
+ const x1 = max2(mid_x, bounding_box[3] - density_margin);
429
+
430
+ const y0 = min2(mid_y, bounding_box[1] + density_margin);
431
+ const y1 = max2(mid_y, bounding_box[4] - density_margin);
432
+
433
+ const z0 = min2(mid_z, bounding_box[2] + density_margin);
434
+ const z1 = max2(mid_z, bounding_box[5] - density_margin);
435
+
436
+ main_loop:for (let x = x0; x <= x1; x += density_step) {
437
+ for (let y = y0; y <= y1; y += density_step) {
438
+ for (let z = z0; z <= z1; z += density_step) {
439
+
440
+ v3_position[0] = x;
441
+ v3_position[1] = y;
442
+ v3_position[2] = z;
443
+
444
+ if (ts.contains_point(v3_position)) {
445
+
446
+ vec3.transformMat4(v3_position, v3_position, matrix_elements);
447
+
448
+ particles.writeAttributeVector3(particle_index, PARTICLE_ATTRIBUTE_POSITION, v3_position[0], v3_position[1], v3_position[2]);
449
+
450
+ particle_index++;
451
+
452
+ if (particle_index >= particles.capacity) {
453
+ break main_loop;
454
+ }
455
+ }
456
+ }
457
+ }
458
+ }
459
+
460
+ if (particle_index < particles.capacity) {
461
+ // crop out remaining particles in the pool
462
+ particles.occupancy.clearRange(particle_index, particles.capacity);
463
+ particles.update();
464
+ }
465
+
466
+ // request bounds update
467
+ emitter.setFlag(ParticleEmitterFlag.ParticleBoundsNeedUpdate);
468
+ emitter.computeBoundingBox();
469
+ }
470
+
471
+ /**
472
+ *
473
+ * @param {function} random
474
+ * @private
475
+ */
476
+ __distribute(random) {
477
+ const sf = this.__sampling_function;
478
+
479
+ if (sf === SamplingFunctionKind.Random) {
480
+ this.__distribute_random(random);
481
+ } else if (sf === SamplingFunctionKind.Grid) {
482
+ this.__distribute_grid();
483
+ } else {
484
+ throw new Error(`Unsupported sampling function '${sf}'`);
485
+ }
486
+ }
487
+
356
488
  /**
357
489
  * Distribute particles in the volume
358
490
  * @param {function():number} random
359
491
  * @private
360
492
  */
361
- __distribute(random) {
493
+ __distribute_random(random) {
362
494
  const emitter = this.__emitter;
363
495
  const particles = emitter.particles;
364
496
  const particle_count = particles.size();
@@ -0,0 +1,4 @@
1
+ export enum SamplingFunctionKind {
2
+ Random = 0,
3
+ Grid = 1
4
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @readonly
3
+ * @enum {number}
4
+ */
5
+ export const SamplingFunctionKind = {
6
+ Random: 0,
7
+ Grid: 1
8
+ };
@@ -79,6 +79,8 @@ import { MicronRenderPlugin } from "../../../../../micron/plugin/MicronRenderPlu
79
79
  import { obtainTerrain } from "../../../../../../ecs/terrain/util/obtainTerrain.js";
80
80
  import { pick } from "../../../../../../../ecs/grid/pick.js";
81
81
  import { clamp01 } from "../../../../../../../core/math/clamp01.js";
82
+ import { randomFromArray } from "../../../../../../../core/math/random/randomFromArray.js";
83
+ import { SamplingFunctionKind } from "./SamplingFunctionKind.js";
82
84
 
83
85
  const engineHarness = new EngineHarness();
84
86
 
@@ -895,7 +897,7 @@ async function sample_scene_moicon_0(engine) {
895
897
  * @param {Engine} engine
896
898
  */
897
899
  async function main(engine) {
898
- EngineHarness.buildBasics({
900
+ await EngineHarness.buildBasics({
899
901
  engine,
900
902
  enableWater: false,
901
903
  enableTerrain: false,
@@ -914,8 +916,8 @@ async function main(engine) {
914
916
  engine.graphics.renderer.setClearColor('rgba(99,99,99,1)');
915
917
 
916
918
  // await sample_scene_nam(engine);
917
- // await sample_scene_0(engine);
918
- await sample_scene_moicon_0(engine);
919
+ await sample_scene_0(engine);
920
+ // await sample_scene_moicon_0(engine);
919
921
  }
920
922
 
921
923
  /**
@@ -1068,7 +1070,8 @@ function create_volume({
1068
1070
  particle_size,
1069
1071
  shape: shape,
1070
1072
  sort,
1071
- sprite
1073
+ sprite,
1074
+ sampling_function: randomFromArray(Math.random, [SamplingFunctionKind.Random, SamplingFunctionKind.Grid])
1072
1075
  });
1073
1076
 
1074
1077
  v.build();
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "productName": "Meep",
6
6
  "description": "production-ready JavaScript game engine based on Entity Component System Architecture",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.43.11",
8
+ "version": "2.43.12",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",